Compare commits

...

153 Commits

Author SHA1 Message Date
Martchus 6016ec4bff Set the desktop file name via `SET_QT_APPLICATION_INFO` 2024-04-08 12:37:39 +02:00
Martchus 4feedbb057 Bump patch version 2024-04-08 12:36:51 +02:00
Martchus ded20b34a6 Add helper to react to darkmode changes 2024-03-31 22:48:22 +02:00
Martchus d20130d770 Update translations 2024-03-31 13:45:35 +02:00
Martchus a7d2611400 Allow re-applying the default icon theme independently of palette
This is useful under Android where the palette is apparently not set to
something matching the darkmode setting but the Qt Quick Material style
nevertheless seems to follow the darkmode setting. So we just want to take
the darkmode setting into account for assigning the appropriate icon theme
and ignore the palette.
2024-03-31 13:42:38 +02:00
Martchus 264386acad Bump minor version 2024-03-31 13:28:49 +02:00
Martchus 8ee322abb7 Deprecate the CMake module `AndroidApk` 2024-03-17 00:42:19 +01:00
Martchus bbd42e167d Adapt default QQC2 style definitions for Qt 6
The casing is important, otherwise this results in the error
`module "material" is not installed`.
2024-03-16 23:17:30 +01:00
Martchus 63ffbc4a6f Avoid warning after `QOperatingSystemVersion::OSType` was moved
This is now `QOperatingSystemVersionBase::OSType` causing a warning about
an implicit conversion. Making an explicit cast to whatever type
`os.type()` actually returns (depends on the Qt version) this warning does
not occur anymore and the code should still compile with any Qt version.
2024-03-15 23:54:17 +01:00
Martchus 600470e609 Bump patch version 2024-03-15 23:50:29 +01:00
Martchus 7a07077daf Apply change of `global.h` template 2024-02-22 19:42:13 +01:00
Martchus 7e7f8a5740 Fix configuring macro for network information plugins
The actual name of these plugins differs from the name of the CMake target.
2024-02-16 17:40:16 +01:00
Martchus 6dd98369b8 Allow configuring network information plugins 2024-02-15 21:51:48 +01:00
Martchus 6e103e09ad Update copyright date 2024-02-15 21:47:03 +01:00
Martchus 5bb21d2ead Bump patch version 2024-02-15 21:46:54 +01:00
Martchus 010f5d8408 No longer enforce Fusion style on Windows 11 as of Qt 6.7
It looks like Qt 6.7 is going to provide a style for Windows 11 that will
fix the mentioned bug.
2023-12-15 15:56:09 +01:00
Martchus a30509a743 Add `ModernWindowsStyle` to list of style plugins
It looks like `WindowsVistaStyle`-plugin is going to be renamed
to `ModernWindowsStyle`-plugin. This plugin is going to provide the old
style `windowsvista` and the new style `windows11`.

I haven't seen any changelogs yet but that's what it looks like so far:
https://code.qt.io/cgit/qt/qtbase.git/tree/src/plugins/styles/modernwindows?h=6.7
2023-12-15 15:42:21 +01:00
Martchus 02a5617e36 Bump patch version 2023-12-15 15:36:07 +01:00
Martchus 5a7dac58ac Update translations 2023-11-18 22:08:45 +01:00
Martchus adf64a3d0d Apply clang-format 2023-11-18 22:05:36 +01:00
Martchus f52c7b8214 Fix error message when Qt/KF module is not found 2023-11-18 22:04:25 +01:00
Martchus 88310af665 Show a warning if the current Windows version is not supported 2023-11-18 22:03:27 +01:00
Martchus 265d3739c8 Bump patch version 2023-11-18 22:02:16 +01:00
Martchus 0eaa556a66 Update translations 2023-10-29 21:23:28 +01:00
Martchus 671617bf10 Apply cmake-format 2023-10-29 21:23:18 +01:00
Martchus c347bbfe1f Fix compilation against Qt < 6.1 2023-10-29 21:23:01 +01:00
Martchus c43ba340df Allow configuring OpenGL and Wayland support when linking against static Qt 2023-10-27 21:27:05 +02:00
Martchus cfa8347f19 Update translations 2023-10-15 16:57:52 +02:00
Martchus 458d98a279 Allow logging a few useful Qt-related information by setting env variable
Log Qt-related info when the environment variable
QT_UTILITIES_LOG_QT_CONFIG is set.
2023-10-14 20:44:59 +02:00
Martchus dcc6f71f1e Avoid unqualified `std` calls in `qtsettings.cpp` 2023-10-14 20:09:31 +02:00
Martchus a1f50456e8 Use `QLocale::territory()` instead of deprecated `QLocale::country()` 2023-10-14 20:03:37 +02:00
Martchus d546777f8c Make X11 optional 2023-10-11 18:11:07 +02:00
Martchus 91b9eed2d0 Ensure `use_package` is defined when configuring capslock detection 2023-09-19 12:36:44 +02:00
Martchus d47a5a93ed Update translations 2023-09-11 21:27:56 +02:00
Martchus 6bce4c40f2 Improve configuration and code of capslock detection
* Define `X_AVAILABLE` if that's the case so the X11-specific code can
  actually ever be effective
* Make the default for `CAPSLOCK_DETECTION` simply depending on whether it
  can be configured instead of making it platform dependent
* Simplify code and avoid warnings in X11-specific code
2023-09-11 21:27:41 +02:00
Martchus 472e32bfc2 Apply cmake-format 2023-09-11 21:20:43 +02:00
Martchus 56865f3a89 Add one hard-coded fallback search paths for icon themes
The `CMAKE_INSTALL_DATAROOTDIR` variable might be overridden by the project
to point to some custom location (e.g. `share/games` instead of `share`)
but this doesn't mean we will be able to find icons there.

Since icons are almost always under `/usr/share/icons` it makes sense to
add that location as one last hard-coded fallback to avoid having to
specify `BUILTIN_ICON_THEMES_SEARCH_PATH` in that common case.
2023-09-06 17:32:53 +02:00
Martchus 490345b2ca Bump patch version 2023-09-06 17:29:24 +02:00
Martchus b2f76021cc Update patch version 2023-09-05 17:51:57 +02:00
Martchus f5d99397ff Avoid CMake deprecation warning by bumping version 2023-07-23 21:18:51 +02:00
Martchus 856663f89d Update translations 2023-07-04 18:20:05 +02:00
Martchus 4c32a60551 Remove duplicated lookup in `loadApplicationTranslationFile()` 2023-07-03 22:00:42 +02:00
Martchus a6a64aa429 Make warning about missing Qt translations dependent on BUILTIN_TRANSLATIONS_OF_QT 2023-07-03 21:58:18 +02:00
Martchus 0e9637faa2 Fix dynamic retranslation of about dialog
`retranslateUi` must not be used here as it would revert the controls to
the initial state.
2023-07-03 00:49:37 +02:00
Martchus e448ff04fa Apply clang-format 2023-07-03 00:26:25 +02:00
Martchus 710a092458 Update translations 2023-07-03 00:25:57 +02:00
Martchus c7426403e5 Allow configuring notice on localization option page individually
Whether an application can retranslate the UI dynamically likely differs
from whether the other appearance related settings can be applied
dynamically so this needs to be configurable individually.

Additionally, the environment settings can never be applied dynamically so
the notice on that page should always be shown.
2023-07-03 00:21:29 +02:00
Martchus b340ff819c Retranslate certain dialogs/widgets dynamically
This does not cover all details yet.
2023-07-03 00:07:01 +02:00
Martchus 147b08ecf8 Retranslate tab texts in settings dialog dynamically 2023-07-02 23:54:55 +02:00
Martchus 0392b27b97 Add function for assigning a settings category name with retranslation
The function is still experimental as there's likely a better way to
implement this.
2023-07-02 23:54:14 +02:00
Martchus f69ffec06c Allow tracking whether the locale has changed via the `QtSettings` class
This allows an application to reload translations if the default locale has
changed to apply locale changes dynamically.
2023-07-02 23:48:46 +02:00
Martchus f30d020cda Update translations 2023-07-02 17:12:32 +02:00
Martchus dd99862769 Make the settings dialog react to language changes
This covers the settings dialog itself and option pages based on
`UiFileBasedOptionPage`. Other option pages need to react to the
new `OptionPageWidget::retranslationRequired()` signal or handle
the `QEvent::LanguageChange` event which is now propageted to the
page's widget.
2023-07-02 17:12:32 +02:00
Martchus bc3f84e65d Apply clang-format 2023-07-01 23:43:46 +02:00
Martchus 8fd1452174 Allow clearing previously installed translation files 2023-07-01 23:43:27 +02:00
Martchus 8d8585d00d Allow to instantiate built-in option pages individually
Expose conversion from QtSettings to QtSettingsData so one can pass such
an object to the constructor of e.g. QtLanguageOptionPage.
2023-06-26 21:22:42 +02:00
Martchus 3ac8c8e1a4 Update minor version 2023-06-26 21:22:42 +02:00
Martchus 96f10766d4 Enable web view support only by default if Qt WebEngine is present
The web view is only an optional enhancement in my applications so having
the support disabled is rather common. So for a better out-of-the-box
experience when configuring my projects it makes sense not having to
disable web view support explicitly if Qt WebEngine is not installed
anyways.
2023-06-10 18:08:13 +02:00
Martchus defecc45bc Fix showing font dialog 2023-06-09 12:28:26 +02:00
Martchus 0a952a2093 Bump patch version 2023-06-09 12:27:25 +02:00
Martchus fecf24a845 Update translations 2023-06-08 16:58:12 +02:00
Martchus cc05211664 Fix applying certain color roles when loading palette from file 2023-06-08 16:42:09 +02:00
Martchus c528926ce6 Remove useless function from palette editor 2023-06-08 16:21:50 +02:00
Martchus 0e7c10e622 Apply clang-format 2023-06-08 15:33:15 +02:00
Martchus 474af5eed9 Update translations 2023-06-08 14:04:55 +02:00
Martchus 3fffbc26e8 Fix selecting custom palette
For some reason the tool button's palette changes back to the old palette
when applying changes. So it cannot be used to hold the intermediately
selected paletted.
2023-06-08 13:28:35 +02:00
Martchus 958d7bc7c9 Bump patch version 2023-06-08 13:22:00 +02:00
Martchus 7dab696f42 Update translations 2023-05-21 20:12:41 +02:00
Martchus acecbd1158 Fix warnings about cv qualifiers droped from `static_cast` return type
The change for avoiding a warning from MSVC about 64-bit shift operations
introduced a different warning from GCC. Hopefully this now avoids warnings
in all compilers.
2023-05-21 19:00:36 +02:00
Martchus 03a3867688 Fix warning from MSVC about 64-bit shift operations 2023-05-10 22:07:39 +02:00
Martchus 6e4ea3652c Build CLI wrappers for Qt applications by default
Most of my Qt apps provide at least a basic CLI as well so providing the
CLI wrapper by default makes most sense.
2023-05-07 21:41:47 +02:00
Martchus a91c015e1c Bump patch version 2023-05-07 21:41:47 +02:00
Martchus 6974beab8a Update translations 2023-04-02 18:29:17 +02:00
Martchus 92c643f5ff Apply clang-format 2023-04-02 18:29:01 +02:00
Martchus 0ce5be5b2f Use Fusion by default on Windows 11
The native Windows style doesn't look very nice
on Windows 11 so Fusion looks better in comparison,
see https://bugreports.qt.io/browse/QTBUG-97668.
2023-03-28 23:42:13 +02:00
Martchus 63b33810e5 Allow disabling notices in Qt settings option pages 2023-03-26 21:24:43 +02:00
Martchus bd1e603fcf Improve behavior of `QtSettings::apply()` to be called multiple times
* Restore default settings
* Remove previously configured paths
2023-03-26 21:05:13 +02:00
Martchus 3e0754ebe5 Use fully qualified type in signal signature of `OptionCategory` 2023-03-26 19:51:50 +02:00
Martchus f27288fb1f Pass context object in lambda connections in `qtsettings.cpp` 2023-03-26 19:42:25 +02:00
Martchus 121d6a10db Fix return type of `dialogStyleForPalette()` for Windows implementation 2023-03-26 19:37:13 +02:00
Martchus 8642f14bb1 Apply clang-format and cmake-format 2023-03-25 18:50:08 +01:00
Martchus 41a1ede6fc Improve handling style changes in custom dialog style
* Avoid using global palette when computing stylesheet
* Re-compute stylesheet on palette changes
2023-03-25 18:49:43 +01:00
Martchus bbb65ae0dd Add function to re-evaluate the palette and default icon theme 2023-03-23 22:40:42 +01:00
Martchus 5430edc5fc Avoid running expensive icon-bundling code on every CMake re-configuration 2023-03-23 22:08:57 +01:00
Martchus 0e151b3cc6 Avoid relying on external tool for working around CMake's broken `REALPATH` 2023-03-23 21:48:59 +01:00
Martchus c2bd68824c Workaround broken `get_filename_component(... REALPATH)` on Windows
See https://gitlab.kitware.com/cmake/cmake/-/issues/17206
2023-03-16 00:50:50 +01:00
Martchus dd3ae7b32b Make search path for icon themes configurable 2023-03-16 00:48:11 +01:00
Martchus b8a428f29f Update translations 2023-03-15 21:29:30 +01:00
Martchus 9f4c7a8aaa Use `PLATFORM_WINDOWS` (and not `PLATFORM_MINGW`) in conversion functions
Whether MinGW (or MSVC) is used shouldn't make a difference here. The code
using these functions generally sticks to UTF-8 on Windows.
2023-03-15 21:29:14 +01:00
Martchus 647ad121a5 Improve setting default icon theme
* Set the default icon theme when applying Qt settings and the "system"
  theme is supposed to be used but none could be determined by Qt
    * Use a bundled icon theme depending on whether the current palette is
      light or dark
* Apply the default not only under Windows anymore; supposedly this makes
  sense under any platform where Qt cannot determine the icon theme for us
2023-03-15 21:27:04 +01:00
Martchus 54650eb2af Improve setting icon theme from CLI arguments
* Use `qEnvironmentVariable()` to read env variables into `QString`s
* Treat CLI arguments as UTF-8 (they will be converted to UTF-8 on Windows)
  which is consistent with the CLI argument handling in tag editor
* Add comment about processing of `m_iconThemeArg` and reserve the correct
  size when building the `QStringLiteral`
2023-03-15 20:07:10 +01:00
Martchus e5bd602d91 Ensure environment settings are effective before applying other settings
At least the documentation of `QIcon::setThemeName()` sounds like it could
make a difference:
```
The name should correspond to a directory name in the themeSearchPath() …
```
2023-03-15 19:31:09 +01:00
Martchus fd151995ce Adapt to dark mode support in Qt 6.5
* Avoid setting platform setting in Qt 6.5 as it is no longer needed for
  dark Window frames
* Reference recent blog post
* Add real `isDarkModeEnabled()` function using new Qt 6.5 API (as existing
  `isPaletteDark()` function is only based on the current palette)
2023-03-13 20:59:57 +01:00
Martchus 89bbd75950 Bump minor version 2023-03-13 20:23:28 +01:00
Martchus 368c9e9b0e Add function to return an error message for a `QSettings` object 2023-02-17 19:17:17 +01:00
Martchus 6e52b62706 Bump minor version 2023-02-17 19:16:30 +01:00
Martchus 2f46ecfbf4 Update translations 2023-02-11 16:21:35 +01:00
Martchus 1dded32095 Avoid assigning wrong window icon to settings tab with no icon
* Reset the settings page's widget's parent so `windowIcon()` won't return
  the parent's icon
* Improve the coding style a little bit
2023-02-11 16:21:19 +01:00
Martchus 41ddfc08b7 Update copyright notice 2023-01-17 18:34:58 +01:00
Martchus a051af49ae Avoid warning about unqualified std cast 2022-11-04 16:50:33 +01:00
Martchus 317bd33b08 Bump patch version 2022-11-04 16:49:34 +01:00
Martchus 1b5fc13475 Add fallback for `qEnvironmentVariable()` to support older Qt versions 2022-10-29 21:34:47 +02:00
Martchus b2e6e763fb Bump minor version 2022-10-29 21:31:15 +02:00
Martchus 777cbb6fc0 Apply clang-format on tests 2022-10-16 15:06:41 +02:00
Martchus 8df0a87970 Bump patch version 2022-10-16 15:06:30 +02:00
Martchus f4c5df7cac Update translations 2022-10-02 20:31:47 +02:00
Martchus ba16862d48 Allow selecting a settings page programmatically 2022-10-02 20:31:35 +02:00
Martchus f81672885c Make `apply()` and `reset()` slots of `SettingsDialog` public 2022-10-01 21:38:40 +02:00
Martchus 66b1a59e85 Bump minor version 2022-10-01 21:37:36 +02:00
Martchus ec03bd8eeb Update minimum required Qt version
Builds with Qt 5.6 are no longer conducted so only the latest
Qt 5 or 6 version is tested anymore.
2022-09-20 21:24:18 +02:00
Martchus abaea1309a Remove unused icons 2022-09-10 16:53:30 +02:00
Martchus 3a4ccfa75d Bump patch version 2022-09-10 16:42:37 +02:00
Martchus c9fcf52e40 Add comments for the tweaks in `setupCommonQtApplicationAttributes()` 2022-09-05 23:25:04 +02:00
Martchus b6701ef837 Enable dark window frame on Windows if configured color palette is dark 2022-09-05 23:22:13 +02:00
Martchus d59b89c067 Update translations 2022-08-27 15:07:08 +02:00
Martchus c91d73afc3 Allow adding additional widgets to the heading of the settings dialog 2022-08-27 15:02:16 +02:00
Martchus 49854354d6 Update translations 2022-08-23 18:43:40 +02:00
Martchus 58690920e4 Allow setting/getting corner widget in tab-widget of settings dialog 2022-08-23 18:43:28 +02:00
Martchus e3e2d24aeb Add `getSettings()` to streamline settings locations in my apps
* Keep handling for old location of settings file
* Add handling to allow portable settings (see
  https://github.com/Martchus/tageditor/issues/88)
2022-08-20 16:16:40 +02:00
Martchus d08ce3781d Bump minor version 2022-08-20 16:15:03 +02:00
Martchus 885323e367 Add OptionPageWidget with `paletteChanged()` signal 2022-07-16 18:44:47 +02:00
Martchus 0f7a4eb059 Add function to determine whether palette is dark 2022-07-16 18:44:47 +02:00
Martchus 6e5a9419be Bump minor version 2022-07-16 18:44:47 +02:00
Martchus b5af7fed34 Update translations 2022-06-09 21:08:38 +02:00
Martchus 29b2267711 Fix invalid use of freed object in settings dialog test 2022-06-09 21:01:58 +02:00
Martchus 9dd375abe9 Avoid creating 2nd instance of Qt-`OptionCategory` when invoking `showCategory()` 2022-06-09 20:24:12 +02:00
Martchus d4e35c28a3 Fix typo 2022-06-09 20:19:27 +02:00
Martchus 13d2049524 Fix invalid test; `SettingsDialog` takes ownership of `OptionCategory` 2022-06-09 20:10:11 +02:00
Martchus 38b748e04b Increment patch version 2022-06-09 20:08:42 +02:00
Martchus 752c8cdc71 Link builds using static Qt also against Wayland shell integration plugins
These are required; the Wayland integration plugin alone is not sufficient.
2022-05-02 20:15:07 +02:00
Martchus b709ab887f Default-initialize FONTCONFIG_PATH if not set
Fixes font loading in some environments
2022-05-02 00:55:07 +02:00
Martchus 6564932b64 Fix applying selected custom font 2022-05-01 21:57:59 +02:00
Martchus 8a4b865c28 Improve linking against static Qt plugins
* Detect a static Qt build and link against Qt plugins automatically in
  that case (without the necassity to set `STATIC_LINKAGE`)
* Add offscreen support on UNIX platforms as it is useful for testing on
  headless systems
* Add wayland support if available
* Populate `QT_TEST_LIBRARIES` in case we're not building an app because
  the plugins are still required when building tests
2022-04-28 21:42:33 +02:00
Martchus 605dca019c Add stalebot config 2022-04-12 01:03:31 +02:00
Martchus 6e57980c2b Add copyright notice 2022-04-05 20:10:18 +02:00
Martchus cbac69cc2d Clarify that license is "GPL-2-or-later" 2022-03-15 21:31:25 +01:00
Martchus 6640d16557 Add properties to PaletteEditor and ColorButton 2022-03-08 00:37:47 +01:00
Martchus de3379d9b7 Ensure the "Confirm" button is always the default one to confirming via enter works
Not sure why this ceased to work in some environments but this change fixes
it. It is still possible to abort via ESC.
2022-01-26 01:00:47 +01:00
Martchus 1e1fe4ae07 Update translations 2021-12-07 13:39:00 +01:00
Martchus b3896eb65e Check for relative paths also under install prefix
In the "normal" case none of the other variables are set
and thus relative paths (e.g. for the plugin directory)
cannot be resolved. This should still work when
CMAKE_FIND_ROOT_PATH is set beause we still check within
the other locations.
2021-11-04 23:22:26 +01:00
Martchus 1c0d6f166c Infer type of midRef's 3rd argument to be equivalent to the 2nd arg type 2021-11-04 00:15:24 +01:00
Martchus d771646af3 Set `Qt::AA_EnableHighDpiScaling` only before QGuiApplication is constructed
Otherwise it is too late anyways and Qt might print a warning.
2021-11-04 00:10:13 +01:00
Martchus a2482ef37c Avoid several warnings when building against Qt 6 2021-11-04 00:06:15 +01:00
Martchus 2fb056e0b5 Add file for zh_CN translation to project file 2021-10-07 18:27:43 +02:00
Martchus 660a5cec69 Bump patch version 2021-10-07 18:23:27 +02:00
Martchus 3c17897a98
Merge pull request #6 from coxde/zh_CN
Add zh_CN translation
2021-10-06 20:15:32 +02:00
Your Name 808e0dd399 Add zh_CN translation 2021-10-07 01:47:20 +08:00
Martchus 684e94e162 Apply cmake-format 2021-10-03 23:06:52 +02:00
Martchus da3d406ed0
Merge pull request #5 from hrittich/upstream-patches
Allow to find packages in namespaces
2021-10-02 22:22:45 +02:00
Hannah Rittich 73a5439381 Allow to find packages in namespaces 2021-10-02 21:51:00 +02:00
Martchus a1a1984075 Update translations 2021-10-02 19:46:36 +02:00
Martchus 9b559b153c Fix TLS support for static builds when using Qt 6.2.0 or newer
This Qt release "pluginized" the backends for TLS support so we need to
make sure to link against at least one TLS plugin since all of my
applications which use Qt Network expect TLS to work.
2021-10-02 19:39:51 +02:00
Martchus b51ac8c322 Improve comments for configuring Qt style plugins 2021-10-02 19:28:54 +02:00
60 changed files with 2132 additions and 1033 deletions

19
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,19 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- feature request
- enhancement
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
# meta data
set(META_PROJECT_NAME qtutilities)
@ -9,7 +9,7 @@ set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION
"Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models")
set(META_VERSION_MAJOR 6)
set(META_VERSION_MINOR 5)
set(META_VERSION_MINOR 14)
set(META_VERSION_PATCH 1)
set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH})
@ -90,7 +90,8 @@ if (ANDROID)
list(APPEND CMAKE_TEMPLATE_FILES cmake/templates/android-deployment.json.in)
endif ()
set(TS_FILES translations/${META_PROJECT_NAME}_de_DE.ts translations/${META_PROJECT_NAME}_en_US.ts)
set(TS_FILES translations/${META_PROJECT_NAME}_zh_CN.ts translations/${META_PROJECT_NAME}_de_DE.ts
translations/${META_PROJECT_NAME}_en_US.ts)
set(DOC_FILES README.md)
@ -115,28 +116,35 @@ set(SCRIPT_FILES scripts/required_icons.sh)
# required to include CMake modules from own project directory
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "${CMAKE_MODULE_PATH}")
# configure platform specific capslock detection for enterpassworddialog.cpp
if (WIN32
OR (UNIX
AND NOT APPLE
AND NOT ANDROID))
set(ENABLE_CAPSLOCK_DETECTION_BY_DEFAULT ON)
else ()
set(ENABLE_CAPSLOCK_DETECTION_BY_DEFAULT OFF)
# find c++utilities
set(CONFIGURATION_PACKAGE_SUFFIX
""
CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities")
set(PACKAGE_NAMESPACE
""
CACHE STRING "sets the namespace (prefix) for find_package() calls to packages configured via c++utilities")
if (PACKAGE_NAMESPACE)
set(PACKAGE_NAMESPACE_PREFIX "${PACKAGE_NAMESPACE}-")
endif ()
option(CAPSLOCK_DETECTION "enables capslock detection" ${ENABLE_CAPSLOCK_DETECTION_BY_DEFAULT})
if (CAPSLOCK_DETECTION)
if (WIN32)
# WinAPI provides functions to provide capslock detection
find_package(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.5.0 REQUIRED)
use_cpp_utilities()
# configure platform specific capslock detection for enterpassworddialog.cpp
if (WIN32)
set(HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION ON)
else ()
include(3rdParty)
use_package(TARGET_NAME X11::X11 PACKAGE_NAME X11 OPTIONAL)
if (TARGET X11::X11)
set_property(
SOURCE enterpassworddialog/enterpassworddialog.cpp
APPEND
PROPERTY COMPILE_DEFINITIONS X_AVAILABLE)
set(HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION ON)
else ()
# X11 can provide functions for capslock detection under non-Windows environments
find_package(X11)
if (X11_FOUND)
list(APPEND LIBRARIES ${X11_LIBRARIES})
set(HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION ON)
endif ()
endif ()
endif ()
option(CAPSLOCK_DETECTION "enables capslock detection" ${HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION})
if (CAPSLOCK_DETECTION)
if (NOT HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION)
message(FATAL_ERROR "No backend for capslock detection found (WinAPI or X11 must be provided)")
endif ()
@ -168,13 +176,6 @@ else ()
message(STATUS "D-Bus notifications disabled")
endif ()
# find c++utilities
set(CONFIGURATION_PACKAGE_SUFFIX
""
CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities")
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.5.0 REQUIRED)
use_cpp_utilities()
# include modules to apply configuration
include(BasicConfig)
include(QtGuiConfig)
@ -191,7 +192,7 @@ include(ConfigHeader)
# configure test target
include(TestUtilities)
set(QT_TEST_LIBRARIES ${CPP_UTILITIES_LIB} ${META_TARGET_NAME})
list(APPEND QT_TEST_LIBRARIES ${CPP_UTILITIES_LIB} ${META_TARGET_NAME})
use_qt_module(LIBRARIES_VARIABLE "QT_TEST_LIBRARIES" PREFIX "${QT_PACKAGE_PREFIX}" MODULE "Test")
foreach (TEST ${QT_TESTS})
configure_test_target(TEST_NAME "${TEST}_tests" SRC_FILES "tests/${TEST}.cpp" LIBRARIES "${QT_TEST_LIBRARIES}")

View File

@ -6,5 +6,12 @@ Qt utilities depends on c++utilities and is built in the same way. So checkout t
instructions. The documentation for various CMake variables influencing the build is also provided in the
c++utilities repository.
The library also depends on the following Qt modules (version 5.6 or higher): core, gui, widgets
The library also depends on the following Qt modules (only the latest Qt 5 and Qt 6 version tested):
core, gui, widgets
Some header files also require further Qt modules.
## Copyright notice and license
Copyright © 2015-2024 Marius Kittler
All code is licensed under [GPL-2-or-later](LICENSE).

View File

@ -44,7 +44,7 @@ AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const
{
m_ui->setupUi(this);
makeHeading(m_ui->productNameLabel);
setStyleSheet(dialogStyle());
setStyleSheet(dialogStyleForPalette(palette()));
setWindowFlags((windowFlags()) & ~(Qt::WindowMinMaxButtonsHint | Qt::WindowContextHelpButtonHint | Qt::WindowFullscreenButtonHint));
if (!applicationName.isEmpty()) {
m_ui->productNameLabel->setText(applicationName);
@ -58,7 +58,11 @@ AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const
m_ui->creatorLabel->setText(creator);
} else {
// add "developed by " before creator name
m_ui->creatorLabel->setText(tr("developed by %1").arg(creator.isEmpty() ? QApplication::organizationName() : creator));
auto setCreator = [this, creator = std::move(creator)] {
m_ui->creatorLabel->setText(tr("developed by %1").arg(creator.isEmpty() ? QApplication::organizationName() : creator));
};
setCreator();
connect(this, &AboutDialog::retranslationRequired, this, std::move(setCreator));
}
m_ui->versionLabel->setText(version.isEmpty() ? QApplication::applicationVersion() : version);
const auto &deps(dependencyVersions.size() ? dependencyVersions : CppUtilities::applicationInfo.dependencyVersions);
@ -72,10 +76,14 @@ AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const
% linkedAgainst.join(QStringLiteral("</li><li>")) % QStringLiteral("</li></ul>"));
}
if (!website.isEmpty() || CppUtilities::applicationInfo.url) {
m_ui->websiteLabel->setText(tr("For updates and bug reports visit the <a href=\"%1\" "
"style=\"text-decoration: underline; color: palette(link);\">project "
"website</a>.")
.arg(!website.isEmpty() ? website : QString::fromUtf8(CppUtilities::applicationInfo.url)));
auto setWebsite = [this, website = std::move(website)] {
m_ui->websiteLabel->setText(tr("For updates and bug reports visit the <a href=\"%1\" "
"style=\"text-decoration: underline; color: palette(link);\">project "
"website</a>.")
.arg(!website.isEmpty() ? website : QString::fromUtf8(CppUtilities::applicationInfo.url)));
};
setWebsite();
connect(this, &AboutDialog::retranslationRequired, this, std::move(setWebsite));
} else {
m_ui->websiteLabel->hide();
}
@ -88,7 +96,9 @@ AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const
: new QGraphicsPixmapItem(QPixmap::fromImage(image));
m_iconScene->addItem(item);
m_ui->graphicsView->setScene(m_iconScene);
m_ui->qtVersionLabel->setText(tr("Using <a href=\"qtversion\">Qt %1</a>").arg(QString::fromUtf8(qVersion())));
auto setQtVersion = [this] { m_ui->qtVersionLabel->setText(tr("Using <a href=\"qtversion\">Qt %1</a>").arg(QString::fromUtf8(qVersion()))); };
setQtVersion();
connect(this, &AboutDialog::retranslationRequired, this, std::move(setQtVersion));
connect(m_ui->qtVersionLabel, &QLabel::linkActivated, this, &AboutDialog::linkActivated);
centerWidget(this, parentWidget());
}
@ -118,6 +128,22 @@ AboutDialog::~AboutDialog()
{
}
bool AboutDialog::event(QEvent *event)
{
const auto res = QDialog::event(event);
switch (event->type()) {
case QEvent::PaletteChange:
setStyleSheet(dialogStyleForPalette(palette()));
break;
case QEvent::LanguageChange:
setWindowTitle(QCoreApplication::translate("QtUtilities::AboutDialog", "About", nullptr));
emit retranslationRequired();
break;
default:;
}
return res;
}
void AboutDialog::linkActivated(const QString &link)
{
if (link == QLatin1String("qtversion")) {

View File

@ -27,6 +27,12 @@ public:
explicit AboutDialog(QWidget *parent, const QString &website = QString(), const QString &description = QString(), const QImage &image = QImage());
~AboutDialog() override;
Q_SIGNALS:
void retranslationRequired();
protected:
bool event(QEvent *event) override;
private Q_SLOTS:
void linkActivated(const QString &link);

View File

@ -1,4 +1,7 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
message(DEPRECATION "Do not use the AndroidApk module anymore. It broke after some minor Qt 5 version. As of Qt 6 the "
"CMake functions that come with Qt itself are sufficient.")
# adds a target to create an Android APK with the help of androiddeployqt if target platform is Android

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
# applies Qt specific configuration notes: For GUI applications, QtGuiConfig must be included before. This module must always
# be included before AppTarget/LibraryTarget.
@ -66,10 +66,6 @@ if (IMPORTED_KF_MODULES)
endif ()
# find and use the required Qt/KF modules
set(QT_PACKAGE_PREFIX
"Qt5"
CACHE STRING "specifies the prefix for Qt packages")
string(TOLOWER "${QT_PACKAGE_PREFIX}" QT_PACKAGE_PREFIX_LOWER)
set(QT_LINGUIST_TOOLS_PACKAGE "${QT_PACKAGE_PREFIX}LinguistTools")
set(QT_QMAKE_TARGET "${QT_PACKAGE_PREFIX}::qmake")
foreach (MODULE ${QT_MODULES})
@ -79,9 +75,6 @@ foreach (MODULE ${QT_MODULES})
endif ()
use_qt_module(PREFIX "${QT_PACKAGE_PREFIX}" MODULE "${MODULE}" ${MODULE_OPTIONS})
endforeach ()
set(KF_PACKAGE_PREFIX
"KF5"
CACHE STRING "specifies the prefix for KDE Frameworks packages")
foreach (MODULE ${KF_MODULES})
unset(MODULE_OPTIONS)
if ("${MODULE}" IN_LIST META_PUBLIC_KF_MODULES)
@ -110,15 +103,43 @@ foreach (MODULE ${IMPORTED_KF_MODULES})
endif ()
endforeach ()
# enable TLS support by default when using Qt Network (relevant for linking against static plugin)
if (NOT DEFINED TLS_SUPPORT)
if (Network IN_LIST QT_MODULES OR Network IN_LIST IMPORTED_QT_MODULES)
set(TLS_SUPPORT ON)
else ()
set(TLS_SUPPORT OFF)
endif ()
endif ()
# allow configuring certain features when linking against static Qt
option(OPENGL_SUPPORT "whether to enable native Wayland support (when linking against static Qt)" ON)
option(NATIVE_WAYLAND_SUPPORT "whether to enable native Wayland support (when linking against static Qt)" ON)
option(NATIVE_WAYLAND_EGL_SUPPORT "specifies whether to enable native Wayland-EGL support (when linking against static Qt)"
OFF)
# built-in platform, imageformat and iconengine plugins when linking statically against Qt
if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
message(STATUS "Linking application ${META_PROJECT_NAME} against Qt plugins because static linkage is enabled.")
if (TARGET "${QT_PACKAGE_PREFIX}::Core")
get_target_property(QT_TARGET_TYPE "${QT_PACKAGE_PREFIX}::Core" TYPE)
endif ()
if (STATIC_LINKAGE OR QT_TARGET_TYPE STREQUAL STATIC_LIBRARY)
if (META_PROJECT_IS_APPLICATION)
set(QT_PLUGINS_LIBRARIES_VARIABLE PRIVATE_LIBRARIES)
else ()
set(QT_PLUGINS_LIBRARIES_VARIABLE QT_TEST_LIBRARIES)
endif ()
message(
STATUS
"Linking ${META_PROJECT_NAME} (${QT_PLUGINS_LIBRARIES_VARIABLE}) against Qt plugins because static linkage is enabled or a static Qt build is used."
)
if (Gui IN_LIST QT_MODULES
OR Widgets IN_LIST QT_MODULES
OR Quick IN_LIST QT_MODULES)
if (WIN32)
if (WIN32 AND TARGET "${QT_PACKAGE_PREFIX}::QWindowsIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -126,8 +147,10 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
PLUGINS
WindowsIntegration
ONLY_PLUGINS)
elseif (APPLE)
elseif (APPLE AND TARGET "${QT_PACKAGE_PREFIX}::QCocoaIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -135,8 +158,23 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
PLUGINS
CocoaIntegration
ONLY_PLUGINS)
elseif (TARGET "${QT_PACKAGE_PREFIX}::QXcbIntegrationPlugin")
endif ()
if (UNIX AND TARGET "${QT_PACKAGE_PREFIX}::QOffscreenIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Gui
PLUGINS
OffscreenIntegration
ONLY_PLUGINS)
endif ()
if (TARGET "${QT_PACKAGE_PREFIX}::QXcbIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -144,19 +182,146 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
PLUGINS
XcbIntegration
ONLY_PLUGINS)
else ()
message(WARNING "The required platform plugin for your platform is unknown an can not be linked in statically.")
endif ()
if (OPENGL_SUPPORT AND TARGET "${QT_PACKAGE_PREFIX}::QXcbEglIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Gui
PLUGINS
XcbEglIntegration
ONLY_PLUGINS)
endif ()
if (OPENGL_SUPPORT AND TARGET "${QT_PACKAGE_PREFIX}::QXcbGlxIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Gui
PLUGINS
XcbGlxIntegration
ONLY_PLUGINS)
endif ()
set(QT_CONFIG_USE_WAYLAND_INTEGRATION NO)
set(QT_CONFIG_WAYLAND_CLIENT_PLUGINS WaylandXdgShellIntegration WaylandWlShellIntegration WaylandIviShellIntegration
WaylandQtShellIntegration)
if (NATIVE_WAYLAND_SUPPORT AND TARGET "${QT_PACKAGE_PREFIX}::QWaylandIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Gui
PLUGINS
WaylandIntegration
ONLY_PLUGINS)
set(QT_CONFIG_USE_WAYLAND_INTEGRATION YES)
endif ()
if (NATIVE_WAYLAND_EGL_SUPPORT AND TARGET "${QT_PACKAGE_PREFIX}::QWaylandEglPlatformIntegrationPlugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Gui
PLUGINS
WaylandEglPlatformIntegration
ONLY_PLUGINS)
set(QT_CONFIG_USE_WAYLAND_INTEGRATION YES)
list(APPEND QT_CONFIG_WAYLAND_CLIENT_PLUGINS WaylandEglClientBuffer)
endif ()
if (QT_CONFIG_USE_WAYLAND_INTEGRATION)
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
WaylandClient
PLUGINS
${QT_CONFIG_WAYLAND_CLIENT_PLUGINS}
ONLY_PLUGINS
PLUGINS_OPTIONAL)
endif ()
endif ()
# ensure all available widget style plugins are built-in when creating a Qt Widgets application note: required since Qt
# 5.10 because the styles have been "pluginized" (see commit 4f3249f)
set(KNOWN_WIDGET_STYLE_PLUGINS WindowsVistaStyle MacStyle AndroidStyle)
# ensure a TLS plugin is built-in when available and when creating an app using Qt Network - required since Qt 6.2.0
# which "pluginized" TLS support
if (TLS_SUPPORT)
set(KNOWN_TLS_PLUGINS ${META_TLS_PLUGINS} SchannelBackend TlsBackendOpenSSL)
set(USED_TLS_PLUGINS)
foreach (TLS_PLUGIN ${KNOWN_TLS_PLUGINS})
if (TARGET "${QT_PACKAGE_PREFIX}::Q${TLS_PLUGIN}Plugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Network
PLUGINS
${TLS_PLUGIN}
ONLY_PLUGINS)
list(APPEND USED_TLS_PLUGINS "${TLS_PLUGIN}")
break() # one plugin is sufficient
endif ()
endforeach ()
# allow importing TLS plugins via qtconfig.h
if (USED_TLS_PLUGINS)
list_to_string(" " "\\\n Q_IMPORT_PLUGIN(Q" ")" "${USED_TLS_PLUGINS}" USED_TLS_PLUGINS_ARRAY)
endif ()
endif ()
# enable network information support for projects setting NETWORK_INFORMATION_SUPPORT explicitly
if (NETWORK_INFORMATION_SUPPORT)
set(KNOWN_NETWORK_INFORMATION_PLUGINS
${META_NETWORK_INFORMATION_PLUGINS} NetworkManagerNetworkInformation GlibNetworkInformation NLMNI
AndroidNetworkInformation SCNetworkReachabilityNetworkInformation)
set(USED_NETWORK_INFORMATION_PLUGINS)
foreach (PLUGIN ${KNOWN_NETWORK_INFORMATION_PLUGINS})
if (TARGET "${QT_PACKAGE_PREFIX}::Q${PLUGIN}Plugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
Network
PLUGINS
${PLUGIN}
ONLY_PLUGINS)
if (PLUGIN STREQUAL NLMNI)
set(PLUGIN NetworkListManagerNetworkInformation)
endif ()
list(APPEND USED_NETWORK_INFORMATION_PLUGINS "${PLUGIN}")
endif ()
endforeach ()
# allow importing network information plugins via qtconfig.h
if (USED_NETWORK_INFORMATION_PLUGINS)
list_to_string(" " "\\\n Q_IMPORT_PLUGIN(Q" "BackendFactory)" "${USED_NETWORK_INFORMATION_PLUGINS}"
USED_NETWORK_INFORMATION_PLUGINS_ARRAY)
endif ()
endif ()
# ensure all available widget style plugins are built-in when creating a Qt Widgets application - required since Qt 5.10
# because the styles have been "pluginized" (see commit 4f3249f)
set(KNOWN_WIDGET_STYLE_PLUGINS ModernWindowsStyle WindowsVistaStyle MacStyle AndroidStyle)
set(USED_WIDGET_STYLE_PLUGINS)
if (Widgets IN_LIST QT_MODULES)
foreach (WIDGET_STYLE_PLUGIN ${KNOWN_WIDGET_STYLE_PLUGINS})
if (TARGET "${QT_PACKAGE_PREFIX}::Q${WIDGET_STYLE_PLUGIN}Plugin")
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -168,7 +333,7 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
endif ()
endforeach ()
# allow importing image format plugins via qtconfig.h
# allow importing widget style plugins via qtconfig.h
if (USED_WIDGET_STYLE_PLUGINS)
list_to_string(" " "\\\n Q_IMPORT_PLUGIN(Q" "Plugin)" "${USED_WIDGET_STYLE_PLUGINS}"
WIDGET_STYLE_PLUGINS_ARRAY)
@ -184,6 +349,8 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
list(REMOVE_ITEM IMAGE_FORMAT_SUPPORT Svg)
else ()
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -200,10 +367,12 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
# ensure SVG plugins are built-in if configured
if ((SVG_SUPPORT OR SVG_ICON_SUPPORT) AND NOT Svg IN_LIST QT_MODULES)
use_qt_module(PREFIX "${QT_PACKAGE_PREFIX}" MODULE Svg)
use_qt_module(LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Svg)
endif ()
if (SVG_SUPPORT)
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -214,6 +383,8 @@ if (STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION)
endif ()
if (SVG_ICON_SUPPORT)
use_qt_module(
LIBRARIES_VARIABLE
"${QT_PLUGINS_LIBRARIES_VARIABLE}"
PREFIX
"${QT_PACKAGE_PREFIX}"
MODULE
@ -230,6 +401,7 @@ option(BUILTIN_TRANSLATIONS_OF_QT "enables/disables built-in translations of Qt
"${BUILTIN_TRANSLATIONS}")
# determine relevant Qt translation files
string(TOLOWER "${QT_PACKAGE_PREFIX}" QT_PACKAGE_PREFIX_LOWER)
set(QT_TRANSLATION_FILES)
set(QT_TRANSLATION_SEARCH_PATHS)
query_qmake_variable_path(QT_INSTALL_TRANSLATIONS)
@ -280,7 +452,7 @@ if (NOT QT_TRANSLATIONS_FOUND)
endif ()
# emit warning if no Qt translations found but built-in translations are enabled
if (BUILTIN_TRANSLATIONS AND NOT QT_TRANSLATION_FILES)
if (BUILTIN_TRANSLATIONS_OF_QT AND NOT QT_TRANSLATION_FILES)
message(
WARNING
"Unable to find translations of Qt itself so Qt's translation files will not be built-in. Be sure Qt translations (https://code.qt.io/cgit/qt/qttranslations.git) are installed. Was looking under: ${QT_TRANSLATION_SEARCH_PATHS}"
@ -403,117 +575,151 @@ if (REQUIRED_ICONS)
set(BUILTIN_ICON_THEMES
""
CACHE STRING "specifies icon themes to be built-in")
set(BUILTIN_ICON_THEMES_SEARCH_PATH
""
CACHE PATH "specifies the search path for icon themes to be built-in")
option(BUILTIN_ICON_THEMES_IN_LIBRARIES "specifies whether icon themes should also be built-in when building libraries"
OFF)
if (BUILTIN_ICON_THEMES AND (BUILTIN_ICON_THEMES_IN_LIBRARIES OR (NOT "${META_PROJECT_TYPE}" STREQUAL "library")))
set(ICON_THEME_FILES)
set(ICON_SEARCH_PATHS)
if (CMAKE_FIND_ROOT_PATH)
foreach (ROOT_PATH ${CMAKE_FIND_ROOT_PATH})
list(APPEND ICON_SEARCH_PATHS "${ROOT_PATH}/${CMAKE_INSTALL_DATAROOTDIR}/icons")
endforeach ()
endif ()
list(APPEND ICON_SEARCH_PATHS "${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons")
list(APPEND ICON_SEARCH_PATHS "/usr/${CMAKE_INSTALL_DATAROOTDIR}/icons") # find icons from regular prefix when cross-
# compiling
list(REMOVE_DUPLICATES ICON_SEARCH_PATHS)
set(BUILTIN_ICONS_DIR "${CMAKE_CURRENT_BINARY_DIR}/icons")
set(DEFAULT_THEME_INDEX_FILE "${BUILTIN_ICONS_DIR}/default/index.theme")
file(REMOVE_RECURSE "${BUILTIN_ICONS_DIR}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}")
foreach (ICON_THEME ${BUILTIN_ICON_THEMES})
string(REGEX MATCHALL "([^:]+|[^:]+$)" ICON_THEME_PARTS "${ICON_THEME}")
list(LENGTH ICON_THEME_PARTS ICON_THEME_PARTS_LENGTH)
if ("${ICON_THEME_PARTS_LENGTH}" STREQUAL 2)
list(GET ICON_THEME_PARTS 0 ICON_THEME)
list(GET ICON_THEME_PARTS 1 NEW_ICON_THEME_NAME)
else ()
set(NEW_ICON_THEME_NAME "${ICON_THEME}")
set(BUILTIN_ICON_THEMES_QRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/icons/${META_PROJECT_NAME}_builtinicons.qrc")
list(APPEND RES_FILES "${BUILTIN_ICON_THEMES_QRC_FILE}")
if (EXISTS "${BUILTIN_ICON_THEMES_QRC_FILE}")
message(
STATUS "Using existing \"${BUILTIN_ICON_THEMES_QRC_FILE}\" to bundle icon themes for ${META_PROJECT_NAME}. "
"Remove this file to force re-generation of the resource file.")
else ()
set(ICON_THEME_FILES)
set(ICON_SEARCH_PATHS ${BUILTIN_ICON_THEMES_SEARCH_PATH})
if (CMAKE_FIND_ROOT_PATH)
foreach (ROOT_PATH ${CMAKE_FIND_ROOT_PATH})
list(APPEND ICON_SEARCH_PATHS "${ROOT_PATH}/${CMAKE_INSTALL_DATAROOTDIR}/icons")
endforeach ()
endif ()
foreach (ICON_SEARCH_PATH ${ICON_SEARCH_PATHS})
set(ICON_THEME_PATH "${ICON_SEARCH_PATH}/${ICON_THEME}")
set(NEW_ICON_THEME_PATH "${ICON_SEARCH_PATH}/${ICON_THEME}")
if (IS_DIRECTORY "${ICON_THEME_PATH}")
message(
STATUS
"The specified icon theme \"${ICON_THEME}\" has been located under \"${ICON_THEME_PATH}\" and will be built-in."
)
# find index files
if (NOT ICON_THEME STREQUAL FALLBACK_ICON_THEME)
file(
GLOB GLOBBED_ICON_THEME_INDEX_FILES
LIST_DIRECTORIES false
"${ICON_THEME_PATH}/index.theme" "${ICON_THEME_PATH}/icon-theme.cache")
else ()
# only index.theme required when icons are provided as fallback anyways
file(
GLOB GLOBBED_ICON_THEME_INDEX_FILES
LIST_DIRECTORIES false
"${ICON_THEME_PATH}/index.theme")
endif ()
# make the first specified built-in the default theme
if (NOT EXISTS "${DEFAULT_THEME_INDEX_FILE}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/default")
file(WRITE "${DEFAULT_THEME_INDEX_FILE}" "[Icon Theme]\nInherits=${NEW_ICON_THEME_NAME}")
list(APPEND ICON_THEME_FILES "<file>default/index.theme</file>")
endif ()
# find required icons, except the icon theme is provided as fallback anyways
if (NOT ICON_THEME STREQUAL FALLBACK_ICON_THEME)
set(GLOB_PATTERNS)
foreach (REQUIRED_ICON ${REQUIRED_ICONS})
list(APPEND GLOB_PATTERNS "${ICON_THEME_PATH}/${REQUIRED_ICON}"
"${ICON_THEME_PATH}/${REQUIRED_ICON}.*" "${ICON_THEME_PATH}/*/${REQUIRED_ICON}"
"${ICON_THEME_PATH}/*/${REQUIRED_ICON}.*")
endforeach ()
file(
GLOB_RECURSE GLOBBED_ICON_THEME_FILES
LIST_DIRECTORIES false
${GLOB_PATTERNS})
else ()
list(APPEND ICON_SEARCH_PATHS "${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons")
list(APPEND ICON_SEARCH_PATHS "/usr/${CMAKE_INSTALL_DATAROOTDIR}/icons") # find icons from regular prefix when
# cross- compiling
list(APPEND ICON_SEARCH_PATHS "/usr/share/icons") # the project might change CMAKE__DATAROOTDIR to something
# custom so let's add one hardcoded fallback
list(REMOVE_DUPLICATES ICON_SEARCH_PATHS)
set(BUILTIN_ICONS_DIR "${CMAKE_CURRENT_BINARY_DIR}/icons")
set(DEFAULT_THEME_INDEX_FILE "${BUILTIN_ICONS_DIR}/default/index.theme")
set(DEFAULT_DARK_THEME_INDEX_FILE "${BUILTIN_ICONS_DIR}/default-dark/index.theme")
file(REMOVE_RECURSE "${BUILTIN_ICONS_DIR}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}")
foreach (ICON_THEME ${BUILTIN_ICON_THEMES})
string(REGEX MATCHALL "([^:]+|[^:]+$)" ICON_THEME_PARTS "${ICON_THEME}")
list(LENGTH ICON_THEME_PARTS ICON_THEME_PARTS_LENGTH)
if ("${ICON_THEME_PARTS_LENGTH}" STREQUAL 2)
list(GET ICON_THEME_PARTS 0 ICON_THEME)
list(GET ICON_THEME_PARTS 1 NEW_ICON_THEME_NAME)
else ()
set(NEW_ICON_THEME_NAME "${ICON_THEME}")
endif ()
foreach (ICON_SEARCH_PATH ${ICON_SEARCH_PATHS})
set(ICON_THEME_PATH "${ICON_SEARCH_PATH}/${ICON_THEME}")
set(NEW_ICON_THEME_PATH "${ICON_SEARCH_PATH}/${ICON_THEME}")
if (IS_DIRECTORY "${ICON_THEME_PATH}")
message(
STATUS
"Icon files for specified theme \"${ICON_THEME}\" are skipped because these are provided as fallback anyways."
"The specified icon theme \"${ICON_THEME}\" has been located under \"${ICON_THEME_PATH}\" and will be built-in."
)
set(GLOBBED_ICON_THEME_FILES)
endif ()
# make temporary copy of required icons and create resource list for Qt
foreach (ICON_THEME_FILE ${GLOBBED_ICON_THEME_INDEX_FILES} ${GLOBBED_ICON_THEME_FILES})
# resolve symlinks
if (IS_SYMLINK "${ICON_THEME_FILE}")
# find index files
if (NOT ICON_THEME STREQUAL FALLBACK_ICON_THEME)
file(
GLOB GLOBBED_ICON_THEME_INDEX_FILES
LIST_DIRECTORIES false
"${ICON_THEME_PATH}/index.theme" "${ICON_THEME_PATH}/icon-theme.cache")
else ()
# only index.theme required when icons are provided as fallback anyways
file(
GLOB GLOBBED_ICON_THEME_INDEX_FILES
LIST_DIRECTORIES false
"${ICON_THEME_PATH}/index.theme")
endif ()
# make the first non-dark specified built-in theme the "default" theme make the first dark specified
# built-in theme the "default-dark" theme
if (NEW_ICON_THEME_NAME MATCHES ".*-dark" AND NOT EXISTS "${DEFAULT_DARK_THEME_INDEX_FILE}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/default-dark")
file(WRITE "${DEFAULT_DARK_THEME_INDEX_FILE}" "[Icon Theme]\nInherits=${NEW_ICON_THEME_NAME}")
list(APPEND ICON_THEME_FILES "<file>default-dark/index.theme</file>")
elseif (NOT EXISTS "${DEFAULT_THEME_INDEX_FILE}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/default")
file(WRITE "${DEFAULT_THEME_INDEX_FILE}" "[Icon Theme]\nInherits=${NEW_ICON_THEME_NAME}")
list(APPEND ICON_THEME_FILES "<file>default/index.theme</file>")
endif ()
# find required icons, except the icon theme is provided as fallback anyways
if (NOT ICON_THEME STREQUAL FALLBACK_ICON_THEME)
set(GLOB_PATTERNS)
foreach (REQUIRED_ICON ${REQUIRED_ICONS})
list(APPEND GLOB_PATTERNS "${ICON_THEME_PATH}/${REQUIRED_ICON}"
"${ICON_THEME_PATH}/${REQUIRED_ICON}.*" "${ICON_THEME_PATH}/*/${REQUIRED_ICON}"
"${ICON_THEME_PATH}/*/${REQUIRED_ICON}.*")
endforeach ()
file(
GLOB_RECURSE GLOBBED_ICON_THEME_FILES
LIST_DIRECTORIES false
${GLOB_PATTERNS})
else ()
message(
STATUS
"Icon files for specified theme \"${ICON_THEME}\" are skipped because these are provided as fallback anyways."
)
set(GLOBBED_ICON_THEME_FILES)
endif ()
# make temporary copy of required icons and create resource list for Qt
foreach (ICON_THEME_FILE ${GLOBBED_ICON_THEME_INDEX_FILES} ${GLOBBED_ICON_THEME_FILES})
# resolve symlinks
if (IS_SYMLINK "${ICON_THEME_FILE}")
string(REGEX REPLACE "^${ICON_SEARCH_PATH}/" "" ICON_THEME_FILE_RELATIVE_PATH
"${ICON_THEME_FILE}")
string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_RELATIVE_PATH
"${ICON_THEME_FILE_RELATIVE_PATH}")
set(ICON_THEME_FILE_ALIAS " alias=\"${NEW_ICON_THEME_FILE_RELATIVE_PATH}\"")
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
if (CMAKE_VERSION VERSION_LESS "3.14")
message(FATAL_ERROR "Need at least CMake 3.14 on Windows to read symlinks of"
"icon theme to be bundled.")
endif ()
while (IS_SYMLINK "${ICON_THEME_FILE}")
get_filename_component(ICON_THEME_FILE_DIR "${ICON_THEME_FILE}" DIRECTORY)
file(READ_SYMLINK "${ICON_THEME_FILE}" ICON_THEME_FILE)
if (NOT IS_ABSOLUTE "${ICON_THEME_FILE}")
set(ICON_THEME_FILE "${ICON_THEME_FILE_DIR}/${ICON_THEME_FILE}")
endif ()
endwhile ()
endif ()
get_filename_component(ICON_THEME_FILE "${ICON_THEME_FILE}" REALPATH)
else ()
unset(ICON_THEME_FILE_ALIAS)
endif ()
string(REGEX REPLACE "^${ICON_SEARCH_PATH}/" "" ICON_THEME_FILE_RELATIVE_PATH
"${ICON_THEME_FILE}")
string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_RELATIVE_PATH
"${ICON_THEME_FILE_RELATIVE_PATH}")
set(ICON_THEME_FILE_ALIAS " alias=\"${NEW_ICON_THEME_FILE_RELATIVE_PATH}\"")
get_filename_component(ICON_THEME_FILE "${ICON_THEME_FILE}" REALPATH)
else ()
unset(ICON_THEME_FILE_ALIAS)
endif ()
string(REGEX REPLACE "^${ICON_SEARCH_PATH}/" "" ICON_THEME_FILE_RELATIVE_PATH "${ICON_THEME_FILE}")
string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_RELATIVE_PATH
"${ICON_THEME_FILE_RELATIVE_PATH}")
get_filename_component(ICON_THEME_FILE_DIR "${ICON_THEME_FILE_RELATIVE_PATH}" DIRECTORY)
string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_DIR
"${ICON_THEME_FILE_DIR}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/${NEW_ICON_THEME_FILE_DIR}")
file(COPY "${ICON_THEME_FILE}" DESTINATION "${BUILTIN_ICONS_DIR}/${NEW_ICON_THEME_FILE_DIR}")
list(APPEND ICON_THEME_FILES
"<file${ICON_THEME_FILE_ALIAS}>${NEW_ICON_THEME_FILE_RELATIVE_PATH}</file>")
endforeach ()
break()
get_filename_component(ICON_THEME_FILE_DIR "${ICON_THEME_FILE_RELATIVE_PATH}" DIRECTORY)
string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_DIR
"${ICON_THEME_FILE_DIR}")
file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/${NEW_ICON_THEME_FILE_DIR}")
file(COPY "${ICON_THEME_FILE}" DESTINATION "${BUILTIN_ICONS_DIR}/${NEW_ICON_THEME_FILE_DIR}")
list(APPEND ICON_THEME_FILES
"<file${ICON_THEME_FILE_ALIAS}>${NEW_ICON_THEME_FILE_RELATIVE_PATH}</file>")
endforeach ()
break()
endif ()
unset(ICON_THEME_PATH)
endforeach ()
if (NOT ICON_THEME_PATH)
message(FATAL_ERROR "The specified icon theme \"${ICON_THEME}\" could not be found.")
endif ()
unset(ICON_THEME_PATH)
endforeach ()
if (NOT ICON_THEME_PATH)
message(FATAL_ERROR "The specified icon theme \"${ICON_THEME}\" could not be found.")
endif ()
endforeach ()
set(BUILTIN_ICON_THEMES_QRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/icons/${META_PROJECT_NAME}_builtinicons.qrc")
list(REMOVE_DUPLICATES ICON_THEME_FILES)
string(CONCAT BUILTIN_ICON_THEMES_QRC_FILE_CONTENT "<RCC><qresource prefix=\"/icons\">" ${ICON_THEME_FILES}
"</qresource></RCC>")
file(WRITE "${BUILTIN_ICON_THEMES_QRC_FILE}" "${BUILTIN_ICON_THEMES_QRC_FILE_CONTENT}")
list(APPEND RES_FILES "${BUILTIN_ICON_THEMES_QRC_FILE}")
list(REMOVE_DUPLICATES ICON_THEME_FILES)
string(CONCAT BUILTIN_ICON_THEMES_QRC_FILE_CONTENT "<RCC><qresource prefix=\"/icons\">" ${ICON_THEME_FILES}
"</qresource></RCC>")
file(WRITE "${BUILTIN_ICON_THEMES_QRC_FILE}" "${BUILTIN_ICON_THEMES_QRC_FILE_CONTENT}")
endif ()
endif ()
endif ()

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
if (NOT BASIC_PROJECT_CONFIG_DONE)
message(FATAL_ERROR "Before including the QtGuiConfig module, the BasicConfig module must be included.")
@ -64,6 +64,8 @@ endif ()
if (WIN32)
# set "GUI-type" to WIN32 to hide console under Windows
set(GUI_TYPE WIN32)
# add option for building CLI-wrapper
option(BUILD_CLI_WRAPPER "whether to build a CLI wrapper" ON)
elseif (APPLE)
# make the GUI application a "bundle" under MacOSX
set(GUI_TYPE MACOSX_BUNDLE)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
# determines the JavaScript provider (either Qt Script or Qt Declarative)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
# defines helper to link against Qt dynamically or statically
@ -11,6 +11,14 @@ set(QT_LINKAGE_DETERMINED ON)
# include validate_visibility from c++utilities' 3rdParty module
include(3rdParty)
# allow switching the Qt and KDE Frameworks version
set(QT_PACKAGE_PREFIX
"Qt5"
CACHE STRING "specifies the prefix for Qt packages")
set(KF_PACKAGE_PREFIX
"KF5"
CACHE STRING "specifies the prefix for KDE Frameworks packages")
# determine the minimum Qt version
if (NOT META_QT_VERSION)
if (META_QT5_VERSION)
@ -31,7 +39,7 @@ endif ()
# define function for using Qt and KDE Frameworks modules and static plugins
macro (use_qt_module)
# parse arguments
set(OPTIONAL_ARGS ONLY_PLUGINS)
set(OPTIONAL_ARGS ONLY_PLUGINS PLUGINS_OPTIONAL)
set(ONE_VALUE_ARGS PREFIX MODULE VISIBILITY LIBRARIES_VARIABLE)
set(MULTI_VALUE_ARGS TARGETS PLUGINS)
cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN})
@ -67,7 +75,7 @@ macro (use_qt_module)
find_package("${ARGS_PREFIX}${ARGS_MODULE}" "${META_QT_VERSION}" REQUIRED)
foreach (TARGET ${ARGS_TARGETS})
if (NOT TARGET "${TARGET}")
message(FATAL_ERROR "The ${ARGS_PREFIX}${ARGS_MODULE} does not provide the target ${TARGET}.")
message(FATAL_ERROR "The CMake module ${ARGS_PREFIX}${ARGS_MODULE} does not provide the target ${TARGET}.")
endif ()
if ("${TARGET}" IN_LIST "${ARGS_LIBRARIES_VARIABLE}")
continue()
@ -103,6 +111,9 @@ macro (use_qt_module)
endif ()
endif ()
if (NOT TARGET "${PLUGIN_TARGET}")
if (NOT PLUGINS_OPTIONAL)
continue()
endif ()
message(
FATAL_ERROR
"The ${ARGS_PREFIX}${ARGS_MODULE} package does not provide the target ${ARGS_PREFIX}::Q${PLUGIN}Plugin.")
@ -192,7 +203,7 @@ function (query_qmake_variable_path QMAKE_VARIABLE)
return() # skip if CMake version too old like on Leap 15.1
endif ()
foreach (ROOT_PATH ${CMAKE_FIND_ROOT_PATH} "")
foreach (PREFIX_PATH ${CMAKE_PREFIX_PATH} "")
foreach (PREFIX_PATH ${CMAKE_INSTALL_PREFIX} ${CMAKE_PREFIX_PATH} "")
string(JOIN "/" FULL_PATH ${ROOT_PATH} ${PREFIX_PATH} ${VARIABLE_VALUE})
if (EXISTS "${FULL_PATH}")
set("${QMAKE_VARIABLE}"

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
# determines the web view provider (either Qt WebKit or Qt WebEngine)
@ -6,9 +6,19 @@ if (TARGET_CONFIG_DONE)
message(FATAL_ERROR "Can not include QtWebViewProviderConfig module when targets are already configured.")
endif ()
# include required modules
include(QtLinkage)
# check whether Qt WebEngine is present
find_package("${QT_PACKAGE_PREFIX}WebEngineWidgets" "${META_QT_VERSION}")
set(WEBVIEW_PROVIDER_DEFAULT "none")
if ("${${QT_PACKAGE_PREFIX}WebEngineWidgets_FOUND}")
set(WEBVIEW_PROVIDER_DEFAULT "webengine")
endif ()
# configure the specified web view provider
set(WEBVIEW_PROVIDER
"webengine"
"${WEBVIEW_PROVIDER_DEFAULT}"
CACHE STRING "specifies the web view provider: webengine (default), webkit or none")
if (WEBVIEW_PROVIDER STREQUAL "webkit")
set(WEBVIEW_PROVIDER WebKitWidgets)

View File

@ -12,5 +12,7 @@
#cmakedefine IMAGE_FORMAT_SUPPORT
#define IMPORT_IMAGE_FORMAT_PLUGINS @IMAGE_FORMAT_SUPPORT_ARRAY@
#define IMPORT_WIDGET_STYLE_PLUGINS @WIDGET_STYLE_PLUGINS_ARRAY@
#define IMPORT_TLS_PLUGINS @USED_TLS_PLUGINS_ARRAY@
#define IMPORT_NETWORK_INFORMATION_PLUGINS @USED_NETWORK_INFORMATION_PLUGINS_ARRAY@
@META_CUSTOM_QT_CONFIG@
#endif // @META_PROJECT_VARNAME_UPPER@_QT_CONFIG

View File

@ -12,8 +12,7 @@
#include <QMessageBox>
#include <QStyle>
#ifdef QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
#if defined(Q_OS_WIN32)
#if defined(QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) && defined(Q_OS_WIN32)
#include <windows.h>
#elif defined(X_AVAILABLE)
#include <X11/XKBlib.h>
@ -22,7 +21,6 @@
#undef FocusIn
#undef FocusOut
#endif
#endif
namespace QtUtilities {
@ -43,7 +41,7 @@ EnterPasswordDialog::EnterPasswordDialog(QWidget *parent)
// setup ui
m_ui->setupUi(this);
makeHeading(m_ui->instructionLabel);
setStyleSheet(dialogStyle());
setStyleSheet(dialogStyleForPalette(palette()));
setDescription();
setPromptForUserName(false);
setVerificationRequired(false);
@ -52,12 +50,7 @@ EnterPasswordDialog::EnterPasswordDialog(QWidget *parent)
m_ui->userNameLineEdit->installEventFilter(this);
m_ui->password1LineEdit->installEventFilter(this);
m_ui->password2LineEdit->installEventFilter(this);
// capslock key detection
#ifdef QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
m_capslockPressed = isCapslockPressed();
#else
m_capslockPressed = false;
#endif
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
// draw icon to capslock warning graphics view
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, this);
@ -212,6 +205,9 @@ void EnterPasswordDialog::setInstruction(const QString &value)
bool EnterPasswordDialog::event(QEvent *event)
{
switch (event->type()) {
case QEvent::PaletteChange:
setStyleSheet(dialogStyleForPalette(palette()));
break;
case QEvent::KeyPress: {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_CapsLock) {
@ -220,6 +216,9 @@ bool EnterPasswordDialog::event(QEvent *event)
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
break;
}
case QEvent::LanguageChange:
m_ui->retranslateUi(this);
break;
default:;
}
return QDialog::event(event);
@ -324,22 +323,17 @@ void EnterPasswordDialog::confirm()
*/
bool EnterPasswordDialog::isCapslockPressed()
{
#ifdef QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
// platform dependent method of determining if CAPS LOCK is pressed
#if defined(Q_OS_WIN32)
#if defined(QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) && defined(Q_OS_WIN32)
return GetKeyState(VK_CAPITAL) == 1;
#elif defined(X_AVAILABLE)
Display *d = XOpenDisplay((char *)0);
bool caps_state = false;
auto *const d = XOpenDisplay(nullptr);
auto capsState = false;
if (d) {
unsigned n;
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
caps_state = (n & 0x01) == 1;
capsState = (n & 0x01) == 1;
}
return caps_state;
#else
return false;
#endif
return capsState;
#else
return false;
#endif

View File

@ -258,6 +258,9 @@
<normaloff/>
</iconset>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>

View File

@ -4,6 +4,7 @@
#ifndef QT_UTILITIES_GLOBAL
#define QT_UTILITIES_GLOBAL
#include "qtutilities-definitions.h"
#include <c++utilities/application/global.h>
#ifdef QT_UTILITIES_STATIC

View File

@ -3,6 +3,8 @@
#include "../global.h"
#include <c++utilities/misc/traits.h>
#include <QtGlobal>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@ -50,7 +52,10 @@ inline StringView makeStringView(const QString &str)
/*!
* \brief Makes either a QStringView or a QStringRef depending on the Qt version, applying "mid()" parameters.
*/
inline StringView midRef(const QString &str, int pos, int n = -1)
template <typename PosType1, typename PosType2 = PosType1,
CppUtilities::Traits::EnableIf<std::is_integral<PosType1>, std::is_signed<PosType1>, std::is_integral<PosType2>, std::is_signed<PosType2>>
* = nullptr>
inline StringView midRef(const QString &str, PosType1 pos, PosType2 n = -1)
{
#ifdef QT_UTILITIES_USE_Q_STRING_VIEW
return QStringView(str).mid(pos, n);
@ -75,4 +80,17 @@ template <class... SplitArgs> inline auto splitRef(const QString &str, SplitArgs
} // namespace QtUtilities
#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
QT_BEGIN_NAMESPACE
/*!
* \brief Provides a fallback for qEnvironmentVariable() when using old Qt versions.
*/
inline QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
{
const auto val = qgetenv(varName);
return !val.isEmpty() ? QString::fromLocal8Bit(val) : defaultValue;
}
QT_END_NAMESPACE
#endif
#endif // QT_UTILITIES_COMPAT_H

View File

@ -12,7 +12,7 @@ namespace QtUtilities {
inline QByteArray toNativeFileName(const QString &fileName)
{
#if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
#if !defined(PLATFORM_WINDOWS) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
return fileName.toLocal8Bit();
#else
return fileName.toUtf8();
@ -21,7 +21,7 @@ inline QByteArray toNativeFileName(const QString &fileName)
inline QString fromNativeFileName(const char *nativeFileName, int size = -1)
{
#if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
#if !defined(PLATFORM_WINDOWS) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
return QString::fromLocal8Bit(nativeFileName, size);
#else
return QString::fromUtf8(nativeFileName, size);
@ -30,7 +30,7 @@ inline QString fromNativeFileName(const char *nativeFileName, int size = -1)
inline QString fromNativeFileName(const std::string &nativeFileName)
{
#if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
#if !defined(PLATFORM_WINDOWS) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
return QString::fromLocal8Bit(nativeFileName.data(), static_cast<int>(nativeFileName.size()));
#else
return QString::fromUtf8(nativeFileName.data(), static_cast<int>(nativeFileName.size()));

View File

@ -113,7 +113,7 @@ inline NotificationImage::NotificationImage(SwappedImage image)
inline NotificationImage::NotificationImage(const QImage &image)
: width(image.width())
, height(image.height())
, rowstride(image.bytesPerLine())
, rowstride(static_cast<qint32>(image.bytesPerLine()))
, hasAlpha(image.hasAlphaChannel())
, channels(image.isGrayscale() ? 1
: hasAlpha ? 4
@ -369,7 +369,7 @@ bool DBusNotification::queryCapabilities(const std::function<void(Capabilities &
if (returnValue.isError()) {
callback(Capabilities());
} else {
callback(Capabilities(move(returnValue.value())));
callback(Capabilities(std::move(returnValue.value())));
}
});
return true;

View File

@ -2,6 +2,10 @@
#include <QDesktopServices>
#include <QUrl>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
#include <QGuiApplication>
#include <QStyleHints>
#endif
#ifdef Q_OS_WIN32
#include <QFileInfo>
#endif
@ -38,4 +42,54 @@ bool openLocalFileOrDir(const QString &path)
#endif
return QDesktopServices::openUrl(url);
}
/*!
* \brief Returns whether \a palette is dark.
*/
bool isPaletteDark(const QPalette &palette)
{
return palette.color(QPalette::WindowText).lightness() > palette.color(QPalette::Window).lightness();
}
/*!
* \brief Returns whether dark mode is enabled.
* \remarks Whether dark mode is enabled can only be determined on Qt 6.5 or newer and only on certain
* platforms. If it cannot be determined, std::nullopt is returned.
*/
std::optional<bool> isDarkModeEnabled()
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
if (const auto *const styleHints = QGuiApplication::styleHints()) {
const auto colorScheme = styleHints->colorScheme();
if (colorScheme != Qt::ColorScheme::Unknown) {
return colorScheme == Qt::ColorScheme::Dark;
}
}
#endif
return std::nullopt;
}
/*!
* \brief Invokes the specified callback when the color scheme changed.
*
* The first argument of the callback will be whether the color scheme is dark now (Qt::ColorScheme::Dark).
* The callback is invoked immediately by this function unless \a invokeImmediately is set to false.
*
* \remarks Whether dark mode is enabled can only be determined on Qt 6.5 or newer and only on certain
* platforms. If it cannot be determined the \a darkModeChangedCallback is never invoked.
*/
QMetaObject::Connection onDarkModeChanged(std::function<void(bool)> &&darkModeChangedCallback, QObject *context, bool invokeImmediately)
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
if (const auto *const styleHints = QGuiApplication::styleHints()) {
if (invokeImmediately) {
darkModeChangedCallback(styleHints->colorScheme() == Qt::ColorScheme::Dark);
}
return QObject::connect(styleHints, &QStyleHints::colorSchemeChanged, context,
[handler = std::move(darkModeChangedCallback)](Qt::ColorScheme colorScheme) { return handler(colorScheme == Qt::ColorScheme::Dark); });
}
#endif
return QMetaObject::Connection();
}
} // namespace QtUtilities

View File

@ -3,13 +3,23 @@
#include "../global.h"
#include <QtGlobal>
#include <QMetaObject>
#include <QPalette>
#include <functional>
#include <optional>
QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_CLASS(QString)
namespace QtUtilities {
QT_UTILITIES_EXPORT bool openLocalFileOrDir(const QString &path);
}
QT_UTILITIES_EXPORT bool isPaletteDark(const QPalette &palette = QPalette());
QT_UTILITIES_EXPORT std::optional<bool> isDarkModeEnabled();
QT_UTILITIES_EXPORT QMetaObject::Connection onDarkModeChanged(
std::function<void(bool)> &&darkModeChangedCallback, QObject *context = nullptr, bool invokeImmediately = true);
} // namespace QtUtilities
#endif // DESKTOP_UTILS_DESKTOPSERVICES_H

View File

@ -1,5 +1,7 @@
#include "./dialogutils.h"
#include "../misc/desktoputils.h"
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
@ -56,13 +58,28 @@ QString generateWindowTitle(DocumentStatus documentStatus, const QString &docume
#if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
#ifdef Q_OS_WIN32
/*!
* \brief Returns the color used to draw frames.
*/
QColor windowFrameColorForPalette(const QPalette &palette)
{
return palette.window().color().darker(108);
}
/*!
* \brief Returns the color used to draw frames.
*/
QColor windowFrameColor()
{
return QGuiApplication::palette().window().color().darker(108);
return windowFrameColorForPalette(QGuiApplication::palette());
}
/*!
* \brief Returns the color used to draw instructions.
*/
QColor instructionTextColorForPalette(const QPalette &palette)
{
return isPaletteDark(palette) ? palette.text().color() : QColor(0x00, 0x33, 0x99);
}
/*!
@ -70,32 +87,40 @@ QColor windowFrameColor()
*/
QColor instructionTextColor()
{
const auto baseColor = QGuiApplication::palette().base().color();
return (baseColor.value() > 204 && baseColor.saturation() < 63) ? QColor(0x00, 0x33, 0x99) : QGuiApplication::palette().text().color();
return instructionTextColorForPalette(QGuiApplication::palette());
}
#endif
/*!
* \brief Returns the stylesheet for dialogs and other windows used in my
* applications.
*/
QString dialogStyleForPalette(const QPalette &palette)
{
#ifdef Q_OS_WINDOWS
return QStringLiteral("#mainWidget { color: palette(text); background-color: "
"palette(base); border: none; }"
"#bottomWidget { background-color: palette(window); "
"color: palette(window-text); border-top: 1px solid %1; }"
"QMessageBox QLabel, QInputDialog QLabel, "
"*[classNames~=\"heading\"] { font-size: 12pt; color: %2; "
"}"
"*[classNames~=\"input-invalid\"] { color: red; }")
.arg(windowFrameColorForPalette(palette).name(), instructionTextColorForPalette(palette).name());
#else
Q_UNUSED(palette)
return QStringLiteral("*[classNames~=\"heading\"] { font-weight: bold; }"
"*[classNames~=\"input-invalid\"] { color: red; }");
#endif
}
/*!
* \brief Returns the stylesheet for dialogs and other windows used in my
* applications.
*/
const QString &dialogStyle()
{
#ifdef Q_OS_WIN32
static const auto style = QStringLiteral("#mainWidget { color: palette(text); background-color: "
"palette(base); border: none; }"
"#bottomWidget { background-color: palette(window); "
"color: palette(window-text); border-top: 1px solid %1; }"
"QMessageBox QLabel, QInputDialog QLabel, "
"*[classNames~=\"heading\"] { font-size: 12pt; color: %2; "
"}"
"*[classNames~=\"input-invalid\"] { color: red; }")
.arg(windowFrameColor().name(), instructionTextColor().name());
#else
static const auto style = QStringLiteral("*[classNames~=\"heading\"] { font-weight: bold; }"
"*[classNames~=\"input-invalid\"] { color: red; }");
#endif
static const auto style = dialogStyleForPalette(QGuiApplication::palette());
return style;
}

View File

@ -8,6 +8,7 @@
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QWidget)
QT_FORWARD_DECLARE_CLASS(QColor)
QT_FORWARD_DECLARE_CLASS(QPalette)
QT_FORWARD_DECLARE_CLASS(QPoint)
QT_FORWARD_DECLARE_CLASS(QRect)
@ -29,11 +30,14 @@ enum class DocumentStatus {
QT_UTILITIES_EXPORT QString generateWindowTitle(DocumentStatus documentStatus, const QString &documentPath);
#if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
#ifdef Q_OS_WIN32
QT_UTILITIES_EXPORT QColor windowFrameColor();
QT_UTILITIES_EXPORT QColor instructionTextColor();
#ifdef Q_OS_WINDOWS
[[deprecated]] QT_UTILITIES_EXPORT QColor windowFrameColor();
QT_UTILITIES_EXPORT QColor windowFrameColorForPalette(const QPalette &palette);
[[deprecated]] QT_UTILITIES_EXPORT QColor instructionTextColor();
QT_UTILITIES_EXPORT QColor instructionTextColorForPalette(const QPalette &palette);
#endif
QT_UTILITIES_EXPORT const QString &dialogStyle();
[[deprecated]] QT_UTILITIES_EXPORT const QString &dialogStyle();
QT_UTILITIES_EXPORT QString dialogStyleForPalette(const QPalette &palette);
#ifdef QT_UTILITIES_GUI_QTWIDGETS
QT_UTILITIES_EXPORT QRect availableScreenGeometryAtPoint(const QPoint &point);
QT_UTILITIES_EXPORT void centerWidget(QWidget *widget, const QWidget *parent = nullptr, const QPoint *position = nullptr);

View File

@ -94,7 +94,7 @@ void RecentMenuManager::addEntry(const QString &path)
}
if (!entry) {
// remove old entries to have never more than 10 entries
for (int i = existingEntries.size() - 1; i > 8; --i) {
for (auto i = existingEntries.size() - 1; i > 8; --i) {
delete existingEntries[i];
}
existingEntries = m_menu->actions();

View File

@ -32,7 +32,7 @@ ChecklistModel::ChecklistModel(QObject *parent)
int ChecklistModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return m_items.size();
return static_cast<int>(m_items.size());
}
return 0;
}
@ -261,7 +261,7 @@ void ChecklistModel::restore(QSettings &settings, const QString &name)
*/
void ChecklistModel::save(QSettings &settings, const QString &name) const
{
settings.beginWriteArray(name, m_items.size());
settings.beginWriteArray(name, static_cast<int>(m_items.size()));
int index = 0;
for (const ChecklistItem &item : m_items) {
settings.setArrayIndex(index);
@ -295,7 +295,7 @@ void ChecklistModel::applyVariantList(const QVariantList &checkedIds)
for (auto &item : m_items) {
item.m_checkState = checkedIds.contains(item.id()) ? Qt::Checked : Qt::Unchecked;
}
emit dataChanged(index(0), index(m_items.size()), { Qt::CheckStateRole });
emit dataChanged(index(0), index(static_cast<int>(m_items.size())), { Qt::CheckStateRole });
}
} // namespace QtUtilities

View File

@ -14,6 +14,7 @@ namespace QtUtilities {
*/
class QT_UTILITIES_EXPORT ColorButton : public QToolButton {
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered)
public:
ColorButton(QWidget *parent = nullptr);

View File

@ -14,6 +14,8 @@
#include <QStyle>
#include <QToolButton>
#include <type_traits>
namespace QtUtilities {
enum { BrushRole = 33 };
@ -29,7 +31,6 @@ PaletteEditor::PaletteEditor(QWidget *parent)
{
m_ui->setupUi(this);
m_ui->paletteView->setModel(m_paletteModel);
updatePreviewPalette();
updateStyledButton();
m_ui->paletteView->setModel(m_paletteModel);
auto *const delegate = new ColorDelegate(this);
@ -72,8 +73,9 @@ void PaletteEditor::setPalette(const QPalette &palette)
resolve()
#endif
;
using MaskType = std::remove_cv_t<decltype(mask)>;
for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) {
if (mask & (1 << i)) {
if (mask & (static_cast<MaskType>(1) << static_cast<MaskType>(i))) {
continue;
}
m_editPalette.setBrush(
@ -91,7 +93,6 @@ void PaletteEditor::setPalette(const QPalette &palette)
resolve(mask)
#endif
;
updatePreviewPalette();
updateStyledButton();
m_paletteUpdated = true;
if (!m_modelUpdated) {
@ -106,6 +107,17 @@ void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPa
setPalette(palette);
}
bool PaletteEditor::event(QEvent *event)
{
switch (event->type()) {
case QEvent::LanguageChange:
m_ui->retranslateUi(this);
break;
default:;
}
return QDialog::event(event);
}
void PaletteEditor::handleComputeRadioClicked()
{
if (m_compute) {
@ -182,6 +194,8 @@ void PaletteEditor::load()
auto errorMessage = QString();
if (loadPalette(dialog.selectedFiles().constFirst(), &pal, &errorMessage)) {
setPalette(pal);
// apply again as otherwise highlight and possibly other roles are not shown until the next restart
setPalette(pal, pal);
} else {
QMessageBox::warning(this, tr("Error reading palette"), errorMessage);
}
@ -217,21 +231,6 @@ void PaletteEditor::buildPalette()
setPalette(temp);
}
void PaletteEditor::updatePreviewPalette()
{
const QPalette::ColorGroup g = currentColorGroup();
// build the preview palette
const QPalette currentPalette = palette();
QPalette previewPalette;
for (int i = QPalette::WindowText; i < QPalette::NColorRoles; ++i) {
const QPalette::ColorRole r = static_cast<QPalette::ColorRole>(i);
const QBrush br = currentPalette.brush(g, r);
previewPalette.setBrush(QPalette::Active, r, br);
previewPalette.setBrush(QPalette::Inactive, r, br);
previewPalette.setBrush(QPalette::Disabled, r, br);
}
}
void PaletteEditor::updateStyledButton()
{
m_ui->buildButton->setColor(palette().color(QPalette::Active, QPalette::Button));
@ -248,8 +247,9 @@ QPalette PaletteEditor::getPalette(QWidget *parent, const QPalette &init, const
resolve()
#endif
;
using MaskType = std::remove_cv_t<decltype(mask)>;
for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) {
if (mask & (1 << i)) {
if (mask & (static_cast<MaskType>(1) << static_cast<MaskType>(i))) {
continue;
}
parentPalette.setBrush(
@ -282,7 +282,7 @@ PaletteModel::PaletteModel(QObject *parent)
int PaletteModel::rowCount(const QModelIndex &) const
{
return m_roleNames.count();
return static_cast<int>(m_roleNames.count());
}
int PaletteModel::columnCount(const QModelIndex &) const
@ -308,9 +308,8 @@ QVariant PaletteModel::data(const QModelIndex &index, int role) const
resolve()
#endif
;
if (mask & (1 << index.row()))
return true;
return false;
using MaskType = std::remove_cv_t<decltype(mask)>;
return mask & (static_cast<MaskType>(1) << static_cast<MaskType>(index.row()));
}
return QVariant();
}
@ -348,7 +347,7 @@ bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int
m_palette.setBrush(QPalette::Disabled, QPalette::Text, br);
m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, br);
idxBegin = PaletteModel::index(0, 0);
idxEnd = PaletteModel::index(m_roleNames.count() - 1, 3);
idxEnd = PaletteModel::index(static_cast<int>(m_roleNames.count()) - 1, 3);
break;
case QPalette::Window:
m_palette.setBrush(QPalette::Disabled, QPalette::Base, br);
@ -377,7 +376,7 @@ bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int
const bool isMask = qvariant_cast<bool>(value);
const int r = index.row();
if (isMask) {
mask |= (1 << r);
mask |= (static_cast<decltype(mask)>(1) << static_cast<decltype(mask)>(r));
} else {
m_palette.setBrush(
QPalette::Active, static_cast<QPalette::ColorRole>(r), m_parentPalette.brush(QPalette::Active, static_cast<QPalette::ColorRole>(r)));
@ -385,7 +384,7 @@ bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int
m_parentPalette.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(r)));
m_palette.setBrush(QPalette::Disabled, static_cast<QPalette::ColorRole>(r),
m_parentPalette.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(r)));
mask &= ~static_cast<decltype(mask)>(1 << index.row());
mask &= ~static_cast<decltype(mask)>((static_cast<decltype(mask)>(1) << static_cast<decltype(mask)>(index.row())));
}
m_palette.
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -435,7 +434,7 @@ void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPal
m_parentPalette = parentPalette;
m_palette = palette;
const QModelIndex idxBegin = index(0, 0);
const QModelIndex idxEnd = index(m_roleNames.count() - 1, 3);
const QModelIndex idxEnd = index(static_cast<int>(m_roleNames.count()) - 1, 3);
emit dataChanged(idxBegin, idxEnd);
}

View File

@ -28,6 +28,7 @@ class PaletteEditor;
*/
class QT_UTILITIES_EXPORT PaletteEditor : public QDialog {
Q_OBJECT
Q_PROPERTY(QPalette palette READ palette WRITE setPalette)
public:
PaletteEditor(QWidget *parent);
~PaletteEditor() override;
@ -38,6 +39,9 @@ public:
void setPalette(const QPalette &palette);
void setPalette(const QPalette &palette, const QPalette &parentPalette);
protected:
bool event(QEvent *event) override;
private Q_SLOTS:
void buildPalette();
void paletteChanged(const QPalette &palette);
@ -47,7 +51,6 @@ private Q_SLOTS:
void save();
private:
void updatePreviewPalette();
void updateStyledButton();
QPalette::ColorGroup currentColorGroup() const

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,146 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="120.94489"
height="120.94489"
viewBox="0 0 32 32"
version="1.1"
id="svg8"
sodipodi:docname="edit-clear.svg"
inkscape:version="0.92.1 r"
inkscape:export-filename="../../48x48/actions/edit-clear.png"
inkscape:export-xdpi="38.099995"
inkscape:export-ydpi="38.099995">
<defs
id="defs2">
<linearGradient
id="circle-filling"
inkscape:collect="always">
<stop
style="stop-color:#464646;stop-opacity:0.41960785"
offset="0"
id="stop6209" />
<stop
id="stop6215"
offset="0.84381574"
style="stop-color:#464646;stop-opacity:0.54509807" />
<stop
style="stop-color:#1e1e1e;stop-opacity:0.74117649"
offset="0.9891246"
id="stop6217" />
<stop
style="stop-color:#1e1e1e;stop-opacity:0.74117649"
offset="1"
id="stop6211" />
</linearGradient>
<linearGradient
id="linearGradient6124"
osb:paint="solid">
<stop
style="stop-color:#656565;stop-opacity:1;"
offset="0"
id="stop6122" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#circle-filling"
id="radialGradient6207"
cx="16"
cy="281"
fx="16"
fy="281"
r="14.037946"
gradientTransform="matrix(1.0685091,-0.0070508,0.00712143,1.0792137,-3.0972671,-22.146217)"
gradientUnits="userSpaceOnUse" />
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter4754">
<feFlood
flood-opacity="0.588235"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood4744" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite4746" />
<feGaussianBlur
in="composite1"
stdDeviation="1"
result="blur"
id="feGaussianBlur4748" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset4750" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite4752" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6568542"
inkscape:cx="72.66116"
inkscape:cy="44.340881"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1030"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-265)">
<circle
style="opacity:0.8;fill:url(#radialGradient6207);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20103037;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.25;stroke-dasharray:none;stroke-dashoffset:1.99954295;stroke-opacity:0.85034012;paint-order:normal"
id="circle"
cx="16"
cy="281"
r="15" />
<path
style="fill:#ffffff;fill-opacity:0.94557822;fill-rule:evenodd;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter4754)"
d="m 12.259207,273.77243 -3.2511908,3.14457 3.7343458,3.64477 -3.6618579,3.58141 3.2511909,3.14457 3.712508,-3.60185 3.675756,3.59174 3.251191,-3.14456 -3.696885,-3.57131 3.769373,-3.63466 -3.251191,-3.14456 -3.748244,3.65508 z"
id="x"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -1,217 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="120.94489"
height="120.94489"
viewBox="0 0 32 32"
version="1.1"
id="svg8"
sodipodi:docname="edit-error.svg"
inkscape:version="0.92.1 r"
inkscape:export-filename="../../48x48/actions/edit-error.png"
inkscape:export-xdpi="38.099995"
inkscape:export-ydpi="38.099995">
<defs
id="defs2">
<linearGradient
id="circle-filling"
inkscape:collect="always">
<stop
style="stop-color:#8c2a2a;stop-opacity:0.63529414"
offset="0"
id="stop6209" />
<stop
id="stop6215"
offset="0.84381574"
style="stop-color:#8c2a2a;stop-opacity:0.63529414" />
<stop
style="stop-color:#8c2a2a;stop-opacity:0.80612242"
offset="0.9891246"
id="stop6217" />
<stop
style="stop-color:#8c2a2a;stop-opacity:0.63529414"
offset="1"
id="stop6211" />
</linearGradient>
<linearGradient
id="linearGradient6124"
osb:paint="solid">
<stop
style="stop-color:#656565;stop-opacity:1;"
offset="0"
id="stop6122" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#circle-filling"
id="radialGradient6207"
cx="16"
cy="281"
fx="16"
fy="281"
r="14.037946"
gradientTransform="matrix(1.0685091,-0.0070508,0.00712143,1.0792137,-3.0972671,-22.146217)"
gradientUnits="userSpaceOnUse" />
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter4754">
<feFlood
flood-opacity="0.588235"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood4744" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite4746" />
<feGaussianBlur
in="composite1"
stdDeviation="1"
result="blur"
id="feGaussianBlur4748" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset4750" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite4752" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter4716">
<feFlood
flood-opacity="0.580392"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood4706" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite4708" />
<feGaussianBlur
in="composite1"
stdDeviation="1"
result="blur"
id="feGaussianBlur4710" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset4712" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite4714" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter4843">
<feFlood
flood-opacity="0.588235"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood4833" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite4835" />
<feGaussianBlur
in="composite1"
stdDeviation="1"
result="blur"
id="feGaussianBlur4837" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset4839" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite4841" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="-10.86025"
inkscape:cy="45.456815"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1030"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-265)">
<circle
style="opacity:0.8;fill:url(#radialGradient6207);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20103037;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.25;stroke-dasharray:none;stroke-dashoffset:1.99954295;stroke-opacity:0.85034012;paint-order:normal"
id="circle"
cx="16"
cy="281"
r="15" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:0.82745098;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter4843)"
x="3.192997"
y="289.63605"
id="exclamation"
transform="matrix(1.6364428,0,0,1.0537851,-10.572145,-14.295562)"><tspan
sodipodi:role="line"
id="tspan4504"
x="3.192997"
y="289.63605"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25.39999962px;font-family:'nota sans';-inkscape-font-specification:'nota sans';fill:#ffffff;fill-opacity:0.82745098;stroke-width:0.26458332px"> ! </tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -1,184 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="120.94489"
height="120.94489"
viewBox="0 0 32 32"
version="1.1"
id="svg8"
sodipodi:docname="edit-menu.svg"
inkscape:version="0.92.1 r"
inkscape:export-filename="../../48x48/actions/edit-menu.png"
inkscape:export-xdpi="38.099995"
inkscape:export-ydpi="38.099995">
<defs
id="defs2">
<linearGradient
id="circle-filling"
inkscape:collect="always">
<stop
style="stop-color:#464646;stop-opacity:0.41960785"
offset="0"
id="stop6209" />
<stop
id="stop6215"
offset="0.84381574"
style="stop-color:#464646;stop-opacity:0.54509807" />
<stop
style="stop-color:#1e1e1e;stop-opacity:0.74117649"
offset="0.9891246"
id="stop6217" />
<stop
style="stop-color:#1e1e1e;stop-opacity:0.74117649"
offset="1"
id="stop6211" />
</linearGradient>
<linearGradient
id="linearGradient6124"
osb:paint="solid">
<stop
style="stop-color:#656565;stop-opacity:1;"
offset="0"
id="stop6122" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#circle-filling"
id="radialGradient6207"
cx="16"
cy="281"
fx="16"
fy="281"
r="14.037946"
gradientTransform="matrix(1.0685091,-0.0070508,0.00712143,1.0792137,-3.0972671,-22.146217)"
gradientUnits="userSpaceOnUse" />
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter4754">
<feFlood
flood-opacity="0.588235"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood4744" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite4746" />
<feGaussianBlur
in="composite1"
stdDeviation="1"
result="blur"
id="feGaussianBlur4748" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset4750" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite4752" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Drop Shadow"
id="filter4716">
<feFlood
flood-opacity="0.580392"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood4706" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="in"
result="composite1"
id="feComposite4708" />
<feGaussianBlur
in="composite1"
stdDeviation="1"
result="blur"
id="feGaussianBlur4710" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset4712" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
result="composite2"
id="feComposite4714" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="-64.901506"
inkscape:cy="39.858925"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1030"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-265)">
<circle
style="opacity:0.8;fill:url(#radialGradient6207);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20103037;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.25;stroke-dasharray:none;stroke-dashoffset:1.99954295;stroke-opacity:0.85034012;paint-order:normal"
id="circle"
cx="16"
cy="281"
r="15"
inkscape:export-filename="../../48x48/actions/circle.png"
inkscape:export-xdpi="38"
inkscape:export-ydpi="38" />
<ellipse
style="opacity:1;fill:#ffffff;fill-opacity:0.82653061;fill-rule:nonzero;stroke:none;stroke-width:0.44207191;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.99954295;stroke-opacity:1;paint-order:normal;filter:url(#filter4716)"
id="inner-circle"
cx="-43.410675"
cy="278.35611"
rx="6.4542603"
ry="6.487721"
transform="matrix(0.85951266,-0.17525769,0.18535814,0.85739117,1.8691763,34.503158)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -23,6 +23,8 @@ IMPORT_IMAGE_FORMAT_PLUGINS
#endif
IMPORT_WIDGET_STYLE_PLUGINS
#endif // defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
IMPORT_TLS_PLUGINS
IMPORT_NETWORK_INFORMATION_PLUGINS
#endif // QT_STATIC
#endif // MISC_UTILS_IMPORT_PLUGIN_H

View File

@ -1,5 +1,7 @@
#include "./qtconfigarguments.h"
#include "../misc/compat.h"
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/io/ansiescapecodes.h>
@ -15,6 +17,12 @@
#include <QGuiApplication>
#endif
#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
#define QT_UTILITIES_CHECK_WINDOWS_VERSION
#include <QMessageBox>
#include <QOperatingSystemVersion>
#endif
#include <initializer_list>
#include <iostream>
@ -127,14 +135,14 @@ void QtConfigArguments::applySettings(bool preventApplyingDefaultFont) const
}
#endif
if (m_iconThemeArg.isPresent()) {
auto i = m_iconThemeArg.values().cbegin(), end = m_iconThemeArg.values().end();
if (i != end) {
QIcon::setThemeName(QString::fromLocal8Bit(*i));
// set first value of m_iconThemeArg as icon theme and add further values as search paths
if (auto i = m_iconThemeArg.values().cbegin(), end = m_iconThemeArg.values().cend(); i != end) {
QIcon::setThemeName(QString::fromUtf8(*i));
if (++i != end) {
QStringList searchPaths;
searchPaths.reserve(static_cast<QStringList::size_type>(m_iconThemeArg.values().size() - 1));
auto searchPaths = QStringList();
searchPaths.reserve(static_cast<QStringList::size_type>(m_iconThemeArg.values().size()));
for (; i != end; ++i) {
searchPaths << QString::fromLocal8Bit(*i);
searchPaths << QString::fromUtf8(*i);
}
searchPaths << QStringLiteral(":/icons");
QIcon::setThemeSearchPaths(searchPaths);
@ -142,24 +150,14 @@ void QtConfigArguments::applySettings(bool preventApplyingDefaultFont) const
}
} else {
if (qEnvironmentVariableIsSet("ICON_THEME_SEARCH_PATH")) {
QString path;
path.append(qgetenv("ICON_THEME_SEARCH_PATH"));
QIcon::setThemeSearchPaths(QStringList({ path, QStringLiteral(":/icons") }));
QIcon::setThemeSearchPaths(QStringList({ qEnvironmentVariable("ICON_THEME_SEARCH_PATH"), QStringLiteral(":/icons") }));
} else {
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << QStringLiteral("../share/icons") << QStringLiteral(":/icons"));
}
if (qEnvironmentVariableIsSet("ICON_THEME")) {
QString themeName;
themeName.append(qgetenv("ICON_THEME"));
QIcon::setThemeName(themeName);
QIcon::setThemeName(qEnvironmentVariable("ICON_THEME"));
}
}
#ifdef Q_OS_WIN32
// default configuration under Windows
if (QIcon::themeName().isEmpty()) {
QIcon::setThemeName(QStringLiteral("default"));
}
#endif
if (m_fontArg.isPresent()) {
QFont font;
font.setFamily(QString::fromLocal8Bit(m_fontArg.values().front()));
@ -188,5 +186,14 @@ void QtConfigArguments::applySettings(bool preventApplyingDefaultFont) const
if (m_sceneGraphRenderLoopArg.isPresent()) {
qputenv(m_sceneGraphRenderLoopArg.environmentVariable(), QByteArray(m_sceneGraphRenderLoopArg.firstValue()));
}
#ifdef QT_UTILITIES_CHECK_WINDOWS_VERSION
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10_1809) {
QMessageBox::warning(nullptr, QCoreApplication::applicationName(),
QCoreApplication::translate("QtConfigArguments",
"This application requires Windows 10, version 1809 or newer. The current Windows version is older so the application might not work "
"correctly."));
}
#endif
}
} // namespace CppUtilities

View File

@ -9,6 +9,13 @@
#include <QQuickStyle>
#include <QString>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#if defined(PLATFORM_ANDROID)
#define QT_UTILITIES_DEFAULT_QQC2_STYLE "Material"
#elif defined(PLATFORM_WINDOWS)
#define QT_UTILITIES_DEFAULT_QQC2_STYLE "Universal"
#endif
#else
#if defined(PLATFORM_ANDROID)
#define QT_UTILITIES_DEFAULT_QQC2_STYLE "material"
#elif defined(PLATFORM_WINDOWS)
@ -16,6 +23,8 @@
#endif
#endif
#endif
namespace CppUtilities {
class QT_UTILITIES_EXPORT QtConfigArguments {

View File

@ -68,6 +68,11 @@ void cleanup()
*/
namespace TranslationFiles {
/*!
* \brief The translators installed via the load-functions in this namespace.
*/
static QList<QTranslator *> translators;
/*!
* \brief Allows to set an additional search path for translation files.
* \remarks This path is considered *before* the default directories.
@ -132,6 +137,7 @@ void loadQtTranslationFile(initializer_list<QString> repositoryNames, const QStr
|| qtTranslator->load(fileName, path = QStringLiteral("../share/qt/translations"))
|| qtTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
QCoreApplication::installTranslator(qtTranslator);
translators.append(qtTranslator);
if (debugTranslations) {
cerr << "Loading translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and the locale \""
<< localeName.toLocal8Bit().data() << "\" from \"" << path.toLocal8Bit().data() << "\"." << endl;
@ -223,13 +229,13 @@ void loadApplicationTranslationFile(const QString &configName, const QString &ap
QString path;
if ((!additionalTranslationFilePath().isEmpty() && appTranslator->load(fileName, path = additionalTranslationFilePath()))
|| appTranslator->load(fileName, path = QStringLiteral(".")) || appTranslator->load(fileName, path = QStringLiteral("../") % directoryName)
|| appTranslator->load(fileName, path = QStringLiteral("../") % directoryName)
|| appTranslator->load(fileName, path = QStringLiteral("../../") % directoryName)
|| appTranslator->load(fileName, path = QStringLiteral("./translations"))
|| appTranslator->load(fileName, path = QStringLiteral("../share/") % directoryName % QStringLiteral("/translations"))
|| appTranslator->load(fileName, path = QStringLiteral(APP_INSTALL_PREFIX "/share/") % directoryName % QStringLiteral("/translations"))
|| appTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
QCoreApplication::installTranslator(appTranslator);
translators.append(appTranslator);
if (qEnvironmentVariableIsSet("QT_DEBUG_TRANSLATIONS")) {
logTranslationEvent("Loading", configName, applicationName, localeName, path);
}
@ -267,6 +273,18 @@ void loadApplicationTranslationFile(const QString &configName, const std::initia
loadApplicationTranslationFile(configName, applicationName, localeName);
}
}
/*!
* \brief Clears all translation files previously loaded via the load-functions in this namespace.
*/
void clearTranslationFiles()
{
for (auto *const translator : translators) {
QCoreApplication::removeTranslator(translator);
}
translators.clear();
}
} // namespace TranslationFiles
/*!
@ -313,12 +331,86 @@ bool hasCoreApp()
*/
void setupCommonQtApplicationAttributes()
{
// enable dark window frame on Windows if the configured color palette is dark
// - supported as of Qt 6.4; no longer required as of Qt 6.5
// - see https://bugreports.qt.io/browse/QTBUG-72028?focusedCommentId=677819#comment-677819
// and https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5
#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
if (const auto qtVersion = QLibraryInfo::version();
qtVersion >= QVersionNumber(6, 4, 0) && qtVersion < QVersionNumber(6, 5, 0) && !qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
qputenv("QT_QPA_PLATFORM", "windows:darkmode=1");
}
#endif
// ensure FONTCONFIG_PATH is set (mainly required for static GNU/Linux builds)
#ifdef QT_FEATURE_fontdialog
if (!qEnvironmentVariableIsSet("FONTCONFIG_PATH") && QDir(QStringLiteral("/etc/fonts")).exists()) {
qputenv("FONTCONFIG_PATH", "/etc/fonts");
}
#endif
// enable settings for High-DPI scaling
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
if (!QCoreApplication::instance()) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
}
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
#endif
}
/*!
* \brief Returns the settings object for the specified \a organization and \a application.
* \remarks
* - This function always uses INI as that's what I'd like to use in all of my applications consistently, regardless of the platform.
* - The parameter \a application might be empty. In fact, most of my applications use just `getSettings(QStringLiteral(PROJECT_NAME))`.
* - This function first checks whether a file called `$organization/$application.ini` exists in the current working directory (or just
* `$organization.ini` if \a application is empty) and uses that if it exists. That allows having a portable installation.
* - Some of my apps where using values from QCoreApplication for \a organization and \a application in the beginning. This function
* moves those old config files to their new location if needed. This extra handling will likely removed at some point. Note that
* I moved away from using values from QCoreApplication to avoid having spaces and additional config suffixes in the file name.
*/
std::unique_ptr<QSettings> getSettings(const QString &organization, const QString &application)
{
auto settings = std::unique_ptr<QSettings>();
if (const auto portableFile
= QFile(application.isEmpty() ? organization + QStringLiteral(".ini") : organization % QChar('/') % application % QStringLiteral(".ini"));
portableFile.exists()) {
settings = std::make_unique<QSettings>(portableFile.fileName(), QSettings::IniFormat);
} else {
settings = std::make_unique<QSettings>(QSettings::IniFormat, QSettings::UserScope, organization, application);
// move config created by older versions to new location
if (organization != QCoreApplication::organizationName() || application != QCoreApplication::applicationName()) {
const auto oldConfig
= QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName())
.fileName();
QFile::rename(oldConfig, settings->fileName()) || QFile::remove(oldConfig);
}
}
settings->sync();
return settings;
}
/*!
* \brief Returns an error message for the specified \a settings or an empty string if there's no error.
*/
QString errorMessageForSettings(const QSettings &settings)
{
auto errorMessage = QString();
switch (settings.status()) {
case QSettings::NoError:
return QString();
case QSettings::AccessError:
errorMessage = QCoreApplication::translate("QtUtilities", "unable to access file");
break;
case QSettings::FormatError:
errorMessage = QCoreApplication::translate("QtUtilities", "file has invalid format");
break;
default:
errorMessage = QCoreApplication::translate("QtUtilities", "unknown error");
}
return QCoreApplication::translate("QtUtilities", "Unable to sync settings from \"%1\": %2").arg(settings.fileName(), errorMessage);
}
// namespace ApplicationInstances
} // namespace QtUtilities

View File

@ -3,10 +3,12 @@
#include "../global.h"
#include <QString>
#include <QtContainerFwd>
#include <QtGlobal>
#include <initializer_list>
#include <memory>
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QSettings)
@ -14,6 +16,18 @@ QT_FORWARD_DECLARE_CLASS(QSettings)
QT_FORWARD_DECLARE_CLASS(QStringList)
#endif
/*!
* \brief Sets the base name of the desktop entry for this application from buildsystem-provided meta-data.
* \remarks
* - This is done as part of SET_QT_APPLICATION_INFO and thus normally doesn't need to be invoked individually.
* - This macro is still experimental.
*/
#define SET_QT_DESKTOP_FILE_NAME
#if defined(Q_OS_LINUX) && defined(qGuiApp) && defined(APP_ID)
#undef SET_QT_DESKTOP_FILE_NAME
#define SET_QT_DESKTOP_FILE_NAME QGuiApplication::setDesktopFileName(QStringLiteral(APP_ID));
#endif
/*!
* \brief Sets the application meta data in the QCoreApplication singleton and attributes commonly used
* within my applications.
@ -24,6 +38,7 @@ QT_FORWARD_DECLARE_CLASS(QStringList)
QCoreApplication::setOrganizationDomain(QStringLiteral(APP_DOMAIN)); \
QCoreApplication::setApplicationName(QStringLiteral(APP_NAME)); \
QCoreApplication::setApplicationVersion(QStringLiteral(APP_VERSION)); \
SET_QT_DESKTOP_FILE_NAME \
::QtUtilities::setupCommonQtApplicationAttributes()
/*!
@ -51,6 +66,7 @@ QT_UTILITIES_EXPORT void loadApplicationTranslationFile(const QString &configNam
QT_UTILITIES_EXPORT void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames);
QT_UTILITIES_EXPORT void loadApplicationTranslationFile(
const QString &configName, const std::initializer_list<QString> &applicationNames, const QString &localeName);
QT_UTILITIES_EXPORT void clearTranslationFiles();
} // namespace TranslationFiles
namespace ApplicationInstances {
@ -65,6 +81,8 @@ QT_UTILITIES_EXPORT bool hasCoreApp();
} // namespace ApplicationInstances
QT_UTILITIES_EXPORT void setupCommonQtApplicationAttributes();
QT_UTILITIES_EXPORT std::unique_ptr<QSettings> getSettings(const QString &organization, const QString &application = QString());
QT_UTILITIES_EXPORT QString errorMessageForSettings(const QSettings &settings);
} // namespace QtUtilities

View File

@ -1,6 +1,9 @@
#include "./optioncategory.h"
#include "./optionpage.h"
#include <QCoreApplication>
#include <QEvent>
namespace QtUtilities {
/*!
@ -59,6 +62,20 @@ void OptionCategory::resetAllPages()
}
}
/*!
* \brief Triggers retranslation of all pages.
* \remarks Has no effect if the pages don't react to the LanguageChange event.
*/
void OptionCategory::retranslateAllPages()
{
auto event = QEvent(QEvent::LanguageChange);
for (auto *const page : m_pages) {
if (page->hasBeenShown()) {
QCoreApplication::sendEvent(page->widget(), &event);
}
}
}
/*!
* \brief Returns whether the option category matches the specified \a
* searchKeyWord.

View File

@ -33,6 +33,7 @@ public:
void assignPages(const QList<OptionPage *> &pages);
bool applyAllPages();
void resetAllPages();
void retranslateAllPages();
bool matches(const QString &searchKeyWord) const;
int currentIndex() const;
void setCurrentIndex(int currentIndex);
@ -40,7 +41,7 @@ public:
Q_SIGNALS:
void displayNameChanged(const QString &displayName);
void iconChanged(const QIcon &icon);
void pagesChanged(const QList<OptionPage *> &pages);
void pagesChanged(const QList<QtUtilities::OptionPage *> &pages);
private:
QString m_displayName;

View File

@ -6,6 +6,8 @@
#include <QStyle>
#endif
#include <utility>
namespace QtUtilities {
/*!
@ -30,7 +32,7 @@ OptionCategoryModel::OptionCategoryModel(const QList<OptionCategory *> &categori
: QAbstractListModel(parent)
, m_categories(categories)
{
for (OptionCategory *category : m_categories) {
for (OptionCategory *category : std::as_const(m_categories)) {
category->setParent(this);
}
}
@ -52,7 +54,7 @@ void OptionCategoryModel::setCategories(const QList<OptionCategory *> &categorie
beginResetModel();
qDeleteAll(m_categories);
m_categories = categories;
for (OptionCategory *const category : m_categories) {
for (OptionCategory *const category : std::as_const(m_categories)) {
category->setParent(this);
connect(category, &OptionCategory::displayNameChanged, this, &OptionCategoryModel::categoryChangedName);
connect(category, &OptionCategory::iconChanged, this, &OptionCategoryModel::categoryChangedIcon);
@ -62,7 +64,7 @@ void OptionCategoryModel::setCategories(const QList<OptionCategory *> &categorie
int OptionCategoryModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_categories.size();
return parent.isValid() ? 0 : static_cast<int>(m_categories.size());
}
QVariant OptionCategoryModel::data(const QModelIndex &index, int role) const
@ -98,7 +100,7 @@ void OptionCategoryModel::categoryChangedName()
if (!senderCategory) {
return;
}
for (int i = 0, end = m_categories.size(); i < end; ++i) {
for (int i = 0, end = static_cast<int>(m_categories.size()); i < end; ++i) {
if (senderCategory == m_categories.at(i)) {
QModelIndex index = this->index(i);
emit dataChanged(index, index, QVector<int>({ Qt::DisplayRole }));
@ -115,7 +117,7 @@ void OptionCategoryModel::categoryChangedIcon()
if (!senderCategory) {
return;
}
for (int i = 0, end = m_categories.size(); i < end; ++i) {
for (int i = 0, end = static_cast<int>(m_categories.size()); i < end; ++i) {
if (senderCategory == m_categories.at(i)) {
QModelIndex index = this->index(i);
emit dataChanged(index, index, QVector<int>({ Qt::DecorationRole }));

View File

@ -1,11 +1,14 @@
#include "./optionpage.h"
#include <QCheckBox>
#include <QEvent>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QRadioButton>
#include <utility>
namespace QtUtilities {
/*!
@ -81,12 +84,29 @@ bool OptionPage::matches(const QString &searchKeyWord)
m_keywords << groupBox->title();
m_keywordsInitialized = true;
}
for (const QString &keyword : m_keywords)
for (const QString &keyword : std::as_const(m_keywords))
if (keyword.contains(searchKeyWord, Qt::CaseInsensitive))
return true;
return false;
}
/*!
* \brief Emits the paletteChanged() signal.
*/
bool OptionPageWidget::event(QEvent *event)
{
switch (event->type()) {
case QEvent::PaletteChange:
emit paletteChanged();
break;
case QEvent::LanguageChange:
emit retranslationRequired();
break;
default:;
}
return QWidget::event(event);
}
/*!
* \fn OptionPage::apply()
* \brief Applies altered settings.

View File

@ -12,6 +12,25 @@ namespace QtUtilities {
class SettingsDialog;
class QT_UTILITIES_EXPORT OptionPageWidget : public QWidget {
Q_OBJECT
public:
explicit OptionPageWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
Q_SIGNALS:
void paletteChanged();
void retranslationRequired();
protected:
bool event(QEvent *) override;
};
inline OptionPageWidget::OptionPageWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
}
class QT_UTILITIES_EXPORT OptionPage {
friend class SettingsDialog;
@ -125,11 +144,12 @@ template <class UiClass> UiFileBasedOptionPage<UiClass>::~UiFileBasedOptionPage(
*/
template <class UiClass> QWidget *UiFileBasedOptionPage<UiClass>::setupWidget()
{
QWidget *widget = new QWidget();
auto *const widget = new OptionPageWidget();
if (!m_ui) {
m_ui.reset(new UiClass);
}
m_ui->setupUi(widget);
QObject::connect(widget, &OptionPageWidget::retranslationRequired, [this, widget] { m_ui->retranslateUi(widget); });
return widget;
}

View File

@ -10,22 +10,34 @@
#include "../resources/resources.h"
#include "../misc/desktoputils.h"
#include "resources/config.h"
#include "ui_qtappearanceoptionpage.h"
#include "ui_qtenvoptionpage.h"
#include "ui_qtlanguageoptionpage.h"
#include <c++utilities/application/commandlineutils.h>
#include <QDir>
#include <QFileDialog>
#include <QFontDialog>
#include <QIcon>
#include <QOperatingSystemVersion>
#include <QSettings>
#include <QStringBuilder>
#include <QStyleFactory>
#include <QVersionNumber>
#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
#include <QOperatingSystemVersion>
#define QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
#endif
#include <iostream>
#include <memory>
using namespace std;
#include <optional>
namespace QtUtilities {
@ -33,13 +45,20 @@ struct QtSettingsData {
QtSettingsData();
QFont font;
QPalette palette;
std::optional<QFont> initialFont;
QPalette palette; // the currently applied palette (only in use if customPalette is true, though)
QPalette selectedPalette; // the intermediately selected palette (chosen in palette editor but not yet applied)
QString widgetStyle;
QString initialWidgetStyle;
QString styleSheetPath;
QString iconTheme;
QString initialIconTheme;
QLocale defaultLocale;
QLocale previousLocale;
QString localeName;
QString previousPluginDirectory;
QString additionalPluginDirectory;
QString previousIconThemeSearchPath;
QString additionalIconThemeSearchPath;
bool customFont;
bool customPalette;
@ -47,10 +66,14 @@ struct QtSettingsData {
bool customStyleSheet;
bool customIconTheme;
bool customLocale;
bool isPaletteDark;
bool showNotices;
bool retranslatable;
};
inline QtSettingsData::QtSettingsData()
: iconTheme(QIcon::themeName())
, initialIconTheme(iconTheme)
, localeName(defaultLocale.name())
, customFont(false)
, customPalette(false)
@ -58,6 +81,9 @@ inline QtSettingsData::QtSettingsData()
, customStyleSheet(false)
, customIconTheme(false)
, customLocale(false)
, isPaletteDark(false)
, showNotices(true)
, retranslatable(false)
{
}
@ -69,7 +95,7 @@ inline QtSettingsData::QtSettingsData()
* system-default.
*/
QtSettings::QtSettings()
: m_d(make_unique<QtSettingsData>())
: m_d(std::make_unique<QtSettingsData>())
{
}
@ -82,6 +108,35 @@ QtSettings::~QtSettings()
{
}
/*!
* \brief Disables notices on option pages that settings take only effect after restarting.
* \remarks
* - This function must be called before obtaining the option pages via category().
* - Only call this function if the application actually re-applies these settings when the
* settings dialog is applied and when it is generally able to handle widget style and
* palette changes well.
* - The localization option page's notice is handled via setRetranslatable() and the notice
* for the environment page is still always shown (as those settings can never be applied
* at runtime). So this affects only the appearance page at this point.
*/
void QtSettings::disableNotices()
{
m_d->showNotices = false;
}
/*!
* \brief Sets whether the application supports changing the locale settings at runtime.
* \remarks
* Set this to true if the application will retranslate its UI after the locale has changed.
* This requires the application to re-install translators and to re-invoke all ts() and
* translate() function calls. If set to true, the notice that the locale setting takes only
* effect after restarting is not shown anymore.
*/
void QtSettings::setRetranslatable(bool retranslatable)
{
m_d->retranslatable = retranslatable;
}
/*!
* \brief Returns whether a custom font is set.
*/
@ -141,72 +196,243 @@ void QtSettings::save(QSettings &settings) const
settings.endGroup();
}
/*!
* \brief Returns the icon themes present in the specified \a searchPaths.
* \remarks The display name is the key and the actual icon theme name the value.
* This way the map is sorted correctly for display purposes.
*/
static QMap<QString, QString> scanIconThemes(const QStringList &searchPaths)
{
auto res = QMap<QString, QString>();
for (const auto &searchPath : searchPaths) {
const auto dir = QDir(searchPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
for (const auto &iconTheme : dir) {
auto indexFile = QFile(searchPath % QChar('/') % iconTheme % QStringLiteral("/index.theme"));
auto index = QByteArray();
if (indexFile.open(QFile::ReadOnly) && !(index = indexFile.readAll()).isEmpty()) {
const auto iconThemeSection = index.indexOf("[Icon Theme]");
const auto nameStart = index.indexOf("Name=", iconThemeSection != -1 ? iconThemeSection : 0);
if (nameStart != -1) {
auto nameLength = index.indexOf("\n", nameStart) - nameStart - 5;
if (nameLength > 0) {
auto displayName = QString::fromUtf8(index.mid(nameStart + 5, nameLength));
if (displayName != iconTheme) {
displayName += QChar(' ') % QChar('(') % iconTheme % QChar(')');
}
res[displayName] = iconTheme;
continue;
}
}
}
res[iconTheme] = iconTheme;
}
}
return res;
}
/*!
* \brief Applies the current configuration.
* \remarks
* - Some settings take only affect after restarting the application.
* - QApplication/QGuiApplication must be instantiated before calling this
* method.
* - Hence it makes most sense to call this directly after instantiating
* QApplication/QGuiApplication must be instantiated before calling this
* method. Hence it makes most sense to call this directly after instantiating
* QApplication/QGuiApplication.
*
* This function may be called multiple times. This supports restoring default
* settings (e.g. if use of a custom icon theme has been disabled again after it
* was enabled the default icon theme will be configured again).
*/
void QtSettings::apply()
{
// apply environment
if (m_d->additionalPluginDirectory != m_d->previousPluginDirectory) {
if (!m_d->previousPluginDirectory.isEmpty()) {
QCoreApplication::removeLibraryPath(m_d->previousPluginDirectory);
}
if (!m_d->additionalPluginDirectory.isEmpty()) {
QCoreApplication::addLibraryPath(m_d->additionalPluginDirectory);
}
m_d->previousPluginDirectory = m_d->additionalPluginDirectory;
}
if (m_d->additionalIconThemeSearchPath != m_d->previousIconThemeSearchPath) {
auto paths = QIcon::themeSearchPaths();
if (!m_d->previousIconThemeSearchPath.isEmpty()) {
paths.removeAll(m_d->previousIconThemeSearchPath);
}
if (!m_d->additionalIconThemeSearchPath.isEmpty()) {
paths.append(m_d->additionalIconThemeSearchPath);
}
m_d->previousIconThemeSearchPath = m_d->additionalIconThemeSearchPath;
QIcon::setThemeSearchPaths(paths);
}
// read style sheet
QString styleSheet;
auto styleSheet = QString();
if (m_d->customStyleSheet && !m_d->styleSheetPath.isEmpty()) {
QFile file(m_d->styleSheetPath);
auto file = QFile(m_d->styleSheetPath);
if (!file.open(QFile::ReadOnly)) {
cerr << "Unable to open the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\"." << endl;
std::cerr << "Unable to open the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\"." << std::endl;
}
styleSheet.append(file.readAll());
if (file.error() != QFile::NoError) {
cerr << "Unable to read the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\"." << endl;
std::cerr << "Unable to read the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\"." << std::endl;
}
}
// apply appearance
if (m_d->customFont) {
QGuiApplication::setFont(m_d->font);
}
if (m_d->customWidgetStyle) {
QApplication::setStyle(m_d->widgetStyle);
}
if (!styleSheet.isEmpty()) {
if (auto *qapp = qobject_cast<QApplication *>(QApplication::instance())) {
qapp->setStyleSheet(styleSheet);
} else {
cerr << "Unable to apply the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data()
<< "\" because no QApplication has been instantiated." << endl;
if (!m_d->initialFont.has_value()) {
m_d->initialFont = QGuiApplication::font();
}
QGuiApplication::setFont(m_d->font);
} else if (m_d->initialFont.has_value()) {
QGuiApplication::setFont(m_d->initialFont.value());
}
#ifdef QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
if (m_d->initialWidgetStyle.isEmpty()) {
// use Fusion on Windows 11 as the native style doesn't look good
// see https://bugreports.qt.io/browse/QTBUG-97668
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) {
m_d->initialWidgetStyle = QStringLiteral("Fusion");
}
}
#endif
if (m_d->customWidgetStyle) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
const auto *const currentStyle = QApplication::style();
if (m_d->initialWidgetStyle.isEmpty() && currentStyle) {
m_d->initialWidgetStyle = currentStyle->name();
}
#endif
QApplication::setStyle(m_d->widgetStyle);
} else if (!m_d->initialWidgetStyle.isEmpty()) {
QApplication::setStyle(m_d->initialWidgetStyle);
}
if (auto *const qapp = qobject_cast<QApplication *>(QApplication::instance())) {
qapp->setStyleSheet(styleSheet);
} else {
std::cerr << "Unable to apply the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data()
<< "\" because no QApplication has been instantiated." << std::endl;
}
if (m_d->customPalette) {
QGuiApplication::setPalette(m_d->palette);
} else {
QGuiApplication::setPalette(QPalette());
}
m_d->isPaletteDark = isPaletteDark();
if (m_d->customIconTheme) {
QIcon::setThemeName(m_d->iconTheme);
} else if (!m_d->initialIconTheme.isEmpty()) {
if (m_d->iconTheme != m_d->initialIconTheme) {
// set the icon theme back to what it was before changing anything (not sure how to read the current system icon theme again)
QIcon::setThemeName(m_d->initialIconTheme);
}
} else {
// use bundled default icon theme matching the current palette
// notes: - It is ok that search paths specified via CLI arguments are not set here yet. When doing so one should also
// specify the desired icon theme explicitly.
// - The icon themes "default" and "default-dark" come from QtConfig.cmake which makes the first non-dark bundled
// icon theme available as "default" and the first dark icon theme available as "default-dark". An icon theme
// is considered dark if it ends with "-dark".
const auto bundledIconThemes = scanIconThemes(QStringList(QStringLiteral(":/icons")));
if (m_d->isPaletteDark && bundledIconThemes.contains(QStringLiteral("default-dark"))) {
QIcon::setThemeName(QStringLiteral("default-dark"));
} else if (bundledIconThemes.contains(QStringLiteral("default"))) {
QIcon::setThemeName(QStringLiteral("default"));
}
}
// apply locale
m_d->previousLocale = QLocale();
QLocale::setDefault(m_d->customLocale ? QLocale(m_d->localeName) : m_d->defaultLocale);
// apply environment
if (m_d->additionalPluginDirectory.isEmpty()) {
QCoreApplication::addLibraryPath(m_d->additionalPluginDirectory);
// log some debug information on the first call if env variable set
static auto debugInfoLogged = false;
if (debugInfoLogged) {
return;
}
if (!m_d->additionalIconThemeSearchPath.isEmpty()) {
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << m_d->additionalIconThemeSearchPath);
const auto debugLoggingEnabled = CppUtilities::isEnvVariableSet(PROJECT_VARNAME_UPPER "_LOG_QT_CONFIG");
if (debugLoggingEnabled.has_value() && debugLoggingEnabled.value()) {
if (const auto os = QOperatingSystemVersion::current(); os.type() != static_cast<decltype(os.type())>(QOperatingSystemVersion::Unknown)) {
const auto version = QVersionNumber(os.majorVersion(), os.minorVersion(), os.microVersion());
std::cerr << "OS name and version: " << os.name().toStdString() << ' ' << version.toString().toStdString() << '\n';
}
std::cerr << "Qt version: " << qVersion() << '\n';
std::cerr << "Qt platform (set QT_QPA_PLATFORM to override): " << QGuiApplication::platformName().toStdString() << '\n';
std::cerr << "Qt locale: " << QLocale().name().toStdString() << '\n';
std::cerr << "Qt library paths: " << QCoreApplication::libraryPaths().join(':').toStdString() << '\n';
std::cerr << "Qt theme search paths: " << QIcon::themeSearchPaths().join(':').toStdString() << '\n';
debugInfoLogged = true;
}
}
/*!
* \brief Re-applies default icon theme assuming the palette is dark or not depending on \a isPaletteDark.
*
* Re-assigns the appropriate default icon theme depending on the current palette. Call this function after the
* darkmode setting has changed without the current palette reflecting that but you want the darkmode setting take
* precedence over the palette (e.g. when using Qt Quick Material style under Android). If your application makes
* actually use of the palette and you want the palette take precedence than call \a reevaluatePaletteAndDefaultIconTheme()
* instead.
*
* Note that QtSettings::isPaletteDark() will return \a isPaletteDark after calling this function (even though the current
* palette is not actually reflecting that).
*
* \remarks
* - The default icon theme must have been assigned before using the apply() function.
* - This function has no effect if a custom icon theme is configured.
*/
void QtSettings::reapplyDefaultIconTheme(bool isPaletteDark)
{
if (isPaletteDark == m_d->isPaletteDark) {
return; // no need to do anything if there's no change
}
m_d->isPaletteDark = isPaletteDark;
if (auto iconTheme = QIcon::themeName(); iconTheme == QStringLiteral("default") || iconTheme == QStringLiteral("default-dark")) {
QIcon::setThemeName(m_d->isPaletteDark ? QStringLiteral("default-dark") : QStringLiteral("default"));
}
}
/*!
* \brief Re-evaluates whether the palette is dark and re-applies default icon theme.
*
* Re-assigns the appropriate default icon theme depending on the current palette. Call this function after the palette
* has changed.
*
* \remarks
* - The default icon theme must have been assigned before using the apply() function.
* - This function has no effect if a custom icon theme is configured.
*/
void QtSettings::reevaluatePaletteAndDefaultIconTheme()
{
reapplyDefaultIconTheme(QtUtilities::isPaletteDark());
}
/*!
* \brief Returns whether the palette is dark.
* \remarks
* Changes to the palette since the last call to apply() and reevaluatePaletteAndDefaultIconTheme() are not taken
* into account.
*/
bool QtSettings::isPaletteDark()
{
return m_d->isPaletteDark;
}
/*!
* \brief Returns whether the last apply() call has changed the default locale.
*/
bool QtSettings::hasLocaleChanged() const
{
return m_d->previousLocale != QLocale();
}
/*!
* \brief Returns a new OptionCatecory containing all Qt related option pages.
* \remarks
* - The QtSettings instance does not keep the ownership over the returned
* category.
* - The pages of the returned category require the QtSetings instance which
* hence
* must be present as long as all pages are destroyed.
* category.
* - The pages of the returned category require the QtSettings instance which
* hence must be present as long as all pages are destroyed.
*/
OptionCategory *QtSettings::category()
{
@ -230,13 +456,13 @@ QtAppearanceOptionPage::~QtAppearanceOptionPage()
bool QtAppearanceOptionPage::apply()
{
m_settings.font = ui()->fontComboBox->font();
m_settings.font = ui()->fontComboBox->currentFont();
m_settings.customFont = !ui()->fontCheckBox->isChecked();
m_settings.widgetStyle = ui()->widgetStyleComboBox->currentText();
m_settings.customWidgetStyle = !ui()->widgetStyleCheckBox->isChecked();
m_settings.styleSheetPath = ui()->styleSheetPathSelection->lineEdit()->text();
m_settings.customStyleSheet = !ui()->styleSheetCheckBox->isChecked();
m_settings.palette = ui()->paletteToolButton->palette();
m_settings.palette = m_settings.selectedPalette;
m_settings.customPalette = !ui()->paletteCheckBox->isChecked();
m_settings.iconTheme
= ui()->iconThemeComboBox->currentIndex() != -1 ? ui()->iconThemeComboBox->currentData().toString() : ui()->iconThemeComboBox->currentText();
@ -253,7 +479,7 @@ void QtAppearanceOptionPage::reset()
ui()->widgetStyleCheckBox->setChecked(!m_settings.customWidgetStyle);
ui()->styleSheetPathSelection->lineEdit()->setText(m_settings.styleSheetPath);
ui()->styleSheetCheckBox->setChecked(!m_settings.customStyleSheet);
ui()->paletteToolButton->setPalette(m_settings.palette);
m_settings.selectedPalette = m_settings.palette;
ui()->paletteCheckBox->setChecked(!m_settings.customPalette);
int iconThemeIndex = ui()->iconThemeComboBox->findData(m_settings.iconTheme);
if (iconThemeIndex != -1) {
@ -268,6 +494,9 @@ QWidget *QtAppearanceOptionPage::setupWidget()
{
// call base implementation first, so ui() is available
auto *widget = QtAppearanceOptionPageBase::setupWidget();
if (!m_settings.showNotices) {
ui()->label->hide();
}
// setup widget style selection
ui()->widgetStyleComboBox->addItems(QStyleFactory::keys());
@ -276,7 +505,7 @@ QWidget *QtAppearanceOptionPage::setupWidget()
ui()->styleSheetPathSelection->provideCustomFileMode(QFileDialog::ExistingFile);
// setup font selection
QObject::connect(ui()->fontPushButton, &QPushButton::clicked, [this] {
QObject::connect(ui()->fontPushButton, &QPushButton::clicked, widget, [this] {
if (!m_fontDialog) {
m_fontDialog = new QFontDialog(this->widget());
m_fontDialog->setCurrentFont(ui()->fontComboBox->font());
@ -287,38 +516,19 @@ QWidget *QtAppearanceOptionPage::setupWidget()
});
// setup palette selection
QObject::connect(ui()->paletteToolButton, &QToolButton::clicked,
[this] { ui()->paletteToolButton->setPalette(PaletteEditor::getPalette(this->widget(), ui()->paletteToolButton->palette())); });
QObject::connect(ui()->paletteToolButton, &QToolButton::clicked, ui()->paletteToolButton,
[this] { m_settings.selectedPalette = PaletteEditor::getPalette(this->widget(), m_settings.selectedPalette); });
// setup icon theme selection
const QStringList searchPaths = QIcon::themeSearchPaths() << QStringLiteral("/usr/share/icons/");
for (const QString &searchPath : searchPaths) {
for (const QString &iconTheme : QDir(searchPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name)) {
const int existingItemIndex = ui()->iconThemeComboBox->findData(iconTheme);
QFile indexFile(searchPath % QChar('/') % iconTheme % QStringLiteral("/index.theme"));
QByteArray index;
if (indexFile.open(QFile::ReadOnly) && !(index = indexFile.readAll()).isEmpty()) {
const int iconThemeSection = index.indexOf("[Icon Theme]");
const int nameStart = index.indexOf("Name=", iconThemeSection != -1 ? iconThemeSection : 0);
if (nameStart != -1) {
int nameLength = index.indexOf("\n", nameStart) - nameStart - 5;
if (nameLength > 0) {
QString displayName = index.mid(nameStart + 5, nameLength);
if (displayName != iconTheme) {
displayName += QChar(' ') % QChar('(') % iconTheme % QChar(')');
}
if (existingItemIndex != -1) {
ui()->iconThemeComboBox->setItemText(existingItemIndex, displayName);
} else {
ui()->iconThemeComboBox->addItem(displayName, iconTheme);
}
continue;
}
}
}
if (existingItemIndex == -1) {
ui()->iconThemeComboBox->addItem(iconTheme, iconTheme);
}
const auto iconThemes = scanIconThemes(QIcon::themeSearchPaths() << QStringLiteral("/usr/share/icons/"));
auto *iconThemeComboBox = ui()->iconThemeComboBox;
for (auto i = iconThemes.begin(), end = iconThemes.end(); i != end; ++i) {
const auto &displayName = i.key();
const auto &id = i.value();
if (const auto existingItemIndex = iconThemeComboBox->findData(id); existingItemIndex != -1) {
iconThemeComboBox->setItemText(existingItemIndex, displayName);
} else {
iconThemeComboBox->addItem(displayName, id);
}
}
@ -352,20 +562,29 @@ QWidget *QtLanguageOptionPage::setupWidget()
{
// call base implementation first, so ui() is available
auto *widget = QtLanguageOptionPageBase::setupWidget();
if (m_settings.retranslatable) {
ui()->label->hide();
}
// add all available locales to combo box
auto *localeComboBox = ui()->localeComboBox;
for (const QLocale &locale : QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry)) {
const auto locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
for (const QLocale &locale : locales) {
localeComboBox->addItem(locale.name());
}
auto *languageLabel = ui()->languageLabel;
QObject::connect(ui()->localeComboBox, &QComboBox::currentTextChanged, [languageLabel, localeComboBox] {
const QLocale selectedLocale(localeComboBox->currentText());
const QLocale currentLocale;
QObject::connect(ui()->localeComboBox, &QComboBox::currentTextChanged, languageLabel, [languageLabel, localeComboBox] {
const auto selectedLocale = QLocale(localeComboBox->currentText());
const auto currentLocale = QLocale();
const auto territory =
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
currentLocale.territoryToString(selectedLocale.territory());
#else
currentLocale.countryToString(selectedLocale.country());
#endif
languageLabel->setText(QCoreApplication::translate("QtGui::QtLanguageOptionPage", "recognized by Qt as") % QStringLiteral(" <i>")
% currentLocale.languageToString(selectedLocale.language()) % QChar(',') % QChar(' ')
% currentLocale.countryToString(selectedLocale.country()) % QStringLiteral("</i>"));
% currentLocale.languageToString(selectedLocale.language()) % QChar(',') % QChar(' ') % territory % QStringLiteral("</i>"));
});
return widget;
}
@ -394,6 +613,24 @@ void QtEnvOptionPage::reset()
ui()->iconThemeSearchPathSelection->lineEdit()->setText(m_settings.additionalIconThemeSearchPath);
ui()->translationPathSelection->lineEdit()->setText(TranslationFiles::additionalTranslationFilePath());
}
QWidget *QtEnvOptionPage::setupWidget()
{
// call base implementation first, so ui() is available
return QtEnvOptionPageBase::setupWidget();
}
/*!
* \brief Returns a handle to the internal data.
* \remarks
* This is an opaque data structure. It can be used to construct option pages
* like QtLanguageOptionPage.
*/
QtSettings::operator QtSettingsData &() const
{
return *m_d.get();
}
} // namespace QtUtilities
INSTANTIATE_UI_FILE_BASED_OPTION_PAGE(QtAppearanceOptionPage)

View File

@ -19,7 +19,7 @@ explicit QtAppearanceOptionPage(QtSettingsData &settings, QWidget *parentWidget
private:
DECLARE_SETUP_WIDGETS
QtSettingsData &m_settings;
QtSettingsData & m_settings;
QFontDialog *m_fontDialog;
END_DECLARE_OPTION_PAGE
@ -29,7 +29,7 @@ explicit QtLanguageOptionPage(QtSettingsData &settings, QWidget *parentWidget =
private:
DECLARE_SETUP_WIDGETS
QtSettingsData &m_settings;
QtSettingsData & m_settings;
END_DECLARE_OPTION_PAGE
BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(QtEnvOptionPage)
@ -37,7 +37,8 @@ public:
explicit QtEnvOptionPage(QtSettingsData &settings, QWidget *parentWidget = nullptr);
private:
QtSettingsData &m_settings;
DECLARE_SETUP_WIDGETS
QtSettingsData & m_settings;
END_DECLARE_OPTION_PAGE
class QT_UTILITIES_EXPORT QtSettings {
@ -45,10 +46,17 @@ public:
QtSettings();
~QtSettings();
void disableNotices();
void setRetranslatable(bool retranslatable);
void restore(QSettings &settings);
void save(QSettings &settings) const;
void apply();
void reapplyDefaultIconTheme(bool isPaletteDark);
void reevaluatePaletteAndDefaultIconTheme();
bool isPaletteDark();
bool hasCustomFont() const;
bool hasLocaleChanged() const;
operator QtSettingsData &() const;
OptionCategory *category();

View File

@ -37,7 +37,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
{
m_ui->setupUi(this);
makeHeading(m_ui->headingLabel);
setStyleSheet(dialogStyle());
setStyleSheet(dialogStyleForPalette(palette()));
// setup models
m_categoryFilterModel->setSourceModel(m_categoryModel);
@ -155,6 +155,20 @@ void SettingsDialog::showCategory(OptionCategory *category)
updateTabWidget();
}
/*!
* \brief Allows to set the \a categories display name so that it is retranslated as needed.
* \remarks
* - The specified \a translator is supposed to return the display name to assign to \a category for the current
* locale. The \a translator is called immediately for the initial assignment and on language change events.
* - This function is experimental and might change or be removed completely in the next minor release.
*/
void SettingsDialog::translateCategory(OptionCategory *category, const std::function<QString()> &translator)
{
category->setDisplayName(translator());
connect(this, &SettingsDialog::retranslationRequired, category,
[category, translator = std::move(translator)] { category->setDisplayName(translator()); });
}
/*!
* \brief Enables *single-category mode* to show only the specified \a
* singleCategory.
@ -177,6 +191,43 @@ void SettingsDialog::setSingleCategory(OptionCategory *singleCategory)
}
}
/*!
* \brief Returns the tab-widget's corner widget.
*/
QWidget *SettingsDialog::cornerWidget(Qt::Corner corner) const
{
return m_ui->pagesTabWidget->cornerWidget(corner);
}
/*!
* \brief Sets the tab-widget's corner widget.
*/
void SettingsDialog::setCornerWidget(QWidget *widget, Qt::Corner corner)
{
m_ui->pagesTabWidget->setCornerWidget(widget, corner);
}
/*!
* \brief Adds a widget next to the heading.
*/
void SettingsDialog::addHeadingWidget(QWidget *widget)
{
m_ui->headingLayout->addWidget(widget);
}
/*!
* \brief Selects the specified page within the specified category.
*/
void SettingsDialog::selectPage(int categoryIndex, int pageIndex)
{
m_categoryFilterModel->setFilterFixedString(QString());
m_ui->filterLineEdit->clear();
showCategory(m_categoryModel->category(categoryIndex));
m_ui->categoriesListView->selectionModel()->select(
m_categoryFilterModel->mapFromSource(m_categoryModel->index(categoryIndex)), QItemSelectionModel::ClearAndSelect);
m_ui->pagesTabWidget->setCurrentIndex(pageIndex);
}
/*!
* \brief Updates the tab widget to show the pages for the current category.
*/
@ -187,29 +238,35 @@ void SettingsDialog::updateTabWidget()
return;
}
m_ui->pagesTabWidget->setUpdatesEnabled(false);
const QString searchKeyWord = m_ui->filterLineEdit->text();
const auto searchKeyWord = m_ui->filterLineEdit->text();
int index = 0, pageIndex = 0;
for (OptionPage *const page : m_currentCategory->pages()) {
if (page->matches(searchKeyWord)) {
// ensure the page's widget has no parent anymore; otherwise windowIcon() might return the parent's icon
auto *const widget = page->widget();
widget->setParent(nullptr);
// add the widget to the tab widget within a scroll area
QScrollArea *scrollArea;
if (index < m_ui->pagesTabWidget->count()) {
scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
scrollArea->takeWidget();
m_ui->pagesTabWidget->setTabText(index, page->widget()->windowTitle());
m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon());
m_ui->pagesTabWidget->setTabText(index, widget->windowTitle());
m_ui->pagesTabWidget->setTabIcon(index, widget->windowIcon());
} else {
scrollArea = new QScrollArea(m_ui->pagesTabWidget);
scrollArea->setFrameStyle(QFrame::NoFrame);
scrollArea->setBackgroundRole(QPalette::Base);
scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
scrollArea->setWidgetResizable(true);
m_ui->pagesTabWidget->addTab(scrollArea, page->widget()->windowTitle());
m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon());
m_ui->pagesTabWidget->addTab(scrollArea, widget->windowTitle());
m_ui->pagesTabWidget->setTabIcon(index, widget->windowIcon());
}
if (page->widget()->layout()) {
page->widget()->layout()->setAlignment(Qt::AlignTop | Qt::AlignLeft);
if (auto *const layout = widget->layout()) {
layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
}
scrollArea->setWidget(page->widget());
scrollArea->setWidget(widget);
++index;
}
if (pageIndex == m_currentCategory->currentIndex()) {
@ -217,16 +274,31 @@ void SettingsDialog::updateTabWidget()
}
++pageIndex;
}
// remove surplus tabs
while (index < m_ui->pagesTabWidget->count()) {
QScrollArea *const scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
auto *const scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
scrollArea->takeWidget();
m_ui->pagesTabWidget->removeTab(index);
delete scrollArea;
}
m_ui->pagesTabWidget->tabBar()->setHidden(!m_tabBarAlwaysVisible && m_ui->pagesTabWidget->count() == 1);
m_ui->pagesTabWidget->setUpdatesEnabled(true);
}
/*!
* \brief Updates the tab widget's tab texts to apply possible translation changes.
*/
void SettingsDialog::retranslateTabWidget()
{
for (auto index = 0; index < m_ui->pagesTabWidget->count(); ++index) {
const auto *const scrollArea = qobject_cast<const QScrollArea *>(m_ui->pagesTabWidget->widget(index));
const auto *const widget = scrollArea->widget();
m_ui->pagesTabWidget->setTabText(index, widget->windowTitle());
}
}
/*!
* \brief Applies all changes. Calls OptionCategory::applyAllPages() for each category.
* \remarks Pages which have not been shown yet must have not been initialized anyways
@ -283,4 +355,22 @@ void SettingsDialog::reset()
}
emit resetted();
}
bool SettingsDialog::event(QEvent *event)
{
const auto res = QDialog::event(event);
switch (event->type()) {
case QEvent::PaletteChange:
setStyleSheet(dialogStyleForPalette(palette()));
break;
case QEvent::LanguageChange:
m_ui->retranslateUi(this);
retranslateTabWidget();
emit retranslationRequired();
break;
default:;
}
return res;
}
} // namespace QtUtilities

View File

@ -31,21 +31,30 @@ public:
OptionCategory *category(int categoryIndex) const;
OptionPage *page(int categoryIndex, int pageIndex) const;
void showCategory(OptionCategory *category);
void translateCategory(OptionCategory *category, const std::function<QString()> &translator);
void setSingleCategory(OptionCategory *singleCategory);
QWidget *cornerWidget(Qt::Corner corner = Qt::TopRightCorner) const;
void setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopRightCorner);
void addHeadingWidget(QWidget *widget);
void selectPage(int categoryIndex, int pageIndex);
public Q_SLOTS:
bool apply();
void reset();
Q_SIGNALS:
void applied();
void resetted();
void retranslationRequired();
protected:
bool event(QEvent *event) override;
void showEvent(QShowEvent *event) override;
private Q_SLOTS:
void currentCategoryChanged(const QModelIndex &index);
void updateTabWidget();
bool apply();
void reset();
void retranslateTabWidget();
private:
std::unique_ptr<Ui::SettingsDialog> m_ui;

View File

@ -35,41 +35,6 @@
<property name="bottomMargin">
<number>3</number>
</property>
<item row="0" column="1">
<widget class="QLabel" name="headingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>No category selected</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QtUtilities::ClearLineEdit" name="filterLineEdit">
<property name="maximumSize">
<size>
<width>140</width>
<height>16777215</height>
</size>
</property>
<property name="placeholderText">
<string>Filter</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTabWidget" name="pagesTabWidget">
<property name="currentIndex">
@ -96,6 +61,39 @@
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="headingLayout">
<item>
<widget class="QLabel" name="headingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>No category selected</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QtUtilities::ClearLineEdit" name="filterLineEdit">
<property name="maximumSize">
<size>
<width>140</width>
<height>16777215</height>
</size>
</property>
<property name="placeholderText">
<string>Filter</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -4,8 +4,8 @@
#include "../widgets/clearspinbox.h"
#include "../widgets/iconbutton.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QPushButton>
#include <QtTest/QtTest>
using namespace QtUtilities;
@ -37,7 +37,8 @@ void ButtonOverlayTests::changeBasicConfiguration(ButtonOverlay &buttonOverlay)
{
buttonOverlay.setClearButtonEnabled(false);
QVERIFY2(!buttonOverlay.isClearButtonEnabled(), "clear button disabled");
buttonOverlay.enableInfoButton(QIcon::fromTheme(QStringLiteral("data-information")).pixmap(IconButton::defaultPixmapSize), QStringLiteral("Some info"));
buttonOverlay.enableInfoButton(
QIcon::fromTheme(QStringLiteral("data-information")).pixmap(IconButton::defaultPixmapSize), QStringLiteral("Some info"));
QVERIFY2(buttonOverlay.isInfoButtonEnabled(), "info button enabled");
}

View File

@ -2,8 +2,8 @@
#include "resources/config.h"
#include <QTest>
#include <QSignalSpy>
#include <QTest>
using namespace QtUtilities;
@ -61,19 +61,17 @@ void DBusNotificationTests::semiAutomaticTest()
auto envValueIsInt = false;
if (envValue.isEmpty() || (envValue.toInt(&envValueIsInt) == 0 && envValueIsInt)) {
QSKIP("Set the environment variable " PROJECT_VARNAME_UPPER "_ENABLE_SEMI_AUTOMATIC_NOTIFICATION_TESTS to run "
"the semi-automatic D-Bus notification test.");
"the semi-automatic D-Bus notification test.");
}
QVERIFY2(DBusNotification::isAvailable(), "D-Bus notifications are available");
DBusNotification n(QStringLiteral("Semi-automatic test"), NotificationIcon::Information, 10000);
QString clickedAction, error;
const auto actionConnection = connect(&n, &DBusNotification::actionInvoked, [&clickedAction] (const QString &actionName) {
clickedAction = actionName;
});
const auto errorConnection = connect(&n, &DBusNotification::error, [&error] () {
error = QStringLiteral("error occurred (TODO: pass an error message here)");
});
const auto actionConnection
= connect(&n, &DBusNotification::actionInvoked, [&clickedAction](const QString &actionName) { clickedAction = actionName; });
const auto errorConnection
= connect(&n, &DBusNotification::error, [&error]() { error = QStringLiteral("error occurred (TODO: pass an error message here)"); });
n.setApplicationName(QStringLiteral(APP_NAME " tests; " APP_VERSION));
n.show(QStringLiteral("Some message; will append more lines later"));
for (auto i = 1; i <= 10; ++i) {
@ -83,7 +81,7 @@ void DBusNotificationTests::semiAutomaticTest()
QCOMPARE(error, QString());
n.setImage(QIcon::fromTheme(QStringLiteral("document-open")).pixmap(64).toImage());
n.setTitle(n.title() + QStringLiteral(" - click action to continue"));
n.setActions(QStringList({QStringLiteral("fail"), QStringLiteral("Let test fail"), QStringLiteral("pass"), QStringLiteral("Let test pass")}));
n.setActions(QStringList({ QStringLiteral("fail"), QStringLiteral("Let test fail"), QStringLiteral("pass"), QStringLiteral("Let test pass") }));
QSignalSpy actionInvokedSpy(&n, &DBusNotification::actionInvoked);
n.update(QStringLiteral("Click on \"Let test pass\" to continue within 10 seconds"));
actionInvokedSpy.wait(10000);

View File

@ -1,7 +1,7 @@
#include "../settingsdialog/settingsdialog.h"
#include "../settingsdialog/optioncategory.h"
#include "../settingsdialog/optioncategorymodel.h"
#include "../settingsdialog/qtsettings.h"
#include "../settingsdialog/settingsdialog.h"
#include <QtTest/QtTest>
@ -22,12 +22,14 @@ void DialogsTests::testSettingsDialog()
settingsDlg.setSingleCategory(qtSettings.category());
// add another empty category
auto testCategory = OptionCategory();
testCategory.setDisplayName(QStringLiteral("Test category"));
testCategory.setIcon(QIcon::fromTheme(QStringLiteral("preferences")));
auto *const testCategory = new OptionCategory();
testCategory->setDisplayName(QStringLiteral("Test category"));
testCategory->setIcon(QIcon::fromTheme(QStringLiteral("preferences")));
settingsDlg.showCategory(nullptr); // ensure no current category is shown anymore
settingsDlg.setSingleCategory(nullptr);
settingsDlg.categoryModel()->setCategories(QList<OptionCategory *>({&testCategory, qtSettings.category()}));
settingsDlg.showCategory(qtSettings.category());
auto *const qtCategory = qtSettings.category();
settingsDlg.categoryModel()->setCategories(QList<OptionCategory *>({ testCategory, qtCategory }));
settingsDlg.showCategory(qtCategory);
settingsDlg.show();
}

View File

@ -9,10 +9,18 @@
<translation>Text löschen</translation>
</message>
</context>
<context>
<name>QtConfigArguments</name>
<message>
<location filename="../resources/qtconfigarguments.cpp" line="193"/>
<source>This application requires Windows 10, version 1809 or newer. The current Windows version is older so the application might not work correctly.</source>
<translation>Diese Anwendung benötigt mindestens Windows 10, version 1809 oder neuer. Die momentane Windows-Version ist älter. Deshalb wird die Anwendung möglicherweise nicht richtig funktionieren.</translation>
</message>
</context>
<context>
<name>QtGui::QtLanguageOptionPage</name>
<message>
<location filename="../settingsdialog/qtsettings.cpp" line="366"/>
<location filename="../settingsdialog/qtsettings.cpp" line="586"/>
<source>recognized by Qt as</source>
<translation>von Qt erkannt als</translation>
</message>
@ -20,15 +28,39 @@
<context>
<name>QtGui::QtOptionCategory</name>
<message>
<location filename="../settingsdialog/qtsettings.cpp" line="214"/>
<location filename="../settingsdialog/qtsettings.cpp" line="440"/>
<source>Qt</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities</name>
<message>
<location filename="../resources/resources.cpp" line="403"/>
<source>unable to access file</source>
<translation>Zugriff auf die Datei ist nicht möglich</translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="406"/>
<source>file has invalid format</source>
<translation>Datei hat ungültiges Format</translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="409"/>
<source>unknown error</source>
<translation>unbekannter Fehler</translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="411"/>
<source>Unable to sync settings from &quot;%1&quot;: %2</source>
<translation>Settings können nicht unter &quot;%1&quot; synchronisiert werden: %2</translation>
</message>
</context>
<context>
<name>QtUtilities::AboutDialog</name>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="24"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="139"/>
<source>About</source>
<translation>Über</translation>
</message>
@ -67,22 +99,22 @@
<translation type="vanished">verwendet Qt</translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="61"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="62"/>
<source>developed by %1</source>
<translation>entwickelt von %1</translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="71"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="75"/>
<source>Linked against:</source>
<translation>Gegen folgende Bibliotheken gelinkt:</translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="75"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="80"/>
<source>For updates and bug reports visit the &lt;a href=&quot;%1&quot; style=&quot;text-decoration: underline; color: palette(link);&quot;&gt;project website&lt;/a&gt;.</source>
<translation>Für Aktualisierung und Melden von Fehlern besuche die &lt;a href=&quot;%1&quot; style=&quot;text-decoration: underline; color: palette(link);&quot;&gt;Webseite des Projekts&lt;/a&gt;.</translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="91"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="99"/>
<source>Using &lt;a href=&quot;qtversion&quot;&gt;Qt %1&lt;/a&gt;</source>
<translation>Verwendet &lt;a href=&quot;qtversion&quot;&gt;Qt %1&lt;/a&gt;</translation>
</message>
@ -92,8 +124,8 @@
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="12"/>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="51"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="183"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="198"/>
<source>Enter the password</source>
<translation>Passwort eingeben</translation>
</message>
@ -138,28 +170,28 @@
<translation>Bestätigen</translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="183"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="198"/>
<source>Enter the new password</source>
<translation>Neues Passwort festlegen</translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="290"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="289"/>
<source>You didn&apos;t enter a user name.</source>
<translation>Es wurde kein Benutzername eingegeben.</translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="292"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="291"/>
<source>You didn&apos;t enter a password.</source>
<translation>Es wurde kein Passwort eingegeben.</translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="297"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="296"/>
<source>You have to enter the new password twice to ensure you enterd it correct.</source>
<translation>Um sicher zu stellen, dass das neue Passwort richtig eingegeben wurde, muss es zweimal eingegeben werden.</translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="300"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="299"/>
<source>You mistyped the password.</source>
<translation>Erstes und zweites Passwort stimmen nicht überein.</translation>
</message>
@ -191,26 +223,76 @@
<source>Quick</source>
<translation>Einfach</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="46"/>
<source>Save</source>
<translation>Speichern</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="48"/>
<source>Load</source>
<translation>Laden</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="155"/>
<source>Color palette configuration (*.ini)</source>
<translation>Farbpalettenkonfiguration (*.ini)</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="162"/>
<source>Unable to load &quot;%1&quot;.</source>
<translation>Fehler beim Laden von &quot;%1&quot;.</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="167"/>
<source>&quot;%1&quot; does not contain a valid palette.</source>
<translation>&quot;%1&quot; enthält keine gültige Farbpalette.</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="180"/>
<source>Unable to write &quot;%1&quot;.</source>
<translation>Fehler beim Schreiben von &quot;%1&quot;.</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="188"/>
<source>Load palette</source>
<translation>Palette laden</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="200"/>
<source>Error reading palette</source>
<translation>Fehler beim Einlesen der Palette</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="206"/>
<source>Save palette</source>
<translation>Palette speichern</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="214"/>
<source>Error writing palette</source>
<translation>Fehler beim Schreiben der Palette</translation>
</message>
</context>
<context>
<name>QtUtilities::PaletteModel</name>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="359"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="416"/>
<source>Color Role</source>
<translation>Farbrolle</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="361"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="418"/>
<source>Active</source>
<translation>Aktiv</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="363"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="420"/>
<source>Inactive</source>
<translation>Inaktiv</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="365"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="422"/>
<source>Disabled</source>
<translation>Deaktiviert</translation>
</message>
@ -218,24 +300,24 @@
<context>
<name>QtUtilities::PathSelection</name>
<message>
<location filename="../widgets/pathselection.cpp" line="56"/>
<location filename="../widgets/pathselection.cpp" line="76"/>
<location filename="../widgets/pathselection.cpp" line="87"/>
<location filename="../widgets/pathselection.cpp" line="144"/>
<source>Select ...</source>
<translation>Wählen ...</translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="81"/>
<location filename="../widgets/pathselection.cpp" line="92"/>
<source>Open</source>
<translation>Öffnen</translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="84"/>
<location filename="../widgets/pathselection.cpp" line="95"/>
<source>Explore</source>
<translation>Im Dateibrowser öffnen</translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="120"/>
<location filename="../widgets/pathselection.cpp" line="122"/>
<location filename="../widgets/pathselection.cpp" line="131"/>
<location filename="../widgets/pathselection.cpp" line="133"/>
<source>Select path</source>
<translation>Pfad auswählen</translation>
</message>
@ -399,38 +481,38 @@ Außerdem werden sie vielleicht vom QPA plugin überschrieben und funktionieren
<translation>Einstellungen</translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="53"/>
<location filename="../settingsdialog/settingsdialog.ui" line="75"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="153"/>
<source>No category selected</source>
<translation>Keine Kategorie gewählt</translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="69"/>
<location filename="../settingsdialog/settingsdialog.ui" line="93"/>
<source>Filter</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="136"/>
<location filename="../settingsdialog/settingsdialog.ui" line="134"/>
<source>Abort</source>
<translation>Abbrechen</translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="150"/>
<location filename="../settingsdialog/settingsdialog.ui" line="148"/>
<source>Apply</source>
<translation>Anwenden</translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="161"/>
<location filename="../settingsdialog/settingsdialog.ui" line="159"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.cpp" line="248"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="320"/>
<source>&lt;p&gt;&lt;b&gt;Errors occurred when applying changes:&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;</source>
<translation>&lt;p&gt;&lt;b&gt;Beim Anwenden der Einstellungen sind Fehler aufgetreten:&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;</translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.cpp" line="253"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="325"/>
<source>unknown error</source>
<translation>unbekannter Fehler</translation>
</message>
@ -442,22 +524,22 @@ Außerdem werden sie vielleicht vom QPA plugin überschrieben und funktionieren
<context>
<name>Utilities::windowTitle</name>
<message>
<location filename="../misc/dialogutils.cpp" line="34"/>
<location filename="../misc/dialogutils.cpp" line="36"/>
<source>Unsaved - %1</source>
<translation>Nicht gespeichert - %1</translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="37"/>
<location filename="../misc/dialogutils.cpp" line="39"/>
<source>%1 - %2 - %3</source>
<translation></translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="42"/>
<location filename="../misc/dialogutils.cpp" line="44"/>
<source>*Unsaved - %1</source>
<translation>*Nicht gespeichert - %1</translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="45"/>
<location filename="../misc/dialogutils.cpp" line="47"/>
<source>*%1 - %2 - %3</source>
<translation></translation>
</message>

View File

@ -9,10 +9,18 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtConfigArguments</name>
<message>
<location filename="../resources/qtconfigarguments.cpp" line="193"/>
<source>This application requires Windows 10, version 1809 or newer. The current Windows version is older so the application might not work correctly.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::QtLanguageOptionPage</name>
<message>
<location filename="../settingsdialog/qtsettings.cpp" line="366"/>
<location filename="../settingsdialog/qtsettings.cpp" line="586"/>
<source>recognized by Qt as</source>
<translation type="unfinished"></translation>
</message>
@ -20,15 +28,39 @@
<context>
<name>QtGui::QtOptionCategory</name>
<message>
<location filename="../settingsdialog/qtsettings.cpp" line="214"/>
<location filename="../settingsdialog/qtsettings.cpp" line="440"/>
<source>Qt</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtUtilities</name>
<message>
<location filename="../resources/resources.cpp" line="403"/>
<source>unable to access file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="406"/>
<source>file has invalid format</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="409"/>
<source>unknown error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="411"/>
<source>Unable to sync settings from &quot;%1&quot;: %2</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtUtilities::AboutDialog</name>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="24"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="139"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
@ -63,22 +95,22 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="61"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="62"/>
<source>developed by %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="71"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="75"/>
<source>Linked against:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="75"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="80"/>
<source>For updates and bug reports visit the &lt;a href=&quot;%1&quot; style=&quot;text-decoration: underline; color: palette(link);&quot;&gt;project website&lt;/a&gt;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="91"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="99"/>
<source>Using &lt;a href=&quot;qtversion&quot;&gt;Qt %1&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
@ -88,8 +120,8 @@
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="12"/>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="51"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="183"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="198"/>
<source>Enter the password</source>
<translation type="unfinished"></translation>
</message>
@ -134,28 +166,28 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="183"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="198"/>
<source>Enter the new password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="290"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="289"/>
<source>You didn&apos;t enter a user name.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="292"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="291"/>
<source>You didn&apos;t enter a password.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="297"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="296"/>
<source>You have to enter the new password twice to ensure you enterd it correct.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="300"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="299"/>
<source>You mistyped the password.</source>
<translation type="unfinished"></translation>
</message>
@ -187,26 +219,76 @@
<source>Quick</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="46"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="48"/>
<source>Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="155"/>
<source>Color palette configuration (*.ini)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="162"/>
<source>Unable to load &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="167"/>
<source>&quot;%1&quot; does not contain a valid palette.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="180"/>
<source>Unable to write &quot;%1&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="188"/>
<source>Load palette</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="200"/>
<source>Error reading palette</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="206"/>
<source>Save palette</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="214"/>
<source>Error writing palette</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtUtilities::PaletteModel</name>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="359"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="416"/>
<source>Color Role</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="361"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="418"/>
<source>Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="363"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="420"/>
<source>Inactive</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="365"/>
<location filename="../paletteeditor/paletteeditor.cpp" line="422"/>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
@ -214,24 +296,24 @@
<context>
<name>QtUtilities::PathSelection</name>
<message>
<location filename="../widgets/pathselection.cpp" line="56"/>
<location filename="../widgets/pathselection.cpp" line="76"/>
<location filename="../widgets/pathselection.cpp" line="87"/>
<location filename="../widgets/pathselection.cpp" line="144"/>
<source>Select ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="81"/>
<location filename="../widgets/pathselection.cpp" line="92"/>
<source>Open</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="84"/>
<location filename="../widgets/pathselection.cpp" line="95"/>
<source>Explore</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="120"/>
<location filename="../widgets/pathselection.cpp" line="122"/>
<location filename="../widgets/pathselection.cpp" line="131"/>
<location filename="../widgets/pathselection.cpp" line="133"/>
<source>Select path</source>
<translation type="unfinished"></translation>
</message>
@ -382,38 +464,38 @@ These settings might be overwritten by your Qt platform integration plugin and h
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="53"/>
<location filename="../settingsdialog/settingsdialog.ui" line="75"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="153"/>
<source>No category selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="69"/>
<location filename="../settingsdialog/settingsdialog.ui" line="93"/>
<source>Filter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="136"/>
<location filename="../settingsdialog/settingsdialog.ui" line="134"/>
<source>Abort</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="150"/>
<location filename="../settingsdialog/settingsdialog.ui" line="148"/>
<source>Apply</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="161"/>
<location filename="../settingsdialog/settingsdialog.ui" line="159"/>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.cpp" line="248"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="320"/>
<source>&lt;p&gt;&lt;b&gt;Errors occurred when applying changes:&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.cpp" line="253"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="325"/>
<source>unknown error</source>
<translation type="unfinished"></translation>
</message>
@ -421,22 +503,22 @@ These settings might be overwritten by your Qt platform integration plugin and h
<context>
<name>Utilities::windowTitle</name>
<message>
<location filename="../misc/dialogutils.cpp" line="34"/>
<location filename="../misc/dialogutils.cpp" line="36"/>
<source>Unsaved - %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="37"/>
<location filename="../misc/dialogutils.cpp" line="39"/>
<source>%1 - %2 - %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="42"/>
<location filename="../misc/dialogutils.cpp" line="44"/>
<source>*Unsaved - %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="45"/>
<location filename="../misc/dialogutils.cpp" line="47"/>
<source>*%1 - %2 - %3</source>
<translation type="unfinished"></translation>
</message>

View File

@ -0,0 +1,527 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>QObject</name>
<message>
<location filename="../widgets/buttonoverlay.cpp" line="143"/>
<source>Clear</source>
<translation></translation>
</message>
</context>
<context>
<name>QtConfigArguments</name>
<message>
<location filename="../resources/qtconfigarguments.cpp" line="193"/>
<source>This application requires Windows 10, version 1809 or newer. The current Windows version is older so the application might not work correctly.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::QtLanguageOptionPage</name>
<message>
<location filename="../settingsdialog/qtsettings.cpp" line="586"/>
<source>recognized by Qt as</source>
<translation> Qt </translation>
</message>
</context>
<context>
<name>QtGui::QtOptionCategory</name>
<message>
<location filename="../settingsdialog/qtsettings.cpp" line="440"/>
<source>Qt</source>
<translation>Qt</translation>
</message>
</context>
<context>
<name>QtUtilities</name>
<message>
<location filename="../resources/resources.cpp" line="403"/>
<source>unable to access file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="406"/>
<source>file has invalid format</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="409"/>
<source>unknown error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/resources.cpp" line="411"/>
<source>Unable to sync settings from &quot;%1&quot;: %2</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtUtilities::AboutDialog</name>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="24"/>
<location filename="../aboutdialog/aboutdialog.cpp" line="139"/>
<source>About</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="127"/>
<source>application name</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="137"/>
<source>version</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="163"/>
<source>description</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="185"/>
<source>website link</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="226"/>
<source>creators</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.ui" line="253"/>
<source>Using Qt</source>
<translation>使 Qt</translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="62"/>
<source>developed by %1</source>
<translation> %1 </translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="75"/>
<source>Linked against:</source>
<translation></translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="80"/>
<source>For updates and bug reports visit the &lt;a href=&quot;%1&quot; style=&quot;text-decoration: underline; color: palette(link);&quot;&gt;project website&lt;/a&gt;.</source>
<translation>访&lt;a href=&quot;%1&quot; style=&quot;text-decoration: underline; color: Palette(link);&quot;&gt;&lt;/a&gt;</translation>
</message>
<message>
<location filename="../aboutdialog/aboutdialog.cpp" line="99"/>
<source>Using &lt;a href=&quot;qtversion&quot;&gt;Qt %1&lt;/a&gt;</source>
<translation>使 &lt;a href=&quot;qtversion&quot;&gt;Qt %1&lt;/a&gt;</translation>
</message>
</context>
<context>
<name>QtUtilities::EnterPasswordDialog</name>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="12"/>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="51"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="183"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="198"/>
<source>Enter the password</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="64"/>
<source>user name</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="86"/>
<source>password</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="102"/>
<source>repeat password</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="130"/>
<source>show password</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="137"/>
<source>don&apos;t use a password</source>
<translation>使</translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="213"/>
<source>Capslock is active</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="239"/>
<source>Abort</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.ui" line="254"/>
<source>Confirm</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="183"/>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="198"/>
<source>Enter the new password</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="289"/>
<source>You didn&apos;t enter a user name.</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="291"/>
<source>You didn&apos;t enter a password.</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="296"/>
<source>You have to enter the new password twice to ensure you enterd it correct.</source>
<translation></translation>
</message>
<message>
<location filename="../enterpassworddialog/enterpassworddialog.cpp" line="299"/>
<source>You mistyped the password.</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::PaletteEditor</name>
<message>
<location filename="../paletteeditor/paletteeditor.ui" line="20"/>
<source>Edit Palette</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.ui" line="53"/>
<source>Tune Palette</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.ui" line="84"/>
<source>Show Detai&amp;ls</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.ui" line="91"/>
<source>&amp;Compute Details</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.ui" line="101"/>
<source>Quick</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="46"/>
<source>Save</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="48"/>
<source>Load</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="155"/>
<source>Color palette configuration (*.ini)</source>
<translation> (*.ini)</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="162"/>
<source>Unable to load &quot;%1&quot;.</source>
<translation> &quot;%1&quot;</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="167"/>
<source>&quot;%1&quot; does not contain a valid palette.</source>
<translation>&quot;%1&quot; </translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="180"/>
<source>Unable to write &quot;%1&quot;.</source>
<translation> &quot;%1&quot;.</translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="188"/>
<source>Load palette</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="200"/>
<source>Error reading palette</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="206"/>
<source>Save palette</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="214"/>
<source>Error writing palette</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::PaletteModel</name>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="416"/>
<source>Color Role</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="418"/>
<source>Active</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="420"/>
<source>Inactive</source>
<translation></translation>
</message>
<message>
<location filename="../paletteeditor/paletteeditor.cpp" line="422"/>
<source>Disabled</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::PathSelection</name>
<message>
<location filename="../widgets/pathselection.cpp" line="87"/>
<location filename="../widgets/pathselection.cpp" line="144"/>
<source>Select ...</source>
<translation> ...</translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="92"/>
<source>Open</source>
<translation></translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="95"/>
<source>Explore</source>
<translation></translation>
</message>
<message>
<location filename="../widgets/pathselection.cpp" line="131"/>
<location filename="../widgets/pathselection.cpp" line="133"/>
<source>Select path</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::QtAppearanceOptionPage</name>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="6"/>
<source>Appearance</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="21"/>
<source>Use system default</source>
<translation>使</translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="34"/>
<source>Font</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="71"/>
<source>More options ...</source>
<translation> ...</translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="93"/>
<source>Widget style</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="132"/>
<source>Style sheet</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="162"/>
<source>Palette</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="175"/>
<source>select ...</source>
<translation> ...</translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="195"/>
<source>Icon theme</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtappearanceoptionpage.ui" line="249"/>
<source>These settings take effect after restarting the application.
These settings might be overwritten by your Qt platform integration plugin and hence have no effect.</source>
<translation>
Qt </translation>
</message>
</context>
<context>
<name>QtUtilities::QtEnvOptionPage</name>
<message>
<location filename="../settingsdialog/qtenvoptionpage.ui" line="14"/>
<source>Environment/paths</source>
<translation>Environment/paths</translation>
</message>
<message>
<location filename="../settingsdialog/qtenvoptionpage.ui" line="28"/>
<source>Additional plugin directory</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtenvoptionpage.ui" line="38"/>
<source>Additional icon theme search path</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtenvoptionpage.ui" line="48"/>
<source>Additional translation search path</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtenvoptionpage.ui" line="79"/>
<source>These settings take effect after restarting the application.</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::QtLanguageOptionPage</name>
<message>
<location filename="../settingsdialog/qtlanguageoptionpage.ui" line="6"/>
<source>Localization</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtlanguageoptionpage.ui" line="43"/>
<source>Locale</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/qtlanguageoptionpage.ui" line="53"/>
<source>Use system default</source>
<translation>使</translation>
</message>
<message>
<location filename="../settingsdialog/qtlanguageoptionpage.ui" line="98"/>
<source>These settings take effect after restarting the application.</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::RecentMenuManager</name>
<message>
<location filename="../misc/recentmenumanager.cpp" line="35"/>
<source>&amp;Recent</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="../misc/recentmenumanager.cpp" line="38"/>
<source>&amp;Clear list</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="../misc/recentmenumanager.cpp" line="144"/>
<source>Recently opened files - </source>
<translation> - </translation>
</message>
<message>
<location filename="../misc/recentmenumanager.cpp" line="145"/>
<source>The selected file can&apos;t be found anymore. Do you want to delete the obsolete entry from the list?</source>
<translation></translation>
</message>
<message>
<location filename="../misc/recentmenumanager.cpp" line="148"/>
<source>keep entry</source>
<translation></translation>
</message>
<message>
<location filename="../misc/recentmenumanager.cpp" line="149"/>
<source>delete entry</source>
<translation></translation>
</message>
</context>
<context>
<name>QtUtilities::SettingsDialog</name>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="14"/>
<source>Settings</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="75"/>
<location filename="../settingsdialog/settingsdialog.cpp" line="153"/>
<source>No category selected</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="93"/>
<source>Filter</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="134"/>
<source>Abort</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="148"/>
<source>Apply</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.ui" line="159"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.cpp" line="320"/>
<source>&lt;p&gt;&lt;b&gt;Errors occurred when applying changes:&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;</source>
<translation>&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;</translation>
</message>
<message>
<location filename="../settingsdialog/settingsdialog.cpp" line="325"/>
<source>unknown error</source>
<translation></translation>
</message>
</context>
<context>
<name>Utilities::windowTitle</name>
<message>
<location filename="../misc/dialogutils.cpp" line="36"/>
<source>Unsaved - %1</source>
<translation> - %1</translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="39"/>
<source>%1 - %2 - %3</source>
<translation>%1 - %2 - %3</translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="44"/>
<source>*Unsaved - %1</source>
<translation>* - %1</translation>
</message>
<message>
<location filename="../misc/dialogutils.cpp" line="47"/>
<source>*%1 - %2 - %3</source>
<translation>*%1 - %2 - %3</translation>
</message>
</context>
</TS>

View File

@ -53,7 +53,7 @@ PathSelection::PathSelection(QWidget *parent)
m_lineEdit->installEventFilter(this);
m_lineEdit->setCompleter(s_completer);
m_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_button->setText(tr("Select ..."));
setTexts();
auto *const layout = new QHBoxLayout(this);
layout->setSpacing(3);
@ -65,6 +65,17 @@ PathSelection::PathSelection(QWidget *parent)
connect(m_button, &QPushButton::clicked, this, &PathSelection::showFileDialog);
}
bool PathSelection::event(QEvent *event)
{
switch (event->type()) {
case QEvent::LanguageChange:
setTexts();
break;
default:;
}
return QWidget::event(event);
}
bool PathSelection::eventFilter(QObject *obj, QEvent *event)
{
#ifndef QT_NO_CONTEXTMENU
@ -127,4 +138,10 @@ void PathSelection::showFileDialog()
}
}
}
void PathSelection::setTexts()
{
m_button->setText(tr("Select ..."));
}
} // namespace QtUtilities

View File

@ -24,10 +24,12 @@ public:
void provideCustomFileDialog(QFileDialog *customFileDialog);
protected:
bool event(QEvent *event) override;
bool eventFilter(QObject *obj, QEvent *event) override;
private Q_SLOTS:
void showFileDialog();
void setTexts();
private:
ClearLineEdit *m_lineEdit;