diff --git a/CMakeLists.txt b/CMakeLists.txt index e39dc30..11fda9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ if(WIN32) endif() # Feature options -option(ENABLE_RECOVERY_DEVICE_SUPPORT "Enable recovery device support (requires libirecovery)" ON) set(PACKAGE_MANAGER_HINT "" CACHE STRING "Name of package manager(s) used to manage this build (e.g. paru, yay, pamac)") option(PACKAGE_MANAGER_MANAGED "Build as package manager managed version (auto updates will be handled by the package manager)" OFF) option(DEPLOY "Deploy the application (WIN32 only)" ON) @@ -26,39 +25,27 @@ if (APPLE) endif() # Platform-specific paths for libraries built from source -# if(WIN32) -# include_directories("C:/msys64/mingw64/include") -# link_directories("C:/msys64/mingw64/lib") -# set(PKG_CONFIG_EXECUTABLE "C:/msys64/mingw64/bin/pkg-config.exe") -# list(APPEND CMAKE_PREFIX_PATH "C:/lxqt") -# set(CUSTOM_LIB_PATH "C:/msys64/mingw64/lib") -# set(CUSTOM_INCLUDE_PATH "C:/msys64/mingw64/include") -# set(CUSTOM_PKGCONFIG_PATH "C:/msys64/mingw64/lib/pkgconfig") -# set(ENV{PKG_CONFIG_PATH} "${CUSTOM_PKGCONFIG_PATH};$ENV{PKG_CONFIG_PATH}") -# elseif(APPLE) -# set(CUSTOM_LIB_PATH "/usr/local/lib") -# # Remove the problematic include path that's causing conflicts -# # set(CUSTOM_INCLUDE_PATH "/usr/local/include") -# set(CUSTOM_PKGCONFIG_PATH "/usr/local/lib/pkgconfig") -# set(ENV{PKG_CONFIG_PATH} "${CUSTOM_PKGCONFIG_PATH}:$ENV{PKG_CONFIG_PATH}") -# else () -# set(CUSTOM_LIB_PATH "/usr/local/lib") -# set(CUSTOM_PKGCONFIG_PATH "/usr/local/lib/pkgconfig") -# set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CUSTOM_PKGCONFIG_PATH}") -# endif() +if(WIN32) + include_directories("C:/msys64/mingw64/include") + link_directories("C:/msys64/mingw64/lib") + set(PKG_CONFIG_EXECUTABLE "C:/msys64/mingw64/bin/pkg-config.exe") + list(APPEND CMAKE_PREFIX_PATH "C:/lxqt") + set(CUSTOM_LIB_PATH "C:/msys64/mingw64/lib") + set(CUSTOM_INCLUDE_PATH "C:/msys64/mingw64/include") + set(CUSTOM_PKGCONFIG_PATH "C:/msys64/mingw64/lib/pkgconfig") + set(ENV{PKG_CONFIG_PATH} "${CUSTOM_PKGCONFIG_PATH};$ENV{PKG_CONFIG_PATH}") +elseif(APPLE) + set(CUSTOM_LIB_PATH "/usr/local/lib") + # Remove the problematic include path that's causing conflicts + # set(CUSTOM_INCLUDE_PATH "/usr/local/include") + set(CUSTOM_PKGCONFIG_PATH "/usr/local/lib/pkgconfig") + set(ENV{PKG_CONFIG_PATH} "${CUSTOM_PKGCONFIG_PATH}:$ENV{PKG_CONFIG_PATH}") +else () + set(CUSTOM_LIB_PATH "/usr/local/lib") + set(CUSTOM_PKGCONFIG_PATH "/usr/local/lib/pkgconfig") + set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CUSTOM_PKGCONFIG_PATH}") +endif() -# foreach(_ IN LISTS CMAKE_PREFIX_PATH) -# list(APPEND _qt_pkg_dirs -# "${_}/lib/pkgconfig" -# "${_}/lib64/pkgconfig" -# "${_}/lib64/qt/pkgconfig" -# "${_}/lib/qt/pkgconfig" -# ) -# endforeach() - - -# list(APPEND _qt_pkg_dirs ${CUSTOM_PKGCONFIG_PATH}) - find_package(PkgConfig REQUIRED) find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia MultimediaWidgets Network QuickControls2 SerialPort Positioning Location QuickWidgets) find_package(SQLite3 REQUIRED) @@ -110,6 +97,14 @@ add_custom_command( COMMENT "Building idevice-rs FFI libraryy" VERBATIM ) +elseif(WIN32) +add_custom_command( + OUTPUT ${IDEVICE_RS_LIB_PATH} + COMMAND ${CARGO_EXECUTABLE} +stable-x86_64-pc-windows-gnu build --manifest-path ${IDEVICE_RS_SOURCE_DIR}/Cargo.toml + WORKING_DIRECTORY ${IDEVICE_RS_SOURCE_DIR} + COMMENT "Building idevice-rs FFI libraryy" + VERBATIM +) else() add_custom_command( OUTPUT ${IDEVICE_RS_LIB_PATH} @@ -150,6 +145,11 @@ file(GLOB PLIST_CPP_SOURCES add_library(idevice_cpp STATIC ${IDEVICE_CPP_SOURCES} ${PLIST_CPP_SOURCES}) add_dependencies(idevice_cpp idevice_rs_build) +if(WIN32) +# Define LIBPLIST_STATIC to prevent functions from being marked as DLL imports +target_compile_definitions(idevice_cpp PUBLIC LIBPLIST_STATIC) +endif() + target_include_directories(idevice_cpp PUBLIC ${IDEVICE_CPP_INCLUDE_DIR} ${PLIST_CPP_INCLUDE_DIR} @@ -201,21 +201,6 @@ pkg_check_modules(AVCODEC REQUIRED IMPORTED_TARGET libavcodec) pkg_check_modules(AVUTIL REQUIRED IMPORTED_TARGET libavutil) pkg_check_modules(SWSCALE REQUIRED IMPORTED_TARGET libswscale) -# if(ENABLE_RECOVERY_DEVICE_SUPPORT) -# find_library(IRECOVERY_LIBRARY -# NAMES irecovery-1.0 -# PATHS ${CUSTOM_LIB_PATH} -# NO_DEFAULT_PATH -# ) -# if(IRECOVERY_LIBRARY) -# message(STATUS "Building with recovery device support enabled") -# else() -# message(WARNING "libirecovery not found. Recovery device support will be disabled. This is to be expected if you are installing from Arch AUR.") -# set(ENABLE_RECOVERY_DEVICE_SUPPORT OFF) -# endif() -# else() -# message(STATUS "Recovery device support disabled") -# endif() # Add libssh for SSH connections pkg_check_modules(SSH REQUIRED IMPORTED_TARGET libssh) @@ -252,8 +237,10 @@ elseif (WIN32) src/core/services/dnssd/dnssd_service.h ) - file(GLOB WINDOWS_PLATFORM_SOURCES src/platform/windows/*.cpp src/platform/windows/*.h) + file(GLOB WINDOWS_PLATFORM_SOURCES src/platform/windows/*.cpp src/platform/windows/*.h src/platform/windows/widgets/*.cpp src/platform/windows/widgets/*.h) list(APPEND PROJECT_SOURCES ${WINDOWS_PLATFORM_SOURCES}) + list(APPEND PROJECT_SOURCES + resources.win.qrc) else() list(APPEND PROJECT_SOURCES src/core/services/avahi/avahi_service.cpp @@ -261,17 +248,6 @@ else() ) endif() -if (NOT ENABLE_RECOVERY_DEVICE_SUPPORT) - list(REMOVE_ITEM PROJECT_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/src/recoverydeviceinfowidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/recoverydeviceinfowidget.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/core/helpers/parse_recovery_mode.cpp - src/recoverydeviceinfowidget.cpp - src/recoverydeviceinfowidget.h - src/core/helpers/parse_recovery_mode.cpp - ) -endif() - add_subdirectory(lib/uxplay) add_subdirectory(lib/ipatool-go) add_subdirectory(lib/zupdater) @@ -323,14 +299,8 @@ target_link_libraries(iDescriptor PRIVATE Qt6::Positioning Qt6::QuickWidgets Qt6::QuickControls2 - # ${IMOBILEDEVICE_LIBRARY} - # ${IMOBILEDEVICE_GLUE_LIBRARY} - # ${TATSU_LIBRARY} - # ${SSL_LIBRARY} - # ${CRYPTO_LIBRARY} PkgConfig::SSH ${SSH_LIBRARY} - # ${USBMUXD_LIBRARY} PkgConfig::PUGIXML # PkgConfig::USB # PkgConfig::PLIST @@ -350,11 +320,6 @@ target_link_libraries(iDescriptor PRIVATE SQLite::SQLite3 ) -# # Conditionally link libirecovery -# if(ENABLE_RECOVERY_DEVICE_SUPPORT) -# target_link_libraries(iDescriptor PRIVATE ${IRECOVERY_LIBRARY}) -# endif() - target_include_directories(iDescriptor PRIVATE # Put idevice-rs includes FIRST ${IDEVICE_CPP_INCLUDE_DIR} @@ -376,7 +341,7 @@ if(APPLE) ${CORE_SERVICES_FRAMEWORK}) message(STATUS "Using macOS Bonjour framework for network service discovery") elseif (WIN32) - target_link_libraries(iDescriptor PRIVATE PkgConfig::LIBARCHIVE) + target_link_libraries(iDescriptor PRIVATE PkgConfig::LIBARCHIVE dwmapi ntdll) find_path(DNSSD_INCLUDE_DIR dns_sd.h HINTS ${BONJOUR_SDK}/Include ) # $<$ fixes winres compiler errors target_include_directories(iDescriptor PRIVATE @@ -406,11 +371,6 @@ target_compile_definitions(iDescriptor PRIVATE SOURCE_DIR="${CMAKE_SOURCE_DIR}" ) -# Add compile definition for recovery device support -if(ENABLE_RECOVERY_DEVICE_SUPPORT) - target_compile_definitions(iDescriptor PRIVATE ENABLE_RECOVERY_DEVICE_SUPPORT) -endif() - if(PACKAGE_MANAGER_MANAGED) target_compile_definitions(iDescriptor PRIVATE PACKAGE_MANAGER_MANAGED) message(STATUS "Building as package manager managed version, updates will be handled by the package manager") @@ -435,14 +395,8 @@ set_target_properties(iDescriptor PROPERTIES WIN32_EXECUTABLE TRUE BUILD_WITH_INSTALL_RPATH TRUE ) + if (UNIX AND NOT APPLE) - # Required on Linux to find libirecovery-1.0.so.5 at runtime - if (ENABLE_RECOVERY_DEVICE_SUPPORT) - set_target_properties(iDescriptor PROPERTIES - # Control library search order - system libs first, then /usr/local/lib - INSTALL_RPATH "/usr/lib/x86_64-linux-gnu:/usr/lib:/usr/local/lib:$ORIGIN" - ) - endif() # Add install rules for the project # include(GNUInstallDirs) diff --git a/resources.win.qrc b/resources.win.qrc new file mode 100644 index 0000000..72e0b5b --- /dev/null +++ b/resources.win.qrc @@ -0,0 +1,82 @@ + + + resources/win.dark.qcss + resources/win.light.qcss + + + resources/win/dark/CheckBox.png + resources/win/dark/CheckBoxPressed.png + resources/win/dark/ComboBox.png + resources/win/dark/ComboBoxDisabled.png + resources/win/dark/NextMonth.png + resources/win/dark/NextMonthDisabled.png + resources/win/dark/PrevMonth.png + resources/win/dark/PrevMonthDisabled.png + resources/win/dark/RadioButton.png + resources/win/dark/RadioButtonHover.png + resources/win/dark/RadioButtonPressed.png + resources/win/dark/ScrollBottom.png + resources/win/dark/ScrollBottomHover.png + resources/win/dark/ScrollBottomPressed.png + resources/win/dark/ScrollLeft.png + resources/win/dark/ScrollLeftHover.png + resources/win/dark/ScrollLeftPressed.png + resources/win/dark/ScrollRight.png + resources/win/dark/ScrollRightHover.png + resources/win/dark/ScrollRightPressed.png + resources/win/dark/ScrollTop.png + resources/win/dark/ScrollTopHover.png + resources/win/dark/ScrollTopPressed.png + resources/win/dark/SpinBoxDown.png + resources/win/dark/SpinBoxDownDisabled.png + resources/win/dark/SpinBoxUp.png + resources/win/dark/SpinBoxUpDisabled.png + resources/win/dark/ToggleSwitchDisabled.png + resources/win/dark/ToggleSwitchOff.png + resources/win/dark/ToggleSwitchOffHover.png + resources/win/dark/ToggleSwitchOffPressed.png + resources/win/dark/ToggleSwitchOn.png + resources/win/dark/ToggleSwitchOnHover.png + resources/win/dark/ToggleSwitchOnPressed.png + resources/win/dark/TreeViewClose.png + resources/win/dark/TreeViewOpen.png + + + resources/win/light/CheckBox.png + resources/win/light/CheckBoxPressed.png + resources/win/light/ComboBox.png + resources/win/light/ComboBoxDisabled.png + resources/win/light/NextMonth.png + resources/win/light/NextMonthDisabled.png + resources/win/light/PrevMonth.png + resources/win/light/PrevMonthDisabled.png + resources/win/light/RadioButton.png + resources/win/light/RadioButtonHover.png + resources/win/light/RadioButtonPressed.png + resources/win/light/ScrollBottom.png + resources/win/light/ScrollBottomHover.png + resources/win/light/ScrollBottomPressed.png + resources/win/light/ScrollLeft.png + resources/win/light/ScrollLeftHover.png + resources/win/light/ScrollLeftPressed.png + resources/win/light/ScrollRight.png + resources/win/light/ScrollRightHover.png + resources/win/light/ScrollRightPressed.png + resources/win/light/ScrollTop.png + resources/win/light/ScrollTopHover.png + resources/win/light/ScrollTopPressed.png + resources/win/light/SpinBoxDown.png + resources/win/light/SpinBoxDownDisabled.png + resources/win/light/SpinBoxUp.png + resources/win/light/SpinBoxUpDisabled.png + resources/win/light/ToggleSwitchDisabled.png + resources/win/light/ToggleSwitchOff.png + resources/win/light/ToggleSwitchOffHover.png + resources/win/light/ToggleSwitchOffPressed.png + resources/win/light/ToggleSwitchOn.png + resources/win/light/ToggleSwitchOnHover.png + resources/win/light/ToggleSwitchOnPressed.png + resources/win/light/TreeViewClose.png + resources/win/light/TreeViewOpen.png + + \ No newline at end of file diff --git a/resources/win.dark.qcss b/resources/win.dark.qcss new file mode 100644 index 0000000..28acc57 --- /dev/null +++ b/resources/win.dark.qcss @@ -0,0 +1,1126 @@ +QWidget { + background: transparent; + color: rgb(255, 255, 255); + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; +} + +/*MENU*/ +QMenuBar { + background-color: transparent; + color: white; + padding: 10px; + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; +} + +QMenuBar::item { + background-color: transparent; + padding: 10px 13px; + margin-left: 5px; + border-radius: 5px; +} + +QMenuBar::item:selected { + background-color: rgba(255, 255, 255, 20); +} + +QMenuBar::item:pressed { + background-color: rgba(255, 255, 255, 13); + color: rgba(255, 255, 255, 200); +} + +QMenu { + background-color: transparent; + padding-left: 1px; + padding-top: 1px; + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 13); +} + +QMenu::item { + background-color: transparent; + padding: 5px 15px; + border-radius: 5px; + min-width: 60px; + margin: 3px; +} + +QMenu::item:selected { + background-color: rgba(255, 255, 255, 16); +} + +QMenu::item:pressed { + background-color: rgba(255, 255, 255, 10); +} + +QMenu::right-arrow { + image: url(:/resources/win/dark/TreeViewClose.png); + min-width: 40px; + min-height: 18px; +} + +QMenuBar:disabled { + color: rgb(150, 150, 150); +} + +QMenu::item:disabled { + color: rgb(150, 150, 150); + background-color: transparent; +} + +/*PUSHBUTTON*/ +QPushButton { + background-color: rgba(255, 255, 255, 18); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 7px; + min-height: 38px; + max-height: 38px; + padding-left: 7px; + padding-right: 7px; +} + +QPushButton:hover { + background-color: rgba(255, 255, 255, 25); + border: 1px solid rgba(255, 255, 255, 10); +} + +QPushButton::pressed { + background-color: rgba(255, 255, 255, 7); + border: 1px solid rgba(255, 255, 255, 13); + color: rgba(255, 255, 255, 200); +} + +QPushButton::disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); +} + +/*RADIOBUTTON*/ +QRadioButton { + min-height: 30px; + max-height: 30px; +} + +QRadioButton::indicator { + width: 22px; + height: 22px; + border-radius: 13px; + border: 2px solid #848484; + background-color: rgba(255, 255, 255, 0); + margin-right: 5px; +} + +QRadioButton::indicator:hover { + background-color: rgba(255, 255, 255, 16); +} + +QRadioButton::indicator:pressed { + background-color: rgba(255, 255, 255, 20); + border: 2px solid #434343; + image: url(:/resources/win/dark/RadioButton.png); +} + +QRadioButton::indicator:checked { + background-color: %1; + border: 2px solid %1; + image: url(:/resources/win/dark/RadioButton.png); +} + +QRadioButton::indicator:checked:hover { + image: url(:/resources/win/dark/RadioButtonHover.png); +} + +QRadioButton::indicator:checked:pressed { + image: url(:/resources/win/dark/RadioButtonPressed.png); +} + +QRadioButton:disabled { + color: rgb(150, 150, 150); +} + +QRadioButton::indicator:disabled { + border: 2px solid #646464; + background-color: rgba(255, 255, 255, 0); +} + +/*CHECKBOX*/ +QCheckBox { + min-height: 30px; + max-height: 30px; +} + +QCheckBox::indicator { + width: 22px; + height: 22px; + border-radius: 5px; + border: 2px solid #848484; + background-color: rgba(255, 255, 255, 0); + margin-right: 5px; +} + +QCheckBox::indicator:hover { + background-color: rgba(255, 255, 255, 16); +} + +QCheckBox::indicator:pressed { + background-color: rgba(255, 255, 255, 20); + border: 2px solid #434343; +} + +QCheckBox::indicator:checked { + background-color: %1; + border: 2px solid %1; + image: url(:/CheckBox/img dark/CheckBox.png); +} + +QCheckBox::indicator:checked:pressed { + image: url(:/resources/win/dark/CheckBoxPressed.png); +} + +QCheckBox:disabled { + color: rgb(150, 150, 150); +} + +QCheckBox::indicator:disabled { + border: 2px solid #646464; + background-color: rgba(255, 255, 255, 0); +} + +/*GROUPBOX*/ +QGroupBox { + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 13); + margin-top: 30px; + background-color: rgba(255, 255, 255, 16); +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + /* padding: 7px 15px; */ + /* margin-left: 5px; */ + /* border-top-left-radius: 5px; */ + /* border-top-right-radius: 5px; */ +} + +QGroupBox::title::disabled { + color: rgb(150, 150, 150) +} + +/*TABWIDGET*/ +QTabWidget { +} + +QWidget { + border-radius: 5px; +} + +QTabWidget::pane { + border: 1px solid rgb(43, 43, 43); + border-radius: 5px; +} + +QTabWidget::tab-bar { + left: 5px; +} + +QTabBar::tab { + background-color: rgba(255, 255, 255, 0); + padding: 7px 15px; + margin-right: 2px; +} + +QTabBar::tab:hover { + background-color: rgba(255, 255, 255, 13); + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +QTabBar::tab:selected { + background-color: rgba(255, 255, 255, 16); + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +QTabBar::tab:disabled { + color: rgb(150, 150, 150) +} + +/*SPINBOX*/ +QSpinBox { + background-color: rgba(255, 255, 255, 10); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; + min-width: 100px; + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QSpinBox:hover { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QSpinBox::focus { + background-color: rgba(255, 255, 255, 5); + border: 1px solid rgba(255, 255, 255, 13); + color: rgba(255, 255, 255, 200); + border-bottom: 2px solid %1; +} + +QSpinBox::up-button { + image: url(:/resources/win/dark/SpinBoxUp.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QSpinBox::up-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QSpinBox::up-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QSpinBox::down-button { + image: url(:/resources/win/dark/SpinBoxDown.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QSpinBox::down-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QSpinBox::down-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QSpinBox::drop-down { + background-color: transparent; + width: 50px; +} + +QSpinBox:disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); + border: 1px solid rgba(255, 255, 255, 5); +} + +QSpinBox::up-button:disabled { + image: url(:/resources/win/dark/SpinBoxUpDisabled.png); +} + +QSpinBox::down-button:disabled { + image: url(:/resources/win/dark/SpinBoxDownDisabled.png); +} + +/*DOUBLESPINBOX*/ +QDoubleSpinBox { + background-color: rgba(255, 255, 255, 10); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; + min-width: 100px; + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QDoubleSpinBox:hover { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QDoubleSpinBox::focus { + background-color: rgba(255, 255, 255, 5); + border: 1px solid rgba(255, 255, 255, 13); + color: rgba(255, 255, 255, 200); + border-bottom: 2px solid %1; +} + +QDoubleSpinBox::up-button { + image: url(:/resources/win/dark/SpinBoxUp.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDoubleSpinBox::up-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QDoubleSpinBox::up-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QDoubleSpinBox::down-button { + image: url(:/resources/win/dark/SpinBoxDown.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDoubleSpinBox::down-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QDoubleSpinBox::down-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QDoubleSpinBox::drop-down { + background-color: transparent; + width: 50px; +} + +QDoubleSpinBox:disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); + border: 1px solid rgba(255, 255, 255, 5); +} + +QDoubleSpinBox::up-button:disabled { + image: url(:/resources/win/dark/SpinBoxUpDisabled.png); +} + +QDoubleSpinBox::down-button:disabled { + image: url(:/resources/win/dark/SpinBoxDownDisabled.png); +} + +/*DATETIMEEDIT*/ +QDateTimeEdit { + background-color: rgba(255, 255, 255, 10); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; + min-width: 100px; + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QDateTimeEdit:hover { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QDateTimeEdit::focus { + background-color: rgba(255, 255, 255, 5); + border: 1px solid rgba(255, 255, 255, 13); + color: rgba(255, 255, 255, 200); + border-bottom: 2px solid %1; +} + +QDateTimeEdit::up-button { + image: url(:/resources/win/dark/SpinBoxUp.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDateTimeEdit::up-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QDateTimeEdit::up-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QDateTimeEdit::down-button { + image: url(:/resources/win/dark/SpinBoxDown.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDateTimeEdit::down-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QDateTimeEdit::down-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QDateTimeEdit::drop-down { + background-color: transparent; + width: 50px; +} + +QDateTimeEdit:disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); + border: 1px solid rgba(255, 255, 255, 5); +} + +QDateTimeEdit::up-button:disabled { + image: url(:/resources/win/dark/SpinBoxUpDisabled.png); +} + +QDateTimeEdit::down-button:disabled { + image: url(:/resources/win/dark/SpinBoxDownDisabled.png); +} + +/*SLIDERVERTICAL*/ +QSlider:vertical { + min-width: 30px; + min-height: 100px; +} + +QSlider::groove:vertical { + width: 5px; + background-color: rgba(255, 255, 255, 150); + border-radius: 2px; +} + +QSlider::handle:vertical { + background-color: %1; + border: 6px solid #454545; + height: 13px; + min-width: 15px; + margin: 0px -10px; + border-radius: 12px +} + +QSlider::handle:vertical:hover { + background-color: %1; + border: 4px solid #454545; + height: 17px; + min-width: 15px; + margin: 0px -10px; + border-radius: 12px +} + +QSlider::handle:vertical:pressed { + background-color: %1; + border: 7px solid #454545; + height: 11px; + min-width: 15px; + margin: 0px -10px; + border-radius: 12px +} + +QSlider::groove:vertical:disabled { + background-color: rgba(255, 255, 255, 75); +} + +QSlider::handle:vertical:disabled { + background-color: #555555; + border: 6px solid #353535; +} + +/*SLIDERHORIZONTAL*/ +QSlider:horizontal { + min-width: 100px; + min-height: 30px; +} + +QSlider::groove:horizontal { + height: 5px; + background-color: rgba(255, 255, 255, 150); + border-radius: 2px; +} + +QSlider::handle:horizontal { + background-color: %1; + border: 6px solid #454545; + width: 13px; + min-height: 15px; + margin: -10px 0; + border-radius: 12px +} + +QSlider::handle:horizontal:hover { + background-color: %1; + border: 4px solid #454545; + width: 17px; + min-height: 15px; + margin: -10px 0; + border-radius: 12px +} + +QSlider::handle:horizontal:pressed { + background-color: %1; + border: 7px solid #454545; + width: 11px; + min-height: 15px; + margin: -10px 0; + border-radius: 12px +} + +QSlider::groove:horizontal:disabled { + background-color: rgba(255, 255, 255, 75); +} + +QSlider::handle:horizontal:disabled { + background-color: #555555; + border: 6px solid #353535; +} + +/*PROGRESSBAR*/ +QProgressBar { + background-color: qlineargradient(spread:reflect, x1:0.5, y1:0.5, x2:0.5, y2:1, stop:0.119403 rgba(255, 255, 255, 250), stop:0.273632 rgba(0, 0, 0, 0)); + border-radius: 2px; + min-height: 4px; + max-height: 4px; +} + +QProgressBar::chunk { + background-color: %1; + border-radius: 2px; +} + +/*COMBOBOX*/ +QComboBox { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; +} + +QComboBox:hover { + background-color: rgba(255, 255, 255, 20); + border: 1px solid rgba(255, 255, 255, 10); +} + +QComboBox::pressed { + background-color: rgba(255, 255, 255, 20); + border: 1px solid rgba(255, 255, 255, 13); + color: rgba(255, 255, 255, 200); +} + +QComboBox::down-arrow { + image: url(:/resources/win/dark/ComboBox.png); +} + +QComboBox::drop-down { + background-color: transparent; + min-width: 50px; +} + +QComboBox:disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); + border: 1px solid rgba(255, 255, 255, 5); +} + +QComboBox::down-arrow:disabled { + image: url(:/resources/win/dark/ComboBoxDisabled.png); +} + +/*LINEEDIT*/ +QLineEdit { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + font-size: 16px; + font-family: "Segoe UI", serif; + font-weight: 500; + border-radius: 7px; + border-bottom: 1px solid rgba(255, 255, 255, 150); + padding: 5px; +} + +QLineEdit:hover { + background-color: rgba(255, 255, 255, 20); + border: 1px solid rgba(255, 255, 255, 10); + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QLineEdit:focus { + border-bottom: 2px solid %1; + background-color: rgba(255, 255, 255, 5); + border-top: 1px solid rgba(255, 255, 255, 13); + border-left: 1px solid rgba(255, 255, 255, 13); + border-right: 1px solid rgba(255, 255, 255, 13); +} + +QLineEdit:disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); + border: 1px solid rgba(255, 255, 255, 5); +} + +/*SCROLLVERTICAL*/ +QScrollBar:vertical { + border: 6px solid rgba(255, 255, 255, 0); + margin: 14px 0px 14px 0px; + width: 16px; +} + +QScrollBar:vertical:hover { + border: 5px solid rgba(255, 255, 255, 0); +} + +QScrollBar::handle:vertical { + background-color: rgba(255, 255, 255, 130); + border-radius: 2px; + min-height: 25px; +} + +QScrollBar::sub-line:vertical { + image: url(:/resources/win/dark/ScrollTop.png); + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover { + image: url(:/resources/win/dark/ScrollTopHover.png); +} + +QScrollBar::sub-line:vertical:pressed { + image: url(:/resources/win/dark/ScrollTopPressed.png); +} + +QScrollBar::add-line:vertical { + image: url(:/resources/win/dark/ScrollBottom.png); + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical:hover { + image: url(:/resources/win/dark/ScrollBottomHover.png); +} + +QScrollBar::add-line:vertical:pressed { + image: url(:/resources/win/dark/ScrollBottomPressed.png); +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} + +/*SCROLLHORIZONTAL*/ +QScrollBar:horizontal { + border: 6px solid rgba(255, 255, 255, 0); + margin: 0px 14px 0px 14px; + height: 16px; +} + +QScrollBar:horizontal:hover { + border: 5px solid rgba(255, 255, 255, 0); +} + +QScrollBar::handle:horizontal { + background-color: rgba(255, 255, 255, 130); + border-radius: 2px; + min-width: 25px; +} + +QScrollBar::sub-line:horizontal { + image: url(:/resources/win/dark/ScrollLeft.png); + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal:hover { + image: url(:/resources/win/dark/ScrollLeftHover.png); +} + +QScrollBar::sub-line:horizontal:pressed { + image: url(:/resources/win/dark/ScrollLeftPressed.png); +} + +QScrollBar::add-line:horizontal { + image: url(:/resources/win/dark/ScrollRight.png); + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover { + image: url(:/resources/win/dark/ScrollRightHover.png); +} + +QScrollBar::add-line:horizontal:pressed { + image: url(:/resources/win/dark/ScrollRightPressed.png); +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} + +/*TEXTEDIT*/ +QTextEdit { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + font-size: 16px; + font-family: "Segoe UI", serif; + font-weight: 500; + border-radius: 7px; + border-bottom: 1px solid rgba(255, 255, 255, 150); + padding: 5px; +} + +QTextEdit:hover { + background-color: rgba(255, 255, 255, 20); + border: 1px solid rgba(255, 255, 255, 10); + border-bottom: 1px solid rgba(255, 255, 255, 150); +} + +QTextEdit:focus { + border-bottom: 2px solid %1; + background-color: rgba(255, 255, 255, 5); + border-top: 1px solid rgba(255, 255, 255, 13); + border-left: 1px solid rgba(255, 255, 255, 13); + border-right: 1px solid rgba(255, 255, 255, 13); +} + +QTextEdit:disabled { + color: rgb(150, 150, 150); + background-color: rgba(255, 255, 255, 13); + border: 1px solid rgba(255, 255, 255, 5); +} + +/*CALENDAR*/ +QCalendarWidget { +} + +QCalendarWidget QToolButton { + height: 36px; + font-size: 18px; + background-color: rgba(255, 255, 255, 0); + margin: 5px; +} + +QCalendarWidget QWidget#qt_calendar_navigationbar { + background-color: rgba(255, 255, 255, 0); + border: 1px solid rgba(255, 255, 255, 13); + border-top-left-radius: 5px; + border-top-right-radius: 5px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + border-bottom: none; +} + +#qt_calendar_prevmonth { + qproperty-icon: url(:/resources/win/dark/PrevMonth.png); + width: 32px; +} + +#qt_calendar_nextmonth { + qproperty-icon: url(:/resources/win/dark/NextMonth.png); + width: 32px; +} + +#qt_calendar_prevmonth:hover, #qt_calendar_nextmonth:hover { + background-color: rgba(255, 255, 255, 16); + border-radius: 5px; +} + +#qt_calendar_prevmonth:pressed, #qt_calendar_nextmonth:pressed { + background-color: rgba(255, 255, 255, 10); + border-radius: 5px; +} + +#qt_calendar_yearbutton, #qt_calendar_monthbutton { + color: white; + margin: 5px 0px; + padding: 0px 10px; +} + +#qt_calendar_yearbutton:hover, #qt_calendar_monthbutton:hover { + background-color: rgba(255, 255, 255, 16); + border-radius: 5px; +} + +#qt_calendar_yearbutton:pressed, #qt_calendar_monthbutton:pressed { + background-color: rgba(255, 255, 255, 10); + border-radius: 5px; +} + +QCalendarWidget QToolButton::menu-indicator#qt_calendar_monthbutton { + background-color: transparent; +} + +QCalendarWidget QMenu { + background-color : #202020; +} + +QCalendarWidget QSpinBox { + margin: 5px 0px; +} + +QCalendarWidget QSpinBox::focus { + background-color: rgba(255, 255, 255, 5); + border: 1px solid rgba(255, 255, 255, 13); + color: rgba(0, 0, 0, 200); + border-bottom: 2px solid %1; +} + +QCalendarWidget QSpinBox::up-button { + image: url(:/resources/win/dark/SpinBoxUp.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QCalendarWidget QSpinBox::up-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QCalendarWidget QSpinBox::up-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QCalendarWidget QSpinBox::down-button { + image: url(:/resources/win/dark/SpinBoxDown.png); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QCalendarWidget QSpinBox::down-button:hover { + background-color: rgba(255, 255, 255, 13); +} + +QCalendarWidget QSpinBox::down-button:pressed { + background-color: rgba(255, 255, 255, 5); +} + +QCalendarWidget QWidget { + alternate-background-color: rgba(255, 255, 255, 0); +} + +QCalendarWidget QAbstractItemView:enabled { + color: rgb(255, 255, 255); + selection-background-color: %1; + selection-color: black; + border: 1px solid rgba(255, 255, 255, 13); + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + outline: 0; +} + +QCalendarWidget QAbstractItemView:disabled { + color: rgb(150, 150, 150); + selection-background-color: rgb(150, 150, 150); + selection-color: black; + border: 1px solid rgba(255, 255, 255, 13); + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} + +#qt_calendar_yearbutton:disabled, #qt_calendar_monthbutton:disabled { + color: rgb(150, 150, 150); +} + +#qt_calendar_prevmonth:disabled { + qproperty-icon: url(:/resources/win/dark/PrevMonthDisabled.png); +} + +#qt_calendar_nextmonth:disabled { + qproperty-icon: url(:/resources/win/dark/NextMonthDisabled.png); +} + +/*TREEWIDGET*/ +QTreeView { + background-color: transparent; + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 5px; + outline: 0; + padding-right: 5px; +} + +QTreeView::item { + padding: 7px; + margin-top: 3px; +} + +QTreeView::item:selected { + color: white; + background-color: rgba(255, 255, 255, 13); + border-radius: 5px; + margin-bottom: 3px; + padding-left: 0px; +} + +QTreeView::item:!selected:hover { + background-color: rgba(255, 255, 255, 16); + border-radius: 5px; + margin-bottom: 3px; + padding-left: 0px; +} + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url(:/resources/win/dark/TreeViewClose.png); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url(:/resources/win/dark/TreeViewOpen.png); +} + +QTreeView:disabled { + color: rgb(150, 150, 150); +} + +/*TOGGLESWITCH*/ +#toggleSwitch { + color: rgb(255, 255, 255); + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; +} + +#toggleSwitch::indicator { + width: 22px; + height: 22px; + border-radius: 13px; + border: 2px solid #848484; + background-color: rgba(255, 255, 255, 0); + image: url(:/resources/win/dark/ToggleSwitchOff.png); + margin-right: 5px; + padding-right: 25px; + padding-left: 0px; +} + +#toggleSwitch::indicator:hover { + background-color: rgba(255, 255, 255, 13); + image: url(:/resources/win/dark/ToggleSwitchOffHover.png); +} + +#toggleSwitch::indicator:pressed { + background-color: rgba(255, 255, 255, 20); + width: 26px; + padding-right: 21px; + image: url(:/resources/win/dark/ToggleSwitchOffPressed.png); +} + +#toggleSwitch::indicator:checked { + background-color: %1; + border: 2px solid %1; + image: url(:/resources/win/dark/ToggleSwitchOn.png); + padding-left: 25px; + padding-right: 0px; +} + +#toggleSwitch::indicator:checked:hover { + background-color: %1; + image: url(:/resources/win/dark/ToggleSwitchOnHover.png); +} + +#toggleSwitch::indicator:checked:pressed { + background-color: %1; + width: 26px; + padding-left: 21px; + image: url(:/resources/win/dark/ToggleSwitchOnPressed.png); +} + +#toggleSwitch:disabled { + color: rgb(150, 150, 150); +} + +#toggleSwitch::indicator:disabled { + border: 2px solid #646464; + background-color: rgba(255, 255, 255, 0); + image: url(:/resources/win/dark/ToggleSwitchDisabled.png); +} + +/*HYPERLINKBUTTON*/ +#hyperlinkButton { + color: %1; + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + border-radius: 5px; + background-color: rgba(255, 255, 255, 0); + border: none; +} + +#hyperlinkButton:hover { + background-color: rgba(255, 255, 255, 20); +} + +#hyperlinkButton::pressed { + background-color: rgba(255, 255, 255, 15); + color: %1; +} + +#hyperlinkButton:disabled { + color: rgb(150, 150, 150) +} + +/*LISTVIEW*/ +QListView { + background-color: transparent; + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; + padding: 7px; + border-radius: 10px; + outline: 0; +} + +QListView::item { + height: 35px; +} + +QListView::item:selected { + background-color: rgba(255, 255, 255, 13); + color: white; + border-radius: 5px; + padding-left: 0px; +} \ No newline at end of file diff --git a/resources/win.light.qcss b/resources/win.light.qcss new file mode 100644 index 0000000..3f34631 --- /dev/null +++ b/resources/win.light.qcss @@ -0,0 +1,1124 @@ +QWidget { + background: transparent; + color: rgb(0, 0, 0); + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; +} + +/*MENU*/ +QMenuBar { + background-color: transparent; + color: rgba(0, 0, 0); + padding: 10px; + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; +} + +QMenuBar::item { + background-color: transparent; + padding: 10px 13px; + margin-left: 5px; + border-radius: 5px; +} + +QMenuBar::item:selected { + background-color: rgb(0, 0, 0, 10); +} + +QMenuBar::item:pressed { + background-color: rgb(0, 0, 0, 7); + color: rgb(0, 0, 0, 150); +} + +QMenu { + background-color: transparent; + padding-left: 1px; + padding-top: 1px; + border-radius: 5px; + border: 1px solid rgb(0, 0, 0, 13); +} + +QMenu::item { + background-color: transparent; + padding: 5px 15px; + border-radius: 5px; + min-width: 60px; + margin: 3px; +} + +QMenu::item:selected { + background-color: rgb(0, 0, 0, 10); +} + +QMenu::item:pressed { + background-color: rgb(0, 0, 0, 7); +} + +QMenu::right-arrow { + image: url(:/resources/win/light/TreeViewClose.png); + min-width: 40px; + min-height: 18px; +} + +QMenuBar:disabled { + color: rgb(0, 0, 0, 150); +} + +QMenu::item:disabled { + color: rgb(0, 0, 0, 150); + background-color: transparent; +} + +/*PUSHBUTTON*/ +QPushButton { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 7px; + min-height: 38px; + max-height: 38px; +} + +QPushButton:hover { + background-color: rgb(0, 0, 0, 10); + border: 1px solid rgb(0, 0, 0, 13); +} + +QPushButton::pressed { + color: rgb(0, 0, 0, 150); +} + +QPushButton::disabled { + color: rgb(0, 0, 0, 110); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +/*RADIOBUTTON*/ +QRadioButton { + min-height: 30px; + max-height: 30px; +} + +QRadioButton::indicator { + width: 22px; + height: 22px; + border-radius: 13px; + border: 2px solid #999999; + background-color: rgb(0, 0, 0, 5); + margin-right: 5px; +} + +QRadioButton::indicator:hover { + background-color: rgb(0, 0, 0, 0); +} + +QRadioButton::indicator:pressed { + background-color: rgb(0, 0, 0, 5); + border: 2px solid #bbbbbb; + image: url(:/resources/win/light/RadioButton.png); +} + +QRadioButton::indicator:checked { + background-color: %1; + border: 2px solid %1; + image: url(:/resources/win/light/RadioButton.png); + color: rgb(255, 255, 255); +} + +QRadioButton::indicator:checked:hover { + image: url(:/resources/win/light/RadioButtonHover.png); +} + +QRadioButton::indicator:checked:pressed { + image: url(:/resources/win/light/RadioButtonPressed.png); +} + +QRadioButton:disabled { + color: rgb(0, 0, 0, 110); +} + +QRadioButton::indicator:disabled { + border: 2px solid #bbbbbb; + background-color: rgb(0, 0, 0, 0); +} + +/*CHECKBOX*/ +QCheckBox { + min-height: 30px; + max-height: 30px; +} + +QCheckBox::indicator { + width: 22px; + height: 22px; + border-radius: 5px; + border: 2px solid #999999; + background-color: rgb(0, 0, 0, 0); + margin-right: 5px; +} + +QCheckBox::indicator:hover { + background-color: rgb(0, 0, 0, 15); +} + +QCheckBox::indicator:pressed { + background-color: rgb(0, 0, 0, 24); + border: 2px solid #bbbbbb; +} + +QCheckBox::indicator:checked { + background-color: %1; + border: 2px solid %1; + image: url(:/resources/win/light/CheckBox.png); + color: rgb(255, 255, 255); +} + +QCheckBox::indicator:checked:pressed { + image: url(:/resources/win/light/CheckBoxPressed.png); +} + +QCheckBox:disabled { + color: rgb(0, 0, 0, 110); +} + +QCheckBox::indicator:disabled { + border: 2px solid #bbbbbb; + background-color: rgb(0, 0, 0, 0); +} + +/*GROUPBOX*/ +QGroupBox { + border-radius: 5px; + border: 1px solid rgb(0, 0, 0, 13); + margin-top: 36px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + background-color: rgb(0, 0, 0, 10); + padding: 7px 15px; + margin-left: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +QGroupBox::title::disabled { + color: rgb(0, 0, 0, 150); +} + +/*TABWIDGET*/ +QTabWidget { +} + +QWidget { + border-radius: 5px; +} + +QTabWidget::pane { + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 5px; +} + +QTabWidget::tab-bar { + left: 5px; +} + +QTabBar::tab { + background-color: rgb(0, 0, 0, 0); + padding: 7px 15px; + margin-right: 2px; +} + +QTabBar::tab:hover { + background-color: rgb(0, 0, 0, 13); + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +QTabBar::tab:selected { + background-color: rgb(0, 0, 0, 10); + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +QTabBar::tab:disabled { + color: rgb(0, 0, 0, 150) +} + +/*SPINBOX*/ +QSpinBox { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; + min-width: 100px; + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QSpinBox:hover { + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 13); + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QSpinBox::focus { + background-color: rgb(0, 0, 0, 5); + border: 1px solid rgb(0, 0, 0, 10); + color: rgb(0, 0, 0, 200); + border-bottom: 2px solid %1; +} + +QSpinBox::up-button { + image: url(:/resources/win/light/SpinBoxUp.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QSpinBox::up-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QSpinBox::up-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QSpinBox::down-button { + image: url(:/resources/win/light/SpinBoxDown.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QSpinBox::down-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QSpinBox::down-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QSpinBox::drop-down { + background-color: transparent; + width: 50px; +} + +QSpinBox:disabled { + color: rgb(0, 0, 0, 110); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +QSpinBox::up-button:disabled { + image: url(:/resources/win/light/SpinBoxUpDisabled.png); +} + +QSpinBox::down-button:disabled { + image: url(:/resources/win/light/SpinBoxDownDisabled.png); +} + +/*DOUBLESPINBOX*/ +QDoubleSpinBox { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; + min-width: 100px; + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QDoubleSpinBox:hover { + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 13); + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QDoubleSpinBox::focus { + background-color: rgb(0, 0, 0, 5); + border: 1px solid rgb(0, 0, 0, 10); + color: rgb(0, 0, 0, 200); + border-bottom: 2px solid %1; +} + +QDoubleSpinBox::up-button { + image: url(:/resources/win/light/SpinBoxUp.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDoubleSpinBox::up-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QDoubleSpinBox::up-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QDoubleSpinBox::down-button { + image: url(:/resources/win/light/SpinBoxDown.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDoubleSpinBox::down-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QDoubleSpinBox::down-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QDoubleSpinBox::drop-down { + background-color: transparent; + width: 50px; +} + +QDoubleSpinBox:disabled { + color: rgb(0, 0, 0, 110); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +QDoubleSpinBox::up-button:disabled { + image: url(:/resources/win/light/SpinBoxUpDisabled.png); +} + +QDoubleSpinBox::down-button:disabled { + image: url(:/resources/win/light/SpinBoxDownDisabled.png); +} + +/*DATETIMEEDIT*/ +QDateTimeEdit { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; + min-width: 100px; + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QDateTimeEdit:hover { + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 13); + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QDateTimeEdit::focus { + background-color: rgb(0, 0, 0, 5); + border: 1px solid rgb(0, 0, 0, 10); + color: rgb(0, 0, 0, 200); + border-bottom: 2px solid %1; +} + +QDateTimeEdit::up-button { + image: url(:/resources/win/light/SpinBoxUp.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDateTimeEdit::up-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QDateTimeEdit::up-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QDateTimeEdit::down-button { + image: url(:/resources/win/light/SpinBoxDown.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QDateTimeEdit::down-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QDateTimeEdit::down-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QDateTimeEdit::drop-down { + background-color: transparent; + width: 50px; +} + +QDateTimeEdit:disabled { + color: rgb(0, 0, 0, 110); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +QDateTimeEdit::up-button:disabled { + image: url(:/resources/win/light/SpinBoxUpDisabled.png); +} + +QDateTimeEdit::down-button:disabled { + image: url(:/resources/win/light/SpinBoxDownDisabled.png); +} + +/*SLIDERVERTICAL*/ +QSlider:vertical { + min-width: 30px; + min-height: 100px; +} + +QSlider::groove:vertical { + width: 5px; + background-color: rgb(0, 0, 0, 100); + border-radius: 2px; +} + +QSlider::handle:vertical { + background-color: %1; + border: 6px solid #dbdbdb; + height: 13px; + min-width: 15px; + margin: 0px -10px; + border-radius: 12px; +} + +QSlider::handle:vertical:hover { + background-color: %1; + border: 4px solid #dbdbdb; + height: 17px; + min-width: 15px; + margin: 0px -10px; + border-radius: 12px +} + +QSlider::handle:vertical:pressed { + background-color: %1; + border: 7px solid #dbdbdb; + height: 11px; + min-width: 15px; + margin: 0px -10px; + border-radius: 12px +} + +QSlider::groove:vertical:disabled { + background-color: rgb(0, 0, 0, 75); +} + +QSlider::handle:vertical:disabled { + background-color: #808080; + border: 6px solid #cccccc; +} + +/*SLIDERHORIZONTAL*/ +QSlider:horizontal { + min-width: 100px; + min-height: 30px; +} + +QSlider::groove:horizontal { + height: 5px; + background-color: rgb(0, 0, 0, 100); + border-radius: 2px; +} + +QSlider::handle:horizontal { + background-color: %1; + border: 6px solid #dbdbdb; + width: 13px; + min-height: 15px; + margin: -10px 0; + border-radius: 12px +} + +QSlider::handle:horizontal:hover { + background-color: %1; + border: 4px solid #dbdbdb; + width: 17px; + min-height: 15px; + margin: -10px 0; + border-radius: 12px +} + +QSlider::handle:horizontal:pressed { + background-color: %1; + border: 7px solid #dbdbdb; + width: 11px; + min-height: 15px; + margin: -10px 0; + border-radius: 12px +} + +QSlider::groove:horizontal:disabled { + background-color: rgb(0, 0, 0, 75); +} + +QSlider::handle:horizontal:disabled { + background-color: #808080; + border: 6px solid #cccccc; +} + +/*PROGRESSBAR*/ +QProgressBar { + background-color: qlineargradient(spread:reflect, x1:0.5, y1:0.5, x2:0.5, y2:1, stop:0.233831 rgba(0, 0, 0, 255), stop:0.343284 rgba(0, 0, 0, 0)); + border-radius: 2px; + min-height: 4px; + max-height: 4px; +} + +QProgressBar::chunk { + background-color: %1; + border-radius: 2px; +} + +/*COMBOBOX*/ +QComboBox { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 5px; + padding-left: 10px; + min-height: 38px; + max-height: 38px; +} + +QComboBox:hover { + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 13); +} + +QComboBox::pressed { + border: 1px solid rgb(0, 0, 0, 10); +} + +QComboBox::down-arrow { + image: url(:/resources/win/light/ComboBox.png); +} + +QComboBox::drop-down { + background-color: transparent; + min-width: 50px; +} + +QComboBox:disabled { + color: rgb(0, 0, 0, 110); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +QComboBox::down-arrow:disabled { + image: url(:/resources/win/light/ComboBoxDisabled.png); +} + +/*LINEEDIT*/ +QLineEdit { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + font-size: 16px; + font-family: "Segoe UI", serif; + font-weight: 500; + border-radius: 7px; + border-bottom: 1px solid rgb(0, 0, 0, 100); + padding-top: 0px; + padding-left: 5px; +} + +QLineEdit:hover { + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 13); + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QLineEdit:focus { + border-bottom: 2px solid %1; + background-color: rgb(0, 0, 0, 5); + border-top: 1px solid rgb(0, 0, 0, 13); + border-left: 1px solid rgb(0, 0, 0, 13); + border-right: 1px solid rgb(0, 0, 0, 13); +} + +QLineEdit:disabled { + color: rgb(0, 0, 0, 150); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +/*SCROLLVERTICAL*/ +QScrollBar:vertical { + border: 6px solid rgb(0, 0, 0, 0); + margin: 14px 0px 14px 0px; + width: 16px; +} + +QScrollBar:vertical:hover { + border: 5px solid rgb(0, 0, 0, 0); +} + +QScrollBar::handle:vertical { + background-color: rgb(0, 0, 0, 110); + border-radius: 2px; + min-height: 25px; +} + +QScrollBar::sub-line:vertical { + image: url(:/resources/win/light/ScrollTop.png); + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover { + image: url(:/resources/win/light/ScrollTopHover.png); +} + +QScrollBar::sub-line:vertical:pressed { + image: url(:/resources/win/light/ScrollTopPressed.png); +} + +QScrollBar::add-line:vertical { + image: url(:/resources/win/light/ScrollBottom.png); + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical:hover { + image: url(:/resources/win/light/ScrollBottomHover.png); +} + +QScrollBar::add-line:vertical:pressed { + image: url(:/resources/win/light/ScrollBottomPressed.png); +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} + +/*SCROLLHORIZONTAL*/ +QScrollBar:horizontal { + border: 6px solid rgb(0, 0, 0, 0); + margin: 0px 14px 0px 14px; + height: 16px; +} + +QScrollBar:horizontal:hover { + border: 5px solid rgb(0, 0, 0, 0); +} + +QScrollBar::handle:horizontal { + background-color: rgb(0, 0, 0, 110); + border-radius: 2px; + min-width: 25px; +} + +QScrollBar::sub-line:horizontal { + image: url(:/resources/win/light/ScrollLeft.png); + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal:hover { + image: url(:/resources/win/light/ScrollLeftHover.png); +} + +QScrollBar::sub-line:horizontal:pressed { + image: url(:/resources/win/light/ScrollLeftPressed.png); +} + +QScrollBar::add-line:horizontal { + image: url(:/resources/win/light/ScrollRight.png); + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover { + image: url(:/resources/win/light/ScrollRightHover.png); +} + +QScrollBar::add-line:horizontal:pressed { + image: url(:/resources/win/light/ScrollRightPressed.png); +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} + +/*TEXTEDIT*/ +QTextEdit { + background-color: rgb(0, 0, 0, 7); + border: 1px solid rgb(0, 0, 0, 13); + font-size: 16px; + font-family: "Segoe UI", serif; + font-weight: 500; + border-radius: 7px; + border-bottom: 1px solid rgb(0, 0, 0, 100); + padding: 5px; +} + +QTextEdit:hover { + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 13); + border-bottom: 1px solid rgb(0, 0, 0, 100); +} + +QTextEdit:focus { + background-color: rgb(0, 0, 0, 5); + border-top: 1px solid rgb(0, 0, 0, 13); + border-left: 1px solid rgb(0, 0, 0, 13); + border-right: 1px solid rgb(0, 0, 0, 13); + border-bottom: 2px solid %1; +} + +QTextEdit:disabled { + color: rgb(0, 0, 0, 110); + background-color: rgb(0, 0, 0, 13); + border: 1px solid rgb(0, 0, 0, 5); +} + +/*CALENDAR*/ +QCalendarWidget { +} + +QCalendarWidget QToolButton { + height: 36px; + font-size: 18px; + background-color: rgb(0, 0, 0, 0); + margin: 5px; +} + +QCalendarWidget QWidget#qt_calendar_navigationbar { + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 13); + border-top-left-radius: 5px; + border-top-right-radius: 5px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + border-bottom: none; +} + +QCalendarWidget QMenu { + background-color : #f3f3f3; +} + +#qt_calendar_prevmonth { + qproperty-icon: url(:/resources/win/light/PrevMonth.png); + width: 32px; +} + +#qt_calendar_nextmonth { + qproperty-icon: url(:/resources/win/light/NextMonth.png); + width: 32px; +} + +#qt_calendar_prevmonth:hover, #qt_calendar_nextmonth:hover { + background-color: rgb(0, 0, 0, 10); + border-radius: 5px; +} + +#qt_calendar_prevmonth:pressed, #qt_calendar_nextmonth:pressed { + background-color: rgb(0, 0, 0, 7); + border-radius: 5px; +} + +#qt_calendar_yearbutton, #qt_calendar_monthbutton { + color: rgb(0, 0, 0); + margin: 5px 0px; + padding: 0px 10px; +} + +#qt_calendar_yearbutton:hover, #qt_calendar_monthbutton:hover { + background-color: rgb(0, 0, 0, 10); + border-radius: 5px; +} + +#qt_calendar_yearbutton:pressed, #qt_calendar_monthbutton:pressed { + background-color: rgb(0, 0, 0, 7); + border-radius: 5px; +} + +QCalendarWidget QToolButton::menu-indicator#qt_calendar_monthbutton { + background-color: transparent; +} + +QCalendarWidget QSpinBox { + margin: 5px 0px; +} + +QCalendarWidget QSpinBox::focus { + background-color: rgb(0, 0, 0, 5); + border: 1px solid rgb(0, 0, 0, 10); + color: rgb(0, 0, 0, 200); + border-bottom: 2px solid %1; +} + +QCalendarWidget QSpinBox::up-button { + image: url(:/resources/win/light/SpinBoxUp.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QCalendarWidget QSpinBox::up-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QCalendarWidget QSpinBox::up-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QCalendarWidget QSpinBox::down-button { + image: url(:/resources/win/light/SpinBoxDown.png); + background-color: rgb(0, 0, 0, 0); + border: 1px solid rgb(0, 0, 0, 0); + border-radius: 4px; + margin-top: 1px; + margin-bottom: 1px; + margin-right: 2px; + min-width: 30px; + max-width: 30px; + min-height: 20px; +} + +QCalendarWidget QSpinBox::down-button:hover { + background-color: rgb(0, 0, 0, 10); +} + +QCalendarWidget QSpinBox::down-button:pressed { + background-color: rgb(0, 0, 0, 5); +} + +QCalendarWidget QWidget { + alternate-background-color: rgb(0, 0, 0, 0); +} + +QCalendarWidget QAbstractItemView:enabled { + color: rgb(0, 0, 0); + selection-background-color: %1; + selection-color: black; + border: 1px solid rgb(0, 0, 0, 10); + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + outline: 0; +} + +QCalendarWidget QAbstractItemView:disabled { + color: rgb(30, 30, 30); + selection-background-color: rgb(30, 30, 30); + selection-color: black; + border: 1px solid rgb(0, 0, 0, 13); + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} + +#qt_calendar_yearbutton:disabled, #qt_calendar_monthbutton:disabled { + color: rgb(0, 0, 0, 110); +} + +#qt_calendar_prevmonth:disabled { + qproperty-icon: url(:/resources/win/light/PrevMonthDisabled.png); +} + +#qt_calendar_nextmonth:disabled { + qproperty-icon: url(:/resources/win/light/NextMonthDisabled.png); +} + +/*TREEWIDGET*/ +QTreeView { + background-color: transparent; + border: 1px solid rgb(0, 0, 0, 13); + border-radius: 5px; + outline: 0; + padding-right: 5px; +} + +QTreeView::item { + padding: 7px; + margin-top: 3px; +} + +QTreeView::item:selected { + color: rgb(0, 0, 0); + background-color: rgb(0, 0, 0, 7); + border-radius: 5px; + margin-bottom: 3px; + padding-left: 0px; +} + +QTreeView::item:!selected:hover { + background-color: rgb(0, 0, 0, 13); + border-radius: 5px; + margin-bottom: 3px; + padding-left: 0px; +} + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url(:/resources/win/light/TreeViewClose.png); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url(:/resources/win/light/TreeViewOpen.png); +} + +QTreeView:disabled { + color: rgb(0, 0, 0, 110); +} + +/*TOGGLESWITCH*/ +#toggleSwitch { + color: rgb(0, 0, 0); + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; +} + +#toggleSwitch::indicator { + width: 22px; + height: 22px; + border-radius: 13px; + border: 2px solid #999999; + background-color: rgb(0, 0, 0, 0); + image: url(:/resources/win/light/ToggleSwitchOff.png); + margin-right: 5px; + padding-right: 25px; + padding-left: 0px; +} + +#toggleSwitch::indicator:hover { + background-color: rgb(0, 0, 0, 15); + image: url(:/resources/win/light/ToggleSwitchOffHover.png); +} + +#toggleSwitch::indicator:pressed { + background-color: rgb(0, 0, 0, 24); + width: 26px; + padding-right: 21px; + image: url(:/resources/win/light/ToggleSwitchOffPressed.png); +} + +#toggleSwitch::indicator:checked { + background-color: %1; + border: 2px solid %1; + image: url(:/resources/win/light/ToggleSwitchOn.png); + color: rgb(255, 255, 255); + padding-left: 25px; + padding-right: 0px; +} + +#toggleSwitch::indicator:checked:hover { + background-color: %1; + image: url(:/resources/win/light/ToggleSwitchOnHover.png); +} + +#toggleSwitch::indicator:checked:pressed { + background-color: %1; + width: 26px; + padding-left: 21px; + image: url(:/resources/win/light/ToggleSwitchOnPressed.png); +} + +#toggleSwitch:disabled { + color: rgb(0, 0, 0, 110); +} + +#toggleSwitch::indicator:disabled { + border: 2px solid #bbbbbb; + image: url(:/resources/win/light/ToggleSwitchDisabled.png); +} + +/*HYPERLINKBUTTON*/ +#hyperlinkButton { + color: %1; + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + border-radius: 5px; + background-color: rgb(0, 0, 0, 0); + border: none; +} + +#hyperlinkButton:hover { + background-color: rgb(0, 0, 0, 10); +} + +#hyperlinkButton::pressed { + background-color: rgb(0, 0, 0, 7); + color: %1; +} + +#hyperlinkButton:disabled { + color: rgb(0, 0, 0, 110) +} + +/*LISTVIEW*/ +QListView { + background-color: transparent; + font-size: 17px; + font-family: "Segoe UI Variable Small", serif; + font-weight: 400; + padding: 7px; + border-radius: 10px; + outline: 0; +} + +QListView::item { + height: 35px; +} + +QListView::item:selected { + background-color: rgb(0, 0, 0, 13); + color: black; + border-radius: 5px; + padding-left: 0px; +} \ No newline at end of file diff --git a/resources/win/dark/CheckBox.png b/resources/win/dark/CheckBox.png new file mode 100644 index 0000000..a6dc41b Binary files /dev/null and b/resources/win/dark/CheckBox.png differ diff --git a/resources/win/dark/CheckBoxPressed.png b/resources/win/dark/CheckBoxPressed.png new file mode 100644 index 0000000..854ab34 Binary files /dev/null and b/resources/win/dark/CheckBoxPressed.png differ diff --git a/resources/win/dark/ComboBox.png b/resources/win/dark/ComboBox.png new file mode 100644 index 0000000..e16366b Binary files /dev/null and b/resources/win/dark/ComboBox.png differ diff --git a/resources/win/dark/ComboBoxDisabled.png b/resources/win/dark/ComboBoxDisabled.png new file mode 100644 index 0000000..997a390 Binary files /dev/null and b/resources/win/dark/ComboBoxDisabled.png differ diff --git a/resources/win/dark/NextMonth.png b/resources/win/dark/NextMonth.png new file mode 100644 index 0000000..395f4ff Binary files /dev/null and b/resources/win/dark/NextMonth.png differ diff --git a/resources/win/dark/NextMonthDisabled.png b/resources/win/dark/NextMonthDisabled.png new file mode 100644 index 0000000..29832ff Binary files /dev/null and b/resources/win/dark/NextMonthDisabled.png differ diff --git a/resources/win/dark/PrevMonth.png b/resources/win/dark/PrevMonth.png new file mode 100644 index 0000000..a995b1f Binary files /dev/null and b/resources/win/dark/PrevMonth.png differ diff --git a/resources/win/dark/PrevMonthDisabled.png b/resources/win/dark/PrevMonthDisabled.png new file mode 100644 index 0000000..4f4d708 Binary files /dev/null and b/resources/win/dark/PrevMonthDisabled.png differ diff --git a/resources/win/dark/RadioButton.png b/resources/win/dark/RadioButton.png new file mode 100644 index 0000000..18fc292 Binary files /dev/null and b/resources/win/dark/RadioButton.png differ diff --git a/resources/win/dark/RadioButtonHover.png b/resources/win/dark/RadioButtonHover.png new file mode 100644 index 0000000..c10c840 Binary files /dev/null and b/resources/win/dark/RadioButtonHover.png differ diff --git a/resources/win/dark/RadioButtonPressed.png b/resources/win/dark/RadioButtonPressed.png new file mode 100644 index 0000000..c17a061 Binary files /dev/null and b/resources/win/dark/RadioButtonPressed.png differ diff --git a/resources/win/dark/ScrollBottom.png b/resources/win/dark/ScrollBottom.png new file mode 100644 index 0000000..c0fdf27 Binary files /dev/null and b/resources/win/dark/ScrollBottom.png differ diff --git a/resources/win/dark/ScrollBottomHover.png b/resources/win/dark/ScrollBottomHover.png new file mode 100644 index 0000000..27b1ac5 Binary files /dev/null and b/resources/win/dark/ScrollBottomHover.png differ diff --git a/resources/win/dark/ScrollBottomPressed.png b/resources/win/dark/ScrollBottomPressed.png new file mode 100644 index 0000000..4d2d496 Binary files /dev/null and b/resources/win/dark/ScrollBottomPressed.png differ diff --git a/resources/win/dark/ScrollLeft.png b/resources/win/dark/ScrollLeft.png new file mode 100644 index 0000000..de8e5cb Binary files /dev/null and b/resources/win/dark/ScrollLeft.png differ diff --git a/resources/win/dark/ScrollLeftHover.png b/resources/win/dark/ScrollLeftHover.png new file mode 100644 index 0000000..b04ff99 Binary files /dev/null and b/resources/win/dark/ScrollLeftHover.png differ diff --git a/resources/win/dark/ScrollLeftPressed.png b/resources/win/dark/ScrollLeftPressed.png new file mode 100644 index 0000000..22d516c Binary files /dev/null and b/resources/win/dark/ScrollLeftPressed.png differ diff --git a/resources/win/dark/ScrollRight.png b/resources/win/dark/ScrollRight.png new file mode 100644 index 0000000..5620fc3 Binary files /dev/null and b/resources/win/dark/ScrollRight.png differ diff --git a/resources/win/dark/ScrollRightHover.png b/resources/win/dark/ScrollRightHover.png new file mode 100644 index 0000000..16da5c8 Binary files /dev/null and b/resources/win/dark/ScrollRightHover.png differ diff --git a/resources/win/dark/ScrollRightPressed.png b/resources/win/dark/ScrollRightPressed.png new file mode 100644 index 0000000..2743f70 Binary files /dev/null and b/resources/win/dark/ScrollRightPressed.png differ diff --git a/resources/win/dark/ScrollTop.png b/resources/win/dark/ScrollTop.png new file mode 100644 index 0000000..684c0e7 Binary files /dev/null and b/resources/win/dark/ScrollTop.png differ diff --git a/resources/win/dark/ScrollTopHover.png b/resources/win/dark/ScrollTopHover.png new file mode 100644 index 0000000..f999c9d Binary files /dev/null and b/resources/win/dark/ScrollTopHover.png differ diff --git a/resources/win/dark/ScrollTopPressed.png b/resources/win/dark/ScrollTopPressed.png new file mode 100644 index 0000000..2043f77 Binary files /dev/null and b/resources/win/dark/ScrollTopPressed.png differ diff --git a/resources/win/dark/SpinBoxDown.png b/resources/win/dark/SpinBoxDown.png new file mode 100644 index 0000000..dca27e0 Binary files /dev/null and b/resources/win/dark/SpinBoxDown.png differ diff --git a/resources/win/dark/SpinBoxDownDisabled.png b/resources/win/dark/SpinBoxDownDisabled.png new file mode 100644 index 0000000..863987b Binary files /dev/null and b/resources/win/dark/SpinBoxDownDisabled.png differ diff --git a/resources/win/dark/SpinBoxUp.png b/resources/win/dark/SpinBoxUp.png new file mode 100644 index 0000000..76e4450 Binary files /dev/null and b/resources/win/dark/SpinBoxUp.png differ diff --git a/resources/win/dark/SpinBoxUpDisabled.png b/resources/win/dark/SpinBoxUpDisabled.png new file mode 100644 index 0000000..3fa452a Binary files /dev/null and b/resources/win/dark/SpinBoxUpDisabled.png differ diff --git a/resources/win/dark/ToggleSwitchDisabled.png b/resources/win/dark/ToggleSwitchDisabled.png new file mode 100644 index 0000000..2045cd2 Binary files /dev/null and b/resources/win/dark/ToggleSwitchDisabled.png differ diff --git a/resources/win/dark/ToggleSwitchOff.png b/resources/win/dark/ToggleSwitchOff.png new file mode 100644 index 0000000..7e32992 Binary files /dev/null and b/resources/win/dark/ToggleSwitchOff.png differ diff --git a/resources/win/dark/ToggleSwitchOffHover.png b/resources/win/dark/ToggleSwitchOffHover.png new file mode 100644 index 0000000..4d36abe Binary files /dev/null and b/resources/win/dark/ToggleSwitchOffHover.png differ diff --git a/resources/win/dark/ToggleSwitchOffPressed.png b/resources/win/dark/ToggleSwitchOffPressed.png new file mode 100644 index 0000000..8fcb8b6 Binary files /dev/null and b/resources/win/dark/ToggleSwitchOffPressed.png differ diff --git a/resources/win/dark/ToggleSwitchOn.png b/resources/win/dark/ToggleSwitchOn.png new file mode 100644 index 0000000..c10c840 Binary files /dev/null and b/resources/win/dark/ToggleSwitchOn.png differ diff --git a/resources/win/dark/ToggleSwitchOnHover.png b/resources/win/dark/ToggleSwitchOnHover.png new file mode 100644 index 0000000..ed9094f Binary files /dev/null and b/resources/win/dark/ToggleSwitchOnHover.png differ diff --git a/resources/win/dark/ToggleSwitchOnPressed.png b/resources/win/dark/ToggleSwitchOnPressed.png new file mode 100644 index 0000000..e46f72f Binary files /dev/null and b/resources/win/dark/ToggleSwitchOnPressed.png differ diff --git a/resources/win/dark/TreeViewClose.png b/resources/win/dark/TreeViewClose.png new file mode 100644 index 0000000..70699fc Binary files /dev/null and b/resources/win/dark/TreeViewClose.png differ diff --git a/resources/win/dark/TreeViewOpen.png b/resources/win/dark/TreeViewOpen.png new file mode 100644 index 0000000..f780dce Binary files /dev/null and b/resources/win/dark/TreeViewOpen.png differ diff --git a/resources/win/light/CheckBox.png b/resources/win/light/CheckBox.png new file mode 100644 index 0000000..2bf3cb7 Binary files /dev/null and b/resources/win/light/CheckBox.png differ diff --git a/resources/win/light/CheckBoxPressed.png b/resources/win/light/CheckBoxPressed.png new file mode 100644 index 0000000..a70e14e Binary files /dev/null and b/resources/win/light/CheckBoxPressed.png differ diff --git a/resources/win/light/ComboBox.png b/resources/win/light/ComboBox.png new file mode 100644 index 0000000..9cb5618 Binary files /dev/null and b/resources/win/light/ComboBox.png differ diff --git a/resources/win/light/ComboBoxDisabled.png b/resources/win/light/ComboBoxDisabled.png new file mode 100644 index 0000000..997a390 Binary files /dev/null and b/resources/win/light/ComboBoxDisabled.png differ diff --git a/resources/win/light/NextMonth.png b/resources/win/light/NextMonth.png new file mode 100644 index 0000000..2eb226a Binary files /dev/null and b/resources/win/light/NextMonth.png differ diff --git a/resources/win/light/NextMonthDisabled.png b/resources/win/light/NextMonthDisabled.png new file mode 100644 index 0000000..395f4ff Binary files /dev/null and b/resources/win/light/NextMonthDisabled.png differ diff --git a/resources/win/light/PrevMonth.png b/resources/win/light/PrevMonth.png new file mode 100644 index 0000000..ef060d0 Binary files /dev/null and b/resources/win/light/PrevMonth.png differ diff --git a/resources/win/light/PrevMonthDisabled.png b/resources/win/light/PrevMonthDisabled.png new file mode 100644 index 0000000..a995b1f Binary files /dev/null and b/resources/win/light/PrevMonthDisabled.png differ diff --git a/resources/win/light/RadioButton.png b/resources/win/light/RadioButton.png new file mode 100644 index 0000000..6350856 Binary files /dev/null and b/resources/win/light/RadioButton.png differ diff --git a/resources/win/light/RadioButtonHover.png b/resources/win/light/RadioButtonHover.png new file mode 100644 index 0000000..22e66bf Binary files /dev/null and b/resources/win/light/RadioButtonHover.png differ diff --git a/resources/win/light/RadioButtonPressed.png b/resources/win/light/RadioButtonPressed.png new file mode 100644 index 0000000..693a119 Binary files /dev/null and b/resources/win/light/RadioButtonPressed.png differ diff --git a/resources/win/light/ScrollBottom.png b/resources/win/light/ScrollBottom.png new file mode 100644 index 0000000..523a9a9 Binary files /dev/null and b/resources/win/light/ScrollBottom.png differ diff --git a/resources/win/light/ScrollBottomHover.png b/resources/win/light/ScrollBottomHover.png new file mode 100644 index 0000000..686c350 Binary files /dev/null and b/resources/win/light/ScrollBottomHover.png differ diff --git a/resources/win/light/ScrollBottomPressed.png b/resources/win/light/ScrollBottomPressed.png new file mode 100644 index 0000000..7612529 Binary files /dev/null and b/resources/win/light/ScrollBottomPressed.png differ diff --git a/resources/win/light/ScrollLeft.png b/resources/win/light/ScrollLeft.png new file mode 100644 index 0000000..24df56a Binary files /dev/null and b/resources/win/light/ScrollLeft.png differ diff --git a/resources/win/light/ScrollLeftHover.png b/resources/win/light/ScrollLeftHover.png new file mode 100644 index 0000000..24af3ae Binary files /dev/null and b/resources/win/light/ScrollLeftHover.png differ diff --git a/resources/win/light/ScrollLeftPressed.png b/resources/win/light/ScrollLeftPressed.png new file mode 100644 index 0000000..8d21b37 Binary files /dev/null and b/resources/win/light/ScrollLeftPressed.png differ diff --git a/resources/win/light/ScrollRight.png b/resources/win/light/ScrollRight.png new file mode 100644 index 0000000..f8e070b Binary files /dev/null and b/resources/win/light/ScrollRight.png differ diff --git a/resources/win/light/ScrollRightHover.png b/resources/win/light/ScrollRightHover.png new file mode 100644 index 0000000..4e9b9fa Binary files /dev/null and b/resources/win/light/ScrollRightHover.png differ diff --git a/resources/win/light/ScrollRightPressed.png b/resources/win/light/ScrollRightPressed.png new file mode 100644 index 0000000..a63fad0 Binary files /dev/null and b/resources/win/light/ScrollRightPressed.png differ diff --git a/resources/win/light/ScrollTop.png b/resources/win/light/ScrollTop.png new file mode 100644 index 0000000..e486662 Binary files /dev/null and b/resources/win/light/ScrollTop.png differ diff --git a/resources/win/light/ScrollTopHover.png b/resources/win/light/ScrollTopHover.png new file mode 100644 index 0000000..c02577a Binary files /dev/null and b/resources/win/light/ScrollTopHover.png differ diff --git a/resources/win/light/ScrollTopPressed.png b/resources/win/light/ScrollTopPressed.png new file mode 100644 index 0000000..72d3ab7 Binary files /dev/null and b/resources/win/light/ScrollTopPressed.png differ diff --git a/resources/win/light/SpinBoxDown.png b/resources/win/light/SpinBoxDown.png new file mode 100644 index 0000000..9898d46 Binary files /dev/null and b/resources/win/light/SpinBoxDown.png differ diff --git a/resources/win/light/SpinBoxDownDisabled.png b/resources/win/light/SpinBoxDownDisabled.png new file mode 100644 index 0000000..1c48117 Binary files /dev/null and b/resources/win/light/SpinBoxDownDisabled.png differ diff --git a/resources/win/light/SpinBoxUp.png b/resources/win/light/SpinBoxUp.png new file mode 100644 index 0000000..f089d8b Binary files /dev/null and b/resources/win/light/SpinBoxUp.png differ diff --git a/resources/win/light/SpinBoxUpDisabled.png b/resources/win/light/SpinBoxUpDisabled.png new file mode 100644 index 0000000..14437d4 Binary files /dev/null and b/resources/win/light/SpinBoxUpDisabled.png differ diff --git a/resources/win/light/ToggleSwitchDisabled.png b/resources/win/light/ToggleSwitchDisabled.png new file mode 100644 index 0000000..f6209ca Binary files /dev/null and b/resources/win/light/ToggleSwitchDisabled.png differ diff --git a/resources/win/light/ToggleSwitchOff.png b/resources/win/light/ToggleSwitchOff.png new file mode 100644 index 0000000..2045cd2 Binary files /dev/null and b/resources/win/light/ToggleSwitchOff.png differ diff --git a/resources/win/light/ToggleSwitchOffHover.png b/resources/win/light/ToggleSwitchOffHover.png new file mode 100644 index 0000000..138c595 Binary files /dev/null and b/resources/win/light/ToggleSwitchOffHover.png differ diff --git a/resources/win/light/ToggleSwitchOffPressed.png b/resources/win/light/ToggleSwitchOffPressed.png new file mode 100644 index 0000000..38d76e0 Binary files /dev/null and b/resources/win/light/ToggleSwitchOffPressed.png differ diff --git a/resources/win/light/ToggleSwitchOn.png b/resources/win/light/ToggleSwitchOn.png new file mode 100644 index 0000000..22e66bf Binary files /dev/null and b/resources/win/light/ToggleSwitchOn.png differ diff --git a/resources/win/light/ToggleSwitchOnHover.png b/resources/win/light/ToggleSwitchOnHover.png new file mode 100644 index 0000000..4c333c1 Binary files /dev/null and b/resources/win/light/ToggleSwitchOnHover.png differ diff --git a/resources/win/light/ToggleSwitchOnPressed.png b/resources/win/light/ToggleSwitchOnPressed.png new file mode 100644 index 0000000..4119adb Binary files /dev/null and b/resources/win/light/ToggleSwitchOnPressed.png differ diff --git a/resources/win/light/TreeViewClose.png b/resources/win/light/TreeViewClose.png new file mode 100644 index 0000000..1bf84c8 Binary files /dev/null and b/resources/win/light/TreeViewClose.png differ diff --git a/resources/win/light/TreeViewOpen.png b/resources/win/light/TreeViewOpen.png new file mode 100644 index 0000000..36a00b8 Binary files /dev/null and b/resources/win/light/TreeViewOpen.png differ diff --git a/src/airplaywidget.cpp b/src/airplaywidget.cpp index 08cb4df..6f8f1ad 100644 --- a/src/airplaywidget.cpp +++ b/src/airplaywidget.cpp @@ -57,7 +57,7 @@ #include "diagnosedialog.h" #ifdef WIN32 -#include "platform/windows/check_deps.h" +#include "platform/windows/win_common.h" #endif #include "toolboxwidget.h" diff --git a/src/appswidget.cpp b/src/appswidget.cpp index 9b74183..c0d7388 100644 --- a/src/appswidget.cpp +++ b/src/appswidget.cpp @@ -87,18 +87,23 @@ void AppsWidget::setupUI() QWidget *headerWidget = new QWidget(); headerWidget->setFixedHeight(60); - headerWidget->setStyleSheet("border-bottom: 1px solid #363d32;"); + headerWidget->setStyleSheet( + "border-bottom: 1px solid #363d32; border-radius: 0px;"); QHBoxLayout *headerLayout = new QHBoxLayout(headerWidget); headerLayout->setContentsMargins(20, 10, 20, 10); // Create status label first m_statusLabel = new QLabel("Not signed in"); - m_statusLabel->setStyleSheet("margin-right: 20px;"); + m_statusLabel->setStyleSheet("margin-right: 20px; border: none;"); m_loginButton = new QPushButton(); - m_searchEdit = new ZLineEdit(); + m_searchEdit = new QLineEdit(); +#ifndef WIN32 m_searchEdit->setMaximumWidth(350); +#else + m_searchEdit->setMaximumWidth(200); +#endif // --- Status and Login Button --- m_manager = AppStoreManager::sharedInstance(); @@ -214,7 +219,7 @@ void AppsWidget::handleInit() } /* FIXME: ipatoolinitialze still uses the secure backends - when if the user rejects it, the moment he/she tries to sign in + even if the user rejects it, the moment he/she tries to sign in prompt(keychain or secret-service whatever the backend is) will be seen again */ @@ -262,10 +267,12 @@ void AppsWidget::onAppStoreInitialized(const QJsonObject &accountInfo) } m_loginButton->setText(m_isLoggedIn ? "Sign Out" : "Sign In"); +#ifndef WIN32 m_loginButton->setStyleSheet( "background-color: #007AFF; color: white; border: none; " "border-radius: " "4px; padding: 8px 16px; font-size: 14px;"); +#endif m_searchEdit->setPlaceholderText(m_isLoggedIn ? "Search for apps..." : "Sign in to search"); } @@ -638,8 +645,9 @@ void AppsWidget::createAppCard( ZLabel *installLabel = new ZLabel("Install"); installLabel->setAlignment(Qt::AlignCenter); installLabel->setStyleSheet( - "font-size: 12px; color: #007AFF; font-weight: " - "bold; background-color: transparent;"); + QString("font-size: 12px; color: %1; font-weight: " + "bold; background-color: transparent;") + .arg(COLOR_ACCENT_BLUE.name())); installLabel->setCursor(Qt::PointingHandCursor); installLabel->setFixedHeight(30); diff --git a/src/base/tool.cpp b/src/base/tool.cpp index 05ec12c..1978828 100644 --- a/src/base/tool.cpp +++ b/src/base/tool.cpp @@ -18,8 +18,16 @@ */ #include "tool.h" +#ifdef WIN32 +#include "../platform/windows/widgets/wintoolwidget.h" +#include "../platform/windows/win_common.h" +#endif +#ifdef WIN32 +Tool::Tool(QWidget *parent) : WinToolWidget(parent) +#else Tool::Tool(QWidget *parent) : QWidget(parent) +#endif { #ifdef __APPLE__ setupToolFrame(this); diff --git a/src/base/tool.h b/src/base/tool.h index c92236a..fe0f280 100644 --- a/src/base/tool.h +++ b/src/base/tool.h @@ -26,7 +26,16 @@ #include "../platform/macos/macos.h" #endif +#ifdef WIN32 +#include "../platform/windows/widgets/wintoolwidget.h" +#endif + +#ifdef WIN32 +class Tool : public WinToolWidget +#else class Tool : public QWidget +#endif + { public: explicit Tool(QWidget *parent = nullptr); diff --git a/src/cableinfowidget.cpp b/src/cableinfowidget.cpp index 60db9ef..073c175 100644 --- a/src/cableinfowidget.cpp +++ b/src/cableinfowidget.cpp @@ -81,7 +81,7 @@ void CableInfoWidget::setupUI() m_loadingWidget = new ZLoadingWidget(true, this); m_loadingWidget->setupContentWidget(m_mainLayout); - QVBoxLayout *layout = new QVBoxLayout(this); + QVBoxLayout *layout = new QVBoxLayout(contentWidget()); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_loadingWidget); @@ -230,7 +230,7 @@ void CableInfoWidget::updateUI() if (!m_cableInfo.isConnected) { m_errorLabel->setText( QString("%1 does not seem to be connected to any cable.") - .arg(m_device->deviceInfo.productType)); + .arg(QString::fromStdString(m_device->deviceInfo.productType))); m_loadingWidget->showError(); return; } @@ -243,13 +243,13 @@ void CableInfoWidget::updateUI() "absolute guarantee of authenticity."); if (m_cableInfo.isGenuine) { // todo: type-c to type-c - statusText = QString("✅ Genuine %1") + statusText = QString("Genuine %1") .arg(m_cableInfo.isTypeC ? "USB-C to Lightning Cable" : "Lightning Cable"); statusStyle = "QLabel { color: #28a745; font-size: 18px; font-weight: bold; }"; } else { - statusText = "⚠️ Third-party Cable"; + statusText = "Third-party Cable"; statusStyle = "QLabel { color: #dc3545; font-size: 18px; font-weight: bold; }"; diff --git a/src/core/services/get-device-info.cpp b/src/core/services/get-device-info.cpp index 7c0a029..93368a7 100644 --- a/src/core/services/get-device-info.cpp +++ b/src/core/services/get-device-info.cpp @@ -126,6 +126,7 @@ void get_device_info_xml(const char *udid, LockdowndClientHandle *client, if (xml_string) { infoXml.load_string(xml_string); - free(xml_string); + // FIXME: crashes on Windows + // free(xml_string); } } \ No newline at end of file diff --git a/src/core/services/init_device.cpp b/src/core/services/init_device.cpp index f9c56b8..84c7f4f 100644 --- a/src/core/services/init_device.cpp +++ b/src/core/services/init_device.cpp @@ -26,12 +26,18 @@ #endif #include "../../heartbeat.h" #include + +#ifdef _WIN32 +#include "../../platform/windows/win_common.h" +#else #include #include -#include -#include #include #include +#endif + +#include +#include std::string safeGetXML(const char *key, pugi::xml_node dict) { @@ -453,7 +459,7 @@ void init_idescriptor_device(const iDescriptor::Uniq &uniq, heartbeatThread->start(); while (!heartbeatThread->initialCompleted()) { - sleep(1); + // sleep(1); } } else { diff --git a/src/deviceinfowidget.cpp b/src/deviceinfowidget.cpp index dd0b30f..bb58bf8 100644 --- a/src/deviceinfowidget.cpp +++ b/src/deviceinfowidget.cpp @@ -25,7 +25,6 @@ #include "iDescriptor.h" #include "infolabel.h" #include "privateinfolabel.h" -// #include "toolboxwidget.h" #include #include #include @@ -146,13 +145,14 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent) m_chargingStatusLabel = new QLabel(device->deviceInfo.batteryInfo.isCharging ? "Charging" : "Not Charging"); - - m_chargingStatusLabel->setStyleSheet( - device->deviceInfo.batteryInfo.isCharging - ? QString("color: %1;").arg(COLOR_GREEN.name()) - : QString("color: %1;") - .arg(qApp->palette().color(QPalette::WindowText).name())); - + m_chargingStatusLabel->setObjectName("ChargingStatusLabel"); + m_chargingStatusLabel->setStyleSheet(mergeStyles( + m_chargingStatusLabel, + (device->deviceInfo.batteryInfo.isCharging + ? QString("QLabel#ChargingStatusLabel { color: %1; }") + .arg(COLOR_GREEN.name()) + : QString("QLabel#ChargingStatusLabel { color: %1; }") + .arg(qApp->palette().color(QPalette::WindowText).name())))); // Create the layout without a parent widget QHBoxLayout *chargingLayout = new QHBoxLayout(); chargingLayout->setContentsMargins(0, 0, 0, 0); @@ -314,7 +314,11 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent) for (int i = 0; i < numRows; ++i) { // Left column item QLabel *keyLabelLeft = new QLabel(infoItems[i].first); +#ifndef WIN32 keyLabelLeft->setStyleSheet("font-weight: bold;"); +#else + keyLabelLeft->setStyleSheet("font-size: 15px; font-weight: 500;"); +#endif gridLayout->addWidget(keyLabelLeft, i, 0); gridLayout->addWidget(infoItems[i].second, i, 1); @@ -322,7 +326,11 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent) int rightIndex = i + numRows; if (rightIndex < infoItems.size()) { QLabel *keyLabelRight = new QLabel(infoItems[rightIndex].first); +#ifndef WIN32 keyLabelRight->setStyleSheet("font-weight: bold;"); +#else + keyLabelRight->setStyleSheet("font-size: 15px; font-weight: 500;"); +#endif gridLayout->addWidget(keyLabelRight, i, 2); gridLayout->addWidget(infoItems[rightIndex].second, i, 3); } @@ -348,8 +356,6 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent) rightSideLayout->addWidget(new DiskUsageWidget(device, this)); rightSideLayout->addStretch(); - // // TODO: layout shift cause ? - // // rightSideLayout->setAlignment(Qt::AlignCenter); mainLayout->addLayout(rightSideLayout); mainLayout->addStretch(); @@ -420,4 +426,4 @@ void DeviceInfoWidget::updateChargingStatusIcon() // m_chargingStatusLabel->setStyleSheet(""); // m_lightningIconLabel->hide(); // } -} \ No newline at end of file +} diff --git a/src/devicesidebarwidget.cpp b/src/devicesidebarwidget.cpp index 6aa7c87..e47c4c7 100644 --- a/src/devicesidebarwidget.cpp +++ b/src/devicesidebarwidget.cpp @@ -39,6 +39,7 @@ DeviceSidebarItem::DeviceSidebarItem(const QString &deviceName, setFrameStyle(QFrame::StyledPanel); setLineWidth(1); updateToggleButton(); + setObjectName("DeviceSidebarItem"); } void DeviceSidebarItem::setupUI() @@ -59,7 +60,9 @@ void DeviceSidebarItem::setupUI() QHBoxLayout *nameLayout = new QHBoxLayout(); nameLayout->setContentsMargins(0, 0, 0, 0); m_deviceLabel = new QLabel(m_deviceName); +#ifndef WIN32 m_deviceLabel->setStyleSheet("QLabel { font-weight: bold; }"); +#endif m_deviceLabel->setWordWrap(true); nameLayout->addWidget(m_deviceLabel); if (m_wireless) { @@ -78,6 +81,9 @@ void DeviceSidebarItem::setupUI() m_toggleButton->setStyleSheet("QPushButton { " " text-align: left; " " padding: 2px 5px; " +#ifdef WIN32 + " min-height: 0; " +#endif " border: none; " " color: #666; " " font-size: 11px; " @@ -121,6 +127,9 @@ void DeviceSidebarItem::setupUI() QString("QPushButton { " " background-color: rgba(255, 255, 255, 120); " " border: 1px solid rgba(255, 255, 255, 200); " +#ifdef WIN32 + " min-height: 0; " +#endif " padding: 4px 8px; " " text-align: center; " " border-radius: 6px; " @@ -159,7 +168,20 @@ void DeviceSidebarItem::setupUI() setSelected(false); } -void DeviceSidebarItem::setSelected(bool selected) { m_selected = selected; } +void DeviceSidebarItem::setSelected(bool selected) +{ + m_selected = selected; + + if (selected) { + setStyleSheet( + "QFrame#DeviceSidebarItem { background-color: rgba(255, " + "255, 255, 45); }"); + } else { + setStyleSheet( + "QFrame#DeviceSidebarItem { background-color: rgba(255, " + "255, 255, 16); }"); + } +} void DeviceSidebarItem::setCollapsed(bool collapsed) { @@ -452,68 +474,70 @@ void DevicePendingSidebarItem::mousePressEvent(QMouseEvent *event) } // FIXME: better move this to a separate file -void DeviceSidebarItem::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); +// void DeviceSidebarItem::paintEvent(QPaintEvent *event) +// { +// Q_UNUSED(event); - QPainter p(this); - p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +// QPainter p(this); +// p.setRenderHints(QPainter::Antialiasing | +// QPainter::SmoothPixmapTransform); - const qreal dpr = devicePixelRatioF(); - const int w = width(); - const int h = height(); - const int sw = int(w * dpr); - const int sh = int(h * dpr); +// const qreal dpr = devicePixelRatioF(); +// const int w = width(); +// const int h = height(); +// const int sw = int(w * dpr); +// const int sh = int(h * dpr); - constexpr int kCornerRadius = 10; - constexpr int kBlurRadius = 2; +// constexpr int kCornerRadius = 10; +// constexpr int kBlurRadius = 2; - // Cache the blurred background per size - static QPixmap cachedBg; - static QSize cachedSize; +// // Cache the blurred background per size +// static QPixmap cachedBg; +// static QSize cachedSize; - const QSize cacheSize(sw, sh); - if (cachedSize != cacheSize) { - cachedSize = cacheSize; +// const QSize cacheSize(sw, sh); +// if (cachedSize != cacheSize) { +// cachedSize = cacheSize; - QPixmap gradPm(sw, sh); - gradPm.fill(Qt::transparent); - { - QPainter gp(&gradPm); - gp.setRenderHints(QPainter::Antialiasing | - QPainter::SmoothPixmapTransform); +// QPixmap gradPm(sw, sh); +// gradPm.fill(Qt::transparent); +// { +// QPainter gp(&gradPm); +// gp.setRenderHints(QPainter::Antialiasing | +// QPainter::SmoothPixmapTransform); - QLinearGradient grad(0, 0, 1, 1); - grad.setCoordinateMode(QGradient::ObjectMode); - grad.setStops({{0.0, QColor(255, 255, 255, 255)}, - {0.35, QColor(255, 255, 255, 125)}, - {0.65, QColor(255, 255, 255, 125)}, - {1.0, QColor(255, 255, 255, 255)}}); +// QLinearGradient grad(0, 0, 1, 1); +// grad.setCoordinateMode(QGradient::ObjectMode); +// grad.setStops({{0.0, QColor(255, 255, 255, 255)}, +// {0.35, QColor(255, 255, 255, 125)}, +// {0.65, QColor(255, 255, 255, 125)}, +// {1.0, QColor(255, 255, 255, 255)}}); - gp.fillRect(QRectF(0, 0, sw, sh), grad); - } +// gp.fillRect(QRectF(0, 0, sw, sh), grad); +// } - QPixmap blurred = BackDrop::blurPixmap(gradPm, kBlurRadius); +// QPixmap blurred = BackDrop::blurPixmap(gradPm, kBlurRadius); - QPixmap mask = BackDrop::getColoredPixmap( - QBrush(Qt::white), Qt::white, 1, sw, sh, int(kCornerRadius * dpr)); +// QPixmap mask = BackDrop::getColoredPixmap( +// QBrush(Qt::white), Qt::white, 1, sw, sh, int(kCornerRadius * +// dpr)); - BackDrop::cutPixmap(blurred, mask, sw, sh); +// BackDrop::cutPixmap(blurred, mask, sw, sh); - blurred.setDevicePixelRatio(dpr); - cachedBg = blurred; - } +// blurred.setDevicePixelRatio(dpr); +// cachedBg = blurred; +// } - QPainterPath clipPath; - QRectF r = rect().adjusted(0.5, 0.5, -0.5, -0.5); - clipPath.addRoundedRect(r, kCornerRadius, kCornerRadius); - p.setClipPath(clipPath); - p.drawPixmap(r.toRect(), cachedBg); +// QPainterPath clipPath; +// QRectF r = rect().adjusted(0.5, 0.5, -0.5, -0.5); +// clipPath.addRoundedRect(r, kCornerRadius, kCornerRadius); +// p.setClipPath(clipPath); +// p.drawPixmap(r.toRect(), cachedBg); - p.setClipping(false); - QColor borderColor = m_selected ? COLOR_BLUE : QColor("#FFFFFF"); - QPen pen(borderColor, m_selected ? 2.0 : 1.0); - p.setPen(pen); - p.setBrush(Qt::NoBrush); - p.drawRoundedRect(r, kCornerRadius, kCornerRadius); -} \ No newline at end of file +// p.setClipping(false); +// QColor borderColor = m_selected ? COLOR_BLUE : QColor("#FFFFFF"); +// QPen pen(borderColor, m_selected ? 2.0 : 1.0); +// p.setPen(pen); +// p.setBrush(Qt::NoBrush); +// p.drawRoundedRect(r, kCornerRadius, kCornerRadius); +// } \ No newline at end of file diff --git a/src/devicesidebarwidget.h b/src/devicesidebarwidget.h index 80c62ad..9baa8e8 100644 --- a/src/devicesidebarwidget.h +++ b/src/devicesidebarwidget.h @@ -45,8 +45,8 @@ public: void setCollapsed(bool collapsed); bool isCollapsed() const { return m_collapsed; } -protected: - void paintEvent(QPaintEvent *event) override; + // protected: + // void paintEvent(QPaintEvent *event) override; signals: void deviceSelected(const std::string &uuid); void navigationRequested(const std::string &uuid, const QString §ion); diff --git a/src/diagnosewidget.cpp b/src/diagnosewidget.cpp index 77f9c2f..d2ebd06 100644 --- a/src/diagnosewidget.cpp +++ b/src/diagnosewidget.cpp @@ -19,10 +19,11 @@ #include "diagnosewidget.h" #ifdef WIN32 -#include "platform/windows/check_deps.h" +#include "platform/windows/win_common.h" #include #include #endif +#include "iDescriptor-ui.h" #include #include #include @@ -97,21 +98,37 @@ void DependencyItem::setInstalled(bool installed) if (installed) { if (m_name == "Avahi Daemon") { - m_statusLabel->setText("✓ Activated"); + m_statusLabel->setText("Activated"); } else { - m_statusLabel->setText("✓ Installed"); + m_statusLabel->setText("Installed"); } - m_statusLabel->setStyleSheet("color: green; font-weight: bold;"); +#ifndef WIN32 + m_statusLabel->setStyleSheet("color: green;"); +#else + // FIXME: if we call this multiple times, the styles will keep stacking + // and become a mess, need a better way to handle this + m_statusLabel->setStyleSheet(mergeStyles( + m_statusLabel, + QString("QLabel { color: %1; }").arg(COLOR_GREEN.name()))); +#endif m_installButton->setVisible(false); } else { if (m_name == "Avahi Daemon") { - m_statusLabel->setText("✗ Not activated"); + m_statusLabel->setText("Not activated"); m_installButton->setText("Enable"); } else { - m_statusLabel->setText("✗ Not Installed"); + m_statusLabel->setText("Not Installed"); m_installButton->setText("Install"); } - m_statusLabel->setStyleSheet("color: red; font-weight: bold;"); +#ifndef WIN32 + m_statusLabel->setStyleSheet("color: red;"); +#else + // FIXME: if we call this multiple times, the styles will keep stacking + // and become a mess, need a better way to handle this + m_statusLabel->setStyleSheet(mergeStyles( + m_statusLabel, + QString("QLabel { color: %1; }").arg(COLOR_RED.name()))); +#endif m_installButton->setVisible(true); } } diff --git a/src/diskusagewidget.cpp b/src/diskusagewidget.cpp index a0b631d..bf4face 100644 --- a/src/diskusagewidget.cpp +++ b/src/diskusagewidget.cpp @@ -143,22 +143,25 @@ void DiskUsageWidget::setupUI() // Set colors m_systemBar->setStyleSheet( "QWidget#systemBar { background-color: #a1384d; border: 1px solid" - "#e64a5b; padding: 0; margin: 0; border-top-left-radius: 3px; " + "#e64a5b; padding: 0; margin: 0; border-radius:0px; " + "border-top-left-radius: 3px; " "border-bottom-left-radius: 3px; }"); m_appsBar->setStyleSheet( "QWidget#appsBar { background-color: #4f869f; border: 1px solid " - "#63b4da; padding: 0; margin: 0; }"); + "#63b4da; border-radius:0px; padding: 0; margin: 0; }"); m_mediaBar->setStyleSheet("QWidget#mediaBar { background-color: #2ECC71; " "border: none; padding: 0; margin: 0; }"); m_galleryBar->setStyleSheet( "QWidget#galleryBar { background-color: #9b59b6; border: 1px solid " - "#8e44ad; padding: 0; margin: 0; }"); + "#8e44ad; border-radius:0px; padding: 0; margin: 0; }"); m_othersBar->setStyleSheet( "QWidget#othersBar { background-color: #a28729; border: 1px solid " - "#c4a32d; padding: 0; margin: 0; }"); + "#c4a32d; border-radius:0px; padding: 0; margin: 0; }"); m_freeBar->setStyleSheet( - "QWidget#freeBar { background-color: #474747; border: 1px solid " - "#4f4f4f; padding: 0; margin: 0; border-top-right-radius: 3px; " + "QWidget#freeBar { background-color: rgba(255, 255, 255, 10); border: " + "1px solid " + "#4f4f4f4f; padding: 0; margin: 0; border-radius:0px; " + "border-top-right-radius: 3px; " "border-bottom-right-radius: 3px; }"); // remove padding margin from layout diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 2af451c..852ac2c 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -239,12 +239,12 @@ QString HttpServer::generateJsonManifest() const QString HttpServer::getLocalIP() const { - foreach (const QNetworkInterface &interface, + foreach (const QNetworkInterface &netIf, QNetworkInterface::allInterfaces()) { - if (interface.flags().testFlag(QNetworkInterface::IsUp) && - !interface.flags().testFlag(QNetworkInterface::IsLoopBack)) { + if (netIf.flags().testFlag(QNetworkInterface::IsUp) && + !netIf.flags().testFlag(QNetworkInterface::IsLoopBack)) { foreach (const QNetworkAddressEntry &entry, - interface.addressEntries()) { + netIf.addressEntries()) { if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { return entry.ip().toString(); } diff --git a/src/iDescriptor-ui.h b/src/iDescriptor-ui.h index d5a3708..eb30f9a 100644 --- a/src/iDescriptor-ui.h +++ b/src/iDescriptor-ui.h @@ -40,13 +40,35 @@ #ifdef __APPLE__ #include "./platform/macos/macos.h" +#elif defined(WIN32) +#include "./platform/windows/win_common.h" #endif #define COLOR_GREEN QColor(0, 180, 0) // Green #define COLOR_ORANGE QColor(255, 140, 0) // Orange #define COLOR_RED QColor(255, 0, 0) // Red #define COLOR_BLUE QColor("#2b5693") + +#ifndef WIN32 #define COLOR_ACCENT_BLUE QColor("#0b5ed7") +#define COLOR_HYPERLINK QColor(0, 122, 255) +#else +#define COLOR_ACCENT_BLUE QColor(getWindowsAccentColor()) +#define COLOR_HYPERLINK QColor("#FF7FFFD4") +#endif + +inline QString mergeStyles(QWidget *widget, const QString &newStyles) +{ + if (!widget) { + return newStyles; + } + QString existing = widget->styleSheet(); + if (existing.isEmpty()) + return newStyles; + + return existing + "\n" + newStyles; +} + #define MIN_MAIN_WINDOW_SIZE QSize(900, 600) class ResponsiveGraphicsView : public QGraphicsView diff --git a/src/iDescriptor.h b/src/iDescriptor.h index 341df16..c8dd97b 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -355,7 +355,7 @@ public: plist_get_string_val(current_node, &value); std::string result = value ? value : ""; if (value) - free(value); + plist_mem_free(value); return result; } plist_t getNode() const { return current_node; } @@ -605,10 +605,12 @@ inline void free_directory_listing(char **entries, size_t count) return; for (size_t i = 0; i < count; i++) { if (entries[i]) { - free(entries[i]); + // FIXME: crashes on Windows + // free(entries[i]); } } - free(entries); + // FIXME: crashes on Windows + // free(entries); } inline int read_file(const char *filename, uint8_t **data, size_t *length) diff --git a/src/ifusewidget.cpp b/src/ifusewidget.cpp index 10f0928..28067c3 100644 --- a/src/ifusewidget.cpp +++ b/src/ifusewidget.cpp @@ -30,7 +30,7 @@ #include #include #ifdef WIN32 -#include "platform/windows/check_deps.h" +#include "platform/windows/win_common.h" #endif iFuseWidget::iFuseWidget(iDescriptorDevice *device, QWidget *parent) diff --git a/src/infolabel.cpp b/src/infolabel.cpp index ef5f411..95af921 100644 --- a/src/infolabel.cpp +++ b/src/infolabel.cpp @@ -29,9 +29,7 @@ InfoLabel::InfoLabel(const QString &text, const QString &textToCopy, m_textToCopy(!textToCopy.isEmpty() ? textToCopy : text) { setCursor(Qt::PointingHandCursor); - setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, 0.1); " - "border-radius: 2px; }"); - + setStyleSheet(m_style); m_restoreTimer = new QTimer(this); m_restoreTimer->setSingleShot(true); connect(m_restoreTimer, &QTimer::timeout, this, @@ -49,39 +47,26 @@ void InfoLabel::mousePressEvent(QMouseEvent *event) // prevent layout shifts setMinimumWidth(originalWidth); setText("Copied!"); +#ifdef WIN32 + setStyleSheet(QStringLiteral( + "QLabel { color: #4CAF50; font-weight: bold; font-size: 14px; }" + "QLabel:hover { background-color: rgba(255, 255, 255, 0.1); " + "border-radius: 2px; }")); +#else setStyleSheet("QLabel { color: #4CAF50; font-weight: bold; } " "QLabel:hover { background-color: rgba(255, 255, 255, " "0.1); border-radius: 2px; }"); - +#endif m_restoreTimer->start(1000); // Show "Copied!" for 1 second } QLabel::mousePressEvent(event); } -void InfoLabel::enterEvent(QEnterEvent *event) -{ - if (!m_restoreTimer->isActive()) { - setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, " - "0.1); border-radius: 2px; }"); - } - QLabel::enterEvent(event); -} - -void InfoLabel::leaveEvent(QEvent *event) -{ - if (!m_restoreTimer->isActive()) { - setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, " - "0.1); border-radius: 2px; }"); - } - QLabel::leaveEvent(event); -} - void InfoLabel::restoreOriginalText() { setText(m_originalText); setMinimumWidth(0); - setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, 0.1); " - "border-radius: 2px; }"); + setStyleSheet(m_style); } void InfoLabel::setOriginalText(const QString &text) { m_originalText = text; } diff --git a/src/infolabel.h b/src/infolabel.h index 58f505b..01aa217 100644 --- a/src/infolabel.h +++ b/src/infolabel.h @@ -21,6 +21,7 @@ #define INFOLABEL_H #include +#include #include class InfoLabel : public QLabel @@ -37,8 +38,6 @@ public: protected: void mousePressEvent(QMouseEvent *event) override; - void enterEvent(QEnterEvent *event) override; - void leaveEvent(QEvent *event) override; private slots: void restoreOriginalText(); @@ -47,6 +46,18 @@ private: QString m_originalText; QString m_textToCopy; QTimer *m_restoreTimer; + QString m_style = +#ifdef WIN32 + QStringLiteral( + "QLabel:hover { background-color: rgba(255, 255, 255, 0.1); " + "border-radius: 2px; }" + "QLabel { " + "font-size: 14px;}"); +#else + QStringLiteral( + "QLabel:hover { background-color: rgba(255, 255, 255, 0.1); " + "border-radius: 2px; }"); +#endif }; #endif // INFOLABEL_H \ No newline at end of file diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp index 4c4265e..24ccbbb 100644 --- a/src/installedappswidget.cpp +++ b/src/installedappswidget.cpp @@ -322,7 +322,7 @@ void InstalledAppsWidget::fetchInstalledApps() plist_get_string_val(bundle_id, &bundle_id_str); if (bundle_id_str) { appData["bundleId"] = QString(bundle_id_str); - free(bundle_id_str); + plist_mem_free(bundle_id_str); } } @@ -337,7 +337,7 @@ void InstalledAppsWidget::fetchInstalledApps() if (display_name_str) { appData["displayName"] = QString(display_name_str); - free(display_name_str); + plist_mem_free(display_name_str); } } @@ -350,7 +350,7 @@ void InstalledAppsWidget::fetchInstalledApps() plist_get_string_val(version, &version_str); if (version_str) { appData["version"] = QString(version_str); - free(version_str); + plist_mem_free(version_str); } } diff --git a/src/logindialog.cpp b/src/logindialog.cpp index 5ff6980..f886dad 100644 --- a/src/logindialog.cpp +++ b/src/logindialog.cpp @@ -30,12 +30,20 @@ #include #include +#ifdef WIN32 +#include "platform/windows/win_common.h" +#endif + LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent) { setWindowTitle("Login to App Store - iDescriptor"); setModal(true); setFixedWidth(400); +#ifdef WIN32 + setupWinWindow(this); +#endif + QVBoxLayout *layout = new QVBoxLayout(this); layout->setSpacing(15); layout->setContentsMargins(20, 20, 20, 20); @@ -47,8 +55,11 @@ LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent) m_emailEdit = new QLineEdit(); m_emailEdit->setPlaceholderText("Enter your email"); +#ifndef WIN32 m_emailEdit->setStyleSheet("padding: 8px; border: 1px solid #ddd; " "border-radius: 4px; font-size: 14px;"); +#endif + layout->addWidget(m_emailEdit); // Password @@ -59,33 +70,35 @@ LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent) m_passwordEdit = new QLineEdit(); m_passwordEdit->setPlaceholderText("Enter your password"); m_passwordEdit->setEchoMode(QLineEdit::Password); +#ifndef WIN32 m_passwordEdit->setStyleSheet("padding: 8px; border: 1px solid #ddd; " "border-radius: 4px; font-size: 14px;"); +#endif layout->addWidget(m_passwordEdit); // Description - QLabel *descriptionLabel = - new QLabel("Don't worry, your credentials won't be " - "stored or shared anywhere. This App is open-source."); + QLabel *descriptionLabel = new QLabel( + "You shouldn't be using your main account here and don't worry, " + "your credentials won't be " + "stored or shared anywhere. This App is open-source."); descriptionLabel->setStyleSheet("font-size: 10px; font-weight: thin;"); descriptionLabel->setAlignment(Qt::AlignLeft); - descriptionLabel->setWordWrap(true); // Add this line + descriptionLabel->setWordWrap(true); layout->addWidget(descriptionLabel); - // --- Buttons and Indicator --- - // Create a container widget for the sign-in button and the indicator QWidget *signInContainer = new QWidget(this); m_signInStackedLayout = new QStackedLayout(signInContainer); m_signInStackedLayout->setContentsMargins(0, 0, 0, 0); // Create the actual "Sign In" button m_signInButton = new QPushButton("Sign In"); +#ifndef WIN32 m_signInButton->setStyleSheet( "QPushButton { padding: 8px 16px; font-size: 14px; border-radius: 4px; " "background-color: #007AFF; color: white; border: none; min-width: " "80px; }" "QPushButton:hover { background-color: #0056CC; }"); - +#endif // Create the indicator QWidget *indicatorWidget = new QWidget(); QVBoxLayout *indicatorLayout = new QVBoxLayout(indicatorWidget); @@ -103,15 +116,15 @@ LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent) // Ensure the container has the same size as the button signInContainer->setFixedSize(m_signInButton->sizeHint()); - // Create the "Cancel" button m_cancelButton = new QPushButton("Cancel"); - // add disabled style to cancel button +#ifndef WIN32 m_cancelButton->setStyleSheet( "QPushButton { padding: 8px 16px; font-size: 14px; border-radius: 4px; " "background-color: #f0f0f0; color: #333; border: 1px solid #ddd; " "min-width: 80px; }" "QPushButton:disabled { background-color: #eee; color: #aaa; border: " "1px solid #ddd; }"); +#endif // Layout for the buttons QHBoxLayout *buttonLayout = new QHBoxLayout(); diff --git a/src/main.cpp b/src/main.cpp index 133ae06..4f0cf43 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,14 +23,15 @@ #include #include #include +#include #include #include #include #include - #ifdef WIN32 -#include "platform/windows/check_deps.h" +#include "platform/windows/win_common.h" #endif + int main(int argc, char *argv[]) { QApplication a(argc, argv); @@ -45,6 +46,15 @@ int main(int argc, char *argv[]) // " "their default values."); // } #ifdef WIN32 + QFile styleFile(detectDarkModeWindows() ? ":/resources/win.dark.qcss" + : ":/resources/win.light.qcss"); + if (styleFile.open(QFile::ReadOnly | QFile::Text)) { + const QString style = QString::fromUtf8(styleFile.readAll()) + .arg(COLOR_ACCENT_BLUE.name()); + qDebug() << "Loaded Windows style sheet successfully."; + a.setStyleSheet(style); + } + QString appPath = QCoreApplication::applicationDirPath(); QString gstPluginPath = QDir::toNativeSeparators(appPath + "/gstreamer-1.0"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7687516..80c1fa9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -18,7 +18,6 @@ */ #include "mainwindow.h" -#include "./ui_mainwindow.h" #include "appswidget.h" #include "devicemanagerwidget.h" #include "iDescriptor-ui.h" @@ -26,9 +25,6 @@ #include "ifusediskunmountbutton.h" #include "ifusemanager.h" #include "jailbrokenwidget.h" -// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT -// #include "libirecovery.h" -// #endif #include "toolboxwidget.h" #include "welcomewidget.h" #include @@ -51,112 +47,38 @@ #include #include #include - #ifdef WIN32 -#include "platform/windows/check_deps.h" +#include "platform/windows/win_common.h" #endif using namespace IdeviceFFI; -// void handleCallback(const idevice_event_t *event, void *userData) -// { -// printf("Device event received: "); - -// switch (event->event) { -// case IDEVICE_DEVICE_ADD: { -// /* this should never happen iDescriptor does not support network -// devices but for some reason even though we are only listening for USB -// devices, we still get network devices on macOS*/ if (event->conn_type -// == CONNECTION_NETWORK) { -// return; -// } -// qDebug() << "Device added: " << QString::fromUtf8(event->udid); - -// QMetaObject::invokeMethod( -// AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection, -// Q_ARG(QString, QString::fromUtf8(event->udid)), -// Q_ARG(idevice_connection_type, event->conn_type), -// Q_ARG(AddType, AddType::Regular)); -// break; -// } - -// case IDEVICE_DEVICE_REMOVE: { -// QMetaObject::invokeMethod(AppContext::sharedInstance(), -// "removeDevice", -// Qt::QueuedConnection, -// Q_ARG(QString, QString(event->udid))); -// break; -// } - -// case IDEVICE_DEVICE_PAIRED: { -// if (event->conn_type == CONNECTION_NETWORK) { -// qDebug() -// << "Network devices are not supported but a network device -// was " -// "received in event listener. Please report this issue."; -// return; -// } -// qDebug() << "Device paired: " << QString::fromUtf8(event->udid); - -// QMetaObject::invokeMethod( -// AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection, -// Q_ARG(QString, QString::fromUtf8(event->udid)), -// Q_ARG(idevice_connection_type, event->conn_type), -// Q_ARG(AddType, AddType::Pairing)); -// break; -// } -// default: -// qDebug() << "Unhandled event: " << event->event; -// } -// } - -// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT -// void handleCallbackRecovery(const irecv_device_event_t *event, void -// *userData) -// { - -// switch (event->type) { -// case IRECV_DEVICE_ADD: -// qDebug() << "Recovery device added: "; -// QMetaObject::invokeMethod(AppContext::sharedInstance(), -// "addRecoveryDevice", Qt::QueuedConnection, -// Q_ARG(uint64_t, event->device_info->ecid)); -// break; -// case IRECV_DEVICE_REMOVE: -// qDebug() << "Recovery device removed: "; -// QMetaObject::invokeMethod(AppContext::sharedInstance(), -// "removeRecoveryDevice", -// Qt::QueuedConnection, Q_ARG(uint64_t, -// event->device_info->ecid)); -// break; -// default: -// printf("Unhandled recovery event: %d\n", event->type); -// } -// } -// irecv_device_event_context_t context; -// #endif - MainWindow *MainWindow::sharedInstance() { static MainWindow instance; return &instance; } -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), ui(new Ui::MainWindow) +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { - ui->setupUi(this); setMinimumSize(MIN_MAIN_WINDOW_SIZE); resize(MIN_MAIN_WINDOW_SIZE); - m_ZTabWidget = new ZTabWidget(this); - m_ZTabWidget->setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea, false); - setContentsMargins(0, 0, 0, 0); + + QWidget *centralWidget = new QWidget(this); + setCentralWidget(centralWidget); + auto mainLayout = new QVBoxLayout(centralWidget); + mainLayout->setContentsMargins(0, 0, 0, 0); + + m_ZTabWidget = new ZTabWidget(this); #ifdef __APPLE__ setupMacOSWindow(this); setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea, false); #endif - setCentralWidget(m_ZTabWidget); + mainLayout->addWidget(m_ZTabWidget); +#ifdef WIN32 + setupWinWindow(this); +#endif m_mainStackedWidget = new QStackedWidget(); WelcomeWidget *welcomePage = new WelcomeWidget(this); @@ -200,25 +122,35 @@ MainWindow::MainWindow(QWidget *parent) m_connectedDeviceCountLabel->setStyleSheet( "QLabel:hover { background-color : #13131319; }"); - ui->statusbar->addWidget(m_connectedDeviceCountLabel); + QWidget *statusbar = new QWidget(); + QHBoxLayout *statusLayout = new QHBoxLayout(statusbar); + statusLayout->setContentsMargins(0, 0, 0, 0); + statusbar->setObjectName("StatusBar"); + statusbar->setStyleSheet( + "QWidget#StatusBar { background-color: transparent; }"); + statusLayout->addWidget(m_connectedDeviceCountLabel); // TODO: implement downloads/uploads progress stuff StatusBalloon *statusBalloon = StatusBalloon::sharedInstance(); - ui->statusbar->addWidget(statusBalloon->getButton()); + statusLayout->addWidget(statusBalloon->getButton()); + statusLayout->addStretch(1); - ui->statusbar->setContentsMargins(0, 0, 0, 0); + statusLayout->setContentsMargins(0, 0, 0, 0); QLabel *appVersionLabel = new QLabel(QString("v%1").arg(APP_VERSION)); appVersionLabel->setContentsMargins(5, 0, 5, 0); appVersionLabel->setStyleSheet( "QLabel:hover { background-color : #13131319; }"); - ui->statusbar->addPermanentWidget(appVersionLabel); - ui->statusbar->addPermanentWidget(githubButton); - ui->statusbar->addPermanentWidget(settingsButton); -#ifdef WIN32 - ui->statusbar->setStyleSheet( - "QStatusBar { border-top: 1px solid #dcdcdc; }"); -#endif + statusLayout->addWidget(appVersionLabel); + statusLayout->addWidget(githubButton); + statusLayout->addWidget(settingsButton); + // #ifdef WIN32 + // statusLayout->setStyleSheet("QStatusBar { border-top: 1px solid + // #dcdcdc; + // }"); + // #endif + + mainLayout->addWidget(statusbar); #ifdef __linux__ QList mounted_iFusePaths = iFuseManager::getMountPoints(); @@ -226,7 +158,7 @@ MainWindow::MainWindow(QWidget *parent) for (const QString &path : mounted_iFusePaths) { auto *p = new iFuseDiskUnmountButton(path); - ui->statusbar->addPermanentWidget(p); + statusbar->addWidget(p); connect(p, &iFuseDiskUnmountButton::clicked, this, [this, p, path]() { bool ok = iFuseManager::linuxUnmount(path); if (!ok) { @@ -235,7 +167,7 @@ MainWindow::MainWindow(QWidget *parent) ". Please try again."); return; } - ui->statusbar->removeWidget(p); + statusbar->removeWidget(p); p->deleteLater(); }); } @@ -521,7 +453,6 @@ MainWindow::~MainWindow() // #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT // irecv_device_event_unsubscribe(context); // #endif - delete ui; m_deviceMonitor->requestInterruption(); // FIXME:QThread: Destroyed while thread '' is still running // m_deviceMonitor->wait(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 85d1bbb..8f23da0 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -22,23 +22,13 @@ #include "ZDownloader.h" #include "ZUpdater.h" #include "devicemanagerwidget.h" -#include "iDescriptor.h" -#include -// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT -// #include "libirecovery.h" -// #endif #include "devicemonitor.h" +#include "iDescriptor.h" #include "ztabwidget.h" #include +#include #include -QT_BEGIN_NAMESPACE -namespace Ui -{ -class MainWindow; -} -QT_END_NAMESPACE - class MainWindow : public QMainWindow { Q_OBJECT @@ -54,11 +44,17 @@ public slots: private: void createMenus(); - Ui::MainWindow *ui; ZTabWidget *m_ZTabWidget; DeviceManagerWidget *m_deviceManager; QStackedWidget *m_mainStackedWidget; QLabel *m_connectedDeviceCountLabel; DeviceMonitorThread *m_deviceMonitor; + QLabel *m_titleLabel; + QPushButton *m_minBtn; + QPushButton *m_maxBtn; + QPushButton *m_closeBtn; + QWidget *m_titleBar; + QWidget *m_contentArea; + QHBoxLayout *m_titleBarLayout; }; #endif // MAINWINDOW_H diff --git a/src/platform/windows/blur_imp.cpp b/src/platform/windows/blur_imp.cpp new file mode 100644 index 0000000..aa3698e --- /dev/null +++ b/src/platform/windows/blur_imp.cpp @@ -0,0 +1,101 @@ +/* + * iDescriptor: A free and open-source idevice management tool. + * + * Copyright (C) 2025 Uncore + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "../../settingsmanager.h" +#include "win_common.h" + +void enableAcrylic(HWND hwnd) +{ + const HINSTANCE hModule = LoadLibraryA("user32.dll"); + if (hModule) { + struct ACCENTPOLICY { + int nAccentState; + int nFlags; + DWORD nColor; + int nAnimationId; + }; + struct WINCOMPATTRDATA { + int nAttribute; + PVOID pData; + ULONG ulDataSize; + }; + + typedef BOOL(WINAPI * + pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA *); + const auto SetWindowCompositionAttribute = + (pSetWindowCompositionAttribute)GetProcAddress( + hModule, "SetWindowCompositionAttribute"); + + if (SetWindowCompositionAttribute) { + ACCENTPOLICY policy{}; + policy.nAccentState = 4; // ACCENT_ENABLE_ACRYLICBLURBEHIND + policy.nFlags = 2; + + policy.nColor = 0xD0202020; + + WINCOMPATTRDATA data{}; + data.nAttribute = 19; + data.pData = &policy; + data.ulDataSize = sizeof(policy); + + SetWindowCompositionAttribute(hwnd, &data); + } + FreeLibrary(hModule); + } +} + +#ifndef DWMWA_SYSTEMBACKDROP_TYPE +#define DWMWA_SYSTEMBACKDROP_TYPE 38 +#endif + +#ifndef DWMWA_MICA_EFFECT +#define DWMWA_MICA_EFFECT 1029 +#endif +void enableMica(HWND hwnd) +{ + if (!hwnd) + return; + SettingsManager *sm = SettingsManager::sharedInstance(); + WIN_BACKDROP type = sm->winBackdropType(); + MARGINS margins = {-1}; + DwmExtendFrameIntoClientArea(hwnd, &margins); + + DWORD build = 0; + RTL_OSVERSIONINFOW rovi = {0}; + rovi.dwOSVersionInfoSize = sizeof(rovi); + + using RtlGetVersionPtr = LONG(WINAPI *)(PRTL_OSVERSIONINFOW); + HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); + if (ntdll) { + auto pRtlGetVersion = reinterpret_cast( + GetProcAddress(ntdll, "RtlGetVersion")); + if (pRtlGetVersion && pRtlGetVersion(&rovi) == 0) { + build = rovi.dwBuildNumber; + } + } + + if (build >= 22523) { + DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &type, + sizeof(type)); + } else if (build >= 22000) { + // Undocumented old method + BOOL mica = TRUE; + DwmSetWindowAttribute(hwnd, DWMWA_MICA_EFFECT, &mica, sizeof(mica)); + } +} diff --git a/src/platform/windows/check_deps.cpp b/src/platform/windows/check_deps.cpp index b2a1062..7d8b082 100644 --- a/src/platform/windows/check_deps.cpp +++ b/src/platform/windows/check_deps.cpp @@ -17,8 +17,8 @@ * along with this program. If not, see . */ +#include "win_common.h" #include -#include bool CheckRegistry(HKEY hKeyRoot, LPCSTR subKey, LPCSTR displayNameToFind) { diff --git a/src/platform/windows/check_deps.h b/src/platform/windows/check_deps.h deleted file mode 100644 index df85ea2..0000000 --- a/src/platform/windows/check_deps.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * iDescriptor: A free and open-source idevice management tool. - * - * Copyright (C) 2025 Uncore - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef CHECK_DEPS_H -#define CHECK_DEPS_H - -bool IsAppleMobileDeviceSupportInstalled(); -bool IsWinFspInstalled(); -bool is_iDescriptorInstalled(); -bool IsBonjourServiceInstalled(); - -#endif // CHECK_DEPS_H \ No newline at end of file diff --git a/src/platform/windows/setuptitlebar.cpp b/src/platform/windows/setuptitlebar.cpp new file mode 100644 index 0000000..d344c87 --- /dev/null +++ b/src/platform/windows/setuptitlebar.cpp @@ -0,0 +1,33 @@ +#include "win_common.h" + +// TODO: remove +void setupTitleBar(HWND hwnd) +{ + if (!hwnd) + return; + + LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE); + if (style) { + style &= ~WS_THICKFRAME; // disable resizing + // Keep WS_CAPTION, WS_SYSMENU, WS_MINIMIZEBOX, WS_MAXIMIZEBOX + SetWindowLongPtr(hwnd, GWL_STYLE, style); + } + + SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + + // Clear title text and icon + SetWindowTextW(hwnd, L""); + SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) nullptr); + SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) nullptr); + + // Make the title bar transparent (extend frame into client area) + MARGINS margins = {0, 0, 0, 0}; + DwmExtendFrameIntoClientArea(hwnd, &margins); + + // Disable the DWM caption drawing so the title bar has no background + BOOL value = TRUE; + const DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_ATTR = 20; + DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE_ATTR, &value, + sizeof(value)); +} \ No newline at end of file diff --git a/src/platform/windows/widgets/wintoolwidget.cpp b/src/platform/windows/widgets/wintoolwidget.cpp new file mode 100644 index 0000000..9b8567d --- /dev/null +++ b/src/platform/windows/widgets/wintoolwidget.cpp @@ -0,0 +1,361 @@ +/* + * iDescriptor: A free and open-source idevice management tool. + * + * Copyright (C) 2025 Uncore + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "wintoolwidget.h" +#include "../win_common.h" +#include +#include +#include +#include +#include +#include +#include // GET_X_LPARAM, GET_Y_LPARAM + +static const int TITLE_BAR_HEIGHT = 32; +// FIXME: custom title bar functionality +// doesnt work properly with Mica effetcs applied, need to investigate further +WinToolWidget::WinToolWidget(QWidget *parent) + : QWidget(parent), m_min_btn{nullptr}, m_max_btn{nullptr}, + m_close_btn{nullptr}, m_resize_border_width{6} +{ + + m_hwnd = (HWND)winId(); + setupWinWindow(this); + + QObject::connect(windowHandle(), &QWindow::screenChanged, this, + &WinToolWidget::onScreenChanged); + + // Add widget. (Initialize central widget) + QWidget *entire_widget = this; + setContentsMargins(0, 0, 0, 0); + + // Layout for entire widgets. + QVBoxLayout *entire_layout = new QVBoxLayout(this); + entire_widget->setLayout(entire_layout); + entire_layout->setContentsMargins(0, 0, 0, 0); + entire_layout->setSpacing(0); + + // Initialize title bar widget + m_titlebar_widget = new QWidget(this); + entire_layout->addWidget(m_titlebar_widget); + m_titlebar_widget->setFixedHeight(35); // Default title bar height is 35 + m_titlebar_widget->setContentsMargins(0, 0, 0, 0); + m_titlebar_widget->setSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Fixed); + + // Layout for title bar + QHBoxLayout *titlebar_layout = new QHBoxLayout(this); + m_titlebar_widget->setLayout(titlebar_layout); + titlebar_layout->setContentsMargins(0, 0, 0, 0); + titlebar_layout->setSpacing(0); + + QWidget *custom_titlebar_widget = new QWidget(this); + titlebar_layout->addWidget(custom_titlebar_widget); + custom_titlebar_widget->setContentsMargins(0, 0, 0, 0); + custom_titlebar_widget->setSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Expanding); + + // Caption buttons – sized like Windows (46×32 for min/max, 46×32 for close) + auto makeBtn = [&](const QString &icon, const QString &normalBg, + const QString &hoverBg, + const QString &pressBg) -> QPushButton * { + auto *btn = new QPushButton(icon, this); + btn->setFixedSize(46, TITLE_BAR_HEIGHT); + btn->setFlat(true); + btn->setFocusPolicy(Qt::NoFocus); + btn->setCursor(Qt::ArrowCursor); + btn->setStyleSheet( + QString("QPushButton { background:%1; color:white; border:none;" + " font-size:10px; font-family:'Segoe MDL2 " + "Assets','Segoe UI Symbol'; }" + "QPushButton:hover { background:%2; }" + "QPushButton:pressed{ background:%3; }") + .arg(normalBg, hoverBg, pressBg)); + return btn; + }; + + // U+E921 minimize, U+E922 restore/maximize, U+E8BB close (Segoe MDL2 + // Assets) + m_min_btn = makeBtn("\uE921", "transparent", "rgba(255,255,255,30)", + "rgba(255,255,255,20)"); + m_max_btn = makeBtn("\uE922", "transparent", "rgba(255,255,255,30)", + "rgba(255,255,255,20)"); + m_close_btn = makeBtn("\uE8BB", "transparent", "#C42B1C", "#9B1B0F"); + titlebar_layout->addWidget(m_min_btn); + titlebar_layout->addWidget(m_max_btn); + titlebar_layout->addWidget(m_close_btn); + + // Layout for title bar customization. + m_custom_titlebar_layout = new QHBoxLayout(custom_titlebar_widget); + custom_titlebar_widget->setLayout(m_custom_titlebar_layout); + m_custom_titlebar_layout->setContentsMargins(0, 0, 0, 0); + m_custom_titlebar_layout->setSpacing(0); + m_custom_titlebar_layout->setAlignment(Qt::AlignLeft); + + QObject::connect(m_min_btn, &QPushButton::clicked, this, + &WinToolWidget::onMinimizeButtonClicked); + QObject::connect(m_max_btn, &QPushButton::clicked, this, + &WinToolWidget::onMaximizeButtonClicked); + QObject::connect(m_close_btn, &QPushButton::clicked, this, + &WinToolWidget::onCloseButtonClicked); + entire_layout->setAlignment(titlebar_layout, Qt::AlignTop); + + m_content_widget = new QWidget(this); + entire_layout->addWidget(m_content_widget); + // m_content_layout = new QVBoxLayout(m_content_widget); + m_content_widget->setContentsMargins(0, 0, 0, 0); + m_content_widget->setSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Expanding); + + // Set default title bar palette. + auto pal = m_titlebar_widget->palette(); + pal.setColor(QPalette::Window, QColor(30, 34, 39)); + m_titlebar_widget->setAutoFillBackground(true); + m_titlebar_widget->setPalette(pal); + + // Set default content widget palette. + pal = m_content_widget->palette(); + pal.setColor(QPalette::Window, QColor(35, 39, 46)); + m_content_widget->setAutoFillBackground(true); + m_content_widget->setPalette(pal); +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +bool WinToolWidget::nativeEvent(const QByteArray &event_type, void *message, + long *result) +#else +bool WinToolWidget::nativeEvent(const QByteArray &event_type, void *message, + qintptr *result) +#endif +{ + MSG *msg = (MSG *)message; + + switch (msg->message) { + // Remove the default window frame by hooking the WM_NCCALCSIZE message. + case WM_NCCALCSIZE: { + if (msg->lParam) { + WINDOWPLACEMENT wp; + GetWindowPlacement(m_hwnd, &wp); + + if (wp.showCmd == SW_MAXIMIZE) { + NCCALCSIZE_PARAMS *sz = (NCCALCSIZE_PARAMS *)msg->lParam; + sz->rgrc[0].left += 8; + sz->rgrc[0].top += 8; + sz->rgrc[0].right -= 8; + sz->rgrc[0].bottom -= 8; + } + } + return true; + } + + // Process the mouse when it is on the window border. + case WM_NCHITTEST: { + RECT winrect; + GetWindowRect(msg->hwnd, &winrect); + long x = GET_X_LPARAM(msg->lParam); + long y = GET_Y_LPARAM(msg->lParam); + long local_x = x - winrect.left; + long local_y = y - winrect.top; + + if (x >= winrect.left && x < winrect.left + m_resize_border_width && + y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) { + *result = HTBOTTOMLEFT; + return true; + } + + if (x < winrect.right && x >= winrect.right - m_resize_border_width && + y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) { + *result = HTBOTTOMRIGHT; + return true; + } + + if (x >= winrect.left && x < winrect.left + m_resize_border_width && + y >= winrect.top && y < winrect.top + m_resize_border_width) { + *result = HTTOPLEFT; + return true; + } + + if (x < winrect.right && x >= winrect.right - m_resize_border_width && + y >= winrect.top && y < winrect.top + m_resize_border_width) { + *result = HTTOPRIGHT; + return true; + } + + if (x >= winrect.left && x < winrect.left + m_resize_border_width) { + *result = HTLEFT; + return true; + } + + if (x < winrect.right && x >= winrect.right - m_resize_border_width) { + *result = HTRIGHT; + return true; + } + + if (y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) { + *result = HTBOTTOM; + return true; + } + + if (y >= winrect.top && y < winrect.top + m_resize_border_width) { + *result = HTTOP; + return true; + } + + // Check the area where the user can click to move the window. + if (determineNonClickableWidgetUnderMouse(m_custom_titlebar_layout, + local_x, local_y)) { + *result = HTCAPTION; + return true; + } + + *result = HTTRANSPARENT; + break; + } + case WM_SIZE: { + if (m_max_btn) { + WINDOWPLACEMENT wp; + GetWindowPlacement(m_hwnd, &wp); + m_max_btn->setChecked(wp.showCmd == SW_MAXIMIZE ? true : false); + } + break; + } + default: + break; + } + + return false; +} + +// This is used to change the `active` state of widgets in custom title bar. +bool WinToolWidget::event(QEvent *evt) +{ + switch (evt->type()) { + case QEvent::WindowActivate: { +#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) + m_close_btn->setStyleSheet(m_close_btn->styleSheet()); + m_min_btn->setStyleSheet(m_min_btn->styleSheet()); + m_max_btn->setStyleSheet(m_max_btn->styleSheet()); +#endif + propagateActiveStateInCustomTitlebar(m_custom_titlebar_layout, true); + break; + } + + case QEvent::WindowDeactivate: { +#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) + m_close_btn->setStyleSheet(m_close_btn->styleSheet()); + m_min_btn->setStyleSheet(m_min_btn->styleSheet()); + m_max_btn->setStyleSheet(m_max_btn->styleSheet()); +#endif + propagateActiveStateInCustomTitlebar(m_custom_titlebar_layout, false); + break; + } + + default: + break; + } + + return QWidget::event(evt); +} + +// Determine whether the current mouse coordinate is on the non-clickable widget +// or not using a recursive method. +bool WinToolWidget::determineNonClickableWidgetUnderMouse(QLayout *layout, + int x, int y) +{ + if (!layout->count() && layout->geometry().contains(x, y)) + return true; + + for (size_t i = 0; i < layout->count(); i++) { + auto item = layout->itemAt(i)->widget(); + if (item) { + if (item->geometry().contains(x, y)) + return !item->property("clickable widget").toBool(); + } else { + auto child_layout = layout->itemAt(i)->layout(); + if (child_layout && child_layout->geometry().contains(x, y)) + return determineNonClickableWidgetUnderMouse(child_layout, x, + y); + } + } + return false; +} + +// Set `active' state using recursive method. +void WinToolWidget::propagateActiveStateInCustomTitlebar(QLayout *layout, + bool active_state) +{ + for (size_t i = 0; i < layout->count(); i++) { + auto item = layout->itemAt(i)->widget(); + if (item) { + item->setProperty("active", active_state); + item->setStyleSheet(item->styleSheet()); + } else { + auto child_layout = layout->itemAt(i)->layout(); + if (child_layout) + propagateActiveStateInCustomTitlebar(child_layout, + active_state); + } + } +} + +QWidget &WinToolWidget::getTitlebarWidget() { return *m_titlebar_widget; } + +QHBoxLayout &WinToolWidget::getCustomTitlebarLayout() +{ + return *m_custom_titlebar_layout; +} + +void WinToolWidget::setResizeBorderWidth(const int &resize_border_width) +{ + m_resize_border_width = resize_border_width; +} + +void WinToolWidget::setTitlebarHeight(const int &titlebar_height) +{ + m_titlebar_widget->setFixedHeight(titlebar_height); +} + +// Render again when frame is moved to another monitor. +void WinToolWidget::onScreenChanged(QScreen *screen) +{ + SetWindowPos(m_hwnd, NULL, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | + SWP_FRAMECHANGED | SWP_NOACTIVATE); +} + +void WinToolWidget::onMinimizeButtonClicked() +{ + SendMessage(m_hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); +} + +void WinToolWidget::onMaximizeButtonClicked() +{ + SendMessage(m_hwnd, WM_SYSCOMMAND, + m_max_btn->isChecked() ? SC_MAXIMIZE : SC_RESTORE, 0); + + // Remove the hover state from the maximize button. + m_max_btn->setAttribute(Qt::WA_UnderMouse, false); +} + +void WinToolWidget::onCloseButtonClicked() +{ + SendMessage(m_hwnd, WM_CLOSE, 0, 0); +} + +QWidget *WinToolWidget::contentWidget() { return m_content_widget; } diff --git a/src/platform/windows/widgets/wintoolwidget.h b/src/platform/windows/widgets/wintoolwidget.h new file mode 100644 index 0000000..57d4815 --- /dev/null +++ b/src/platform/windows/widgets/wintoolwidget.h @@ -0,0 +1,76 @@ +/* + * iDescriptor: A free and open-source idevice management tool. + * + * Copyright (C) 2025 Uncore + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef WINTOOLWIDGET_H +#define WINTOOLWIDGET_H + +#include "../win_common.h" +#include +#include +#include +#include +#include +#include +#include + +class WinToolWidget : public QWidget +{ + Q_OBJECT + + HWND m_hwnd; + int m_resize_border_width; + + QPushButton *m_min_btn; + QPushButton *m_max_btn; + QPushButton *m_close_btn; + + QWidget *m_content_widget; + QVBoxLayout *m_content_layout; + QWidget *m_titlebar_widget; + QHBoxLayout *m_custom_titlebar_layout; + QColor m_titleBarColor; + +public: + explicit WinToolWidget(QWidget *parent = nullptr); + void setResizeBorderWidth(const int &resize_border_width); + void setTitlebarHeight(const int &titlebar_height); + QWidget &getTitlebarWidget(); + QHBoxLayout &getCustomTitlebarLayout(); + +private: +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + bool nativeEvent(const QByteArray &event_type, void *message, long *result); +#else + bool nativeEvent(const QByteArray &event_type, void *message, + qintptr *result); +#endif + bool event(QEvent *evt); + bool determineNonClickableWidgetUnderMouse(QLayout *layout, int x, int y); + void propagateActiveStateInCustomTitlebar(QLayout *layout, + bool active_state); + void onScreenChanged(QScreen *screen); + void onMinimizeButtonClicked(); + void onMaximizeButtonClicked(); + void onCloseButtonClicked(); + +protected: + QWidget *contentWidget(); +}; + +#endif // WINTOOLWIDGET_H diff --git a/src/platform/windows/win_common.h b/src/platform/windows/win_common.h new file mode 100644 index 0000000..1403175 --- /dev/null +++ b/src/platform/windows/win_common.h @@ -0,0 +1,107 @@ +/* + * iDescriptor: A free and open-source idevice management tool. + * + * Copyright (C) 2025 Uncore + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum WIN_BACKDROP { MICA = 2, ACRYLIC = 3, MICA_ALT = 4 }; + +bool IsAppleMobileDeviceSupportInstalled(); +bool IsWinFspInstalled(); +bool is_iDescriptorInstalled(); +bool IsBonjourServiceInstalled(); + +void enableAcrylic(HWND hwnd); +void setupTitleBar(HWND hwnd); +void enableMica(HWND hwnd); + +inline QString getWindowsAccentColor() +{ + HKEY hKey = nullptr; + DWORD value = 0; + DWORD size = sizeof(value); + + if (RegOpenKeyExW( + HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Accent", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + const LONG res = + RegQueryValueExW(hKey, L"AccentColorMenu", nullptr, nullptr, + reinterpret_cast(&value), &size); + RegCloseKey(hKey); + + if (res == ERROR_SUCCESS) { + // Value is stored as 0xAABBGGRR, convert to #RRGGBB + const BYTE b = (value & 0x00FF0000) >> 16; + const BYTE g = (value & 0x0000FF00) >> 8; + const BYTE r = (value & 0x000000FF); + + return QString("#%1%2%3") + .arg(r, 2, 16, QChar('0')) + .arg(g, 2, 16, QChar('0')) + .arg(b, 2, 16, QChar('0')) + .toUpper(); + } + } + + // Fallback default accent color + return "#0078D4"; +} + +inline bool detectDarkModeWindows() +{ + HKEY hKey; + DWORD value; + DWORD size = sizeof(value); + if (RegOpenKeyExW(HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\P" + L"ersonalize", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + if (RegQueryValueExW(hKey, L"AppsUseLightTheme", nullptr, nullptr, + reinterpret_cast(&value), + &size) == ERROR_SUCCESS) { + RegCloseKey(hKey); + return value == 0; // 0 means dark mode, 1 means light mode + } + RegCloseKey(hKey); + } + return true; // Default to dark mode if detection fails +} + +inline void setupWinWindow(QWidget *window) +{ + window->setAttribute(Qt::WA_TranslucentBackground); + HWND hwnd = reinterpret_cast(window->winId()); + + // use mica on Windows 11 + QOperatingSystemVersion osVersion = QOperatingSystemVersion::current(); + if (osVersion >= QOperatingSystemVersion::Windows11) { + enableMica(hwnd); + } else { + enableAcrylic(hwnd); + } +} \ No newline at end of file diff --git a/src/servicemanager.cpp b/src/servicemanager.cpp index c2c089e..8d07428 100644 --- a/src/servicemanager.cpp +++ b/src/servicemanager.cpp @@ -98,10 +98,10 @@ ServiceManager::safeAfcFileSeek(const iDescriptorDevice *device, AfcFileHandle *handle, int64_t offset, int whence) { - off_t newPos; return executeAfcOperation( device, - [offset, whence, &newPos](AfcFileHandle *handle) { + [offset, whence](AfcFileHandle *handle) { + int64_t newPos; return afc_file_seek(handle, offset, whence, &newPos); }, handle); @@ -109,7 +109,7 @@ ServiceManager::safeAfcFileSeek(const iDescriptorDevice *device, IdeviceFfiError * ServiceManager::safeAfcFileTell(const iDescriptorDevice *device, - AfcFileHandle *handle, off_t *position) + AfcFileHandle *handle, int64_t *position) { return executeAfcOperation( device, diff --git a/src/servicemanager.h b/src/servicemanager.h index 541f9ca..3c2f65f 100644 --- a/src/servicemanager.h +++ b/src/servicemanager.h @@ -268,7 +268,7 @@ public: int64_t offset, int whence); static IdeviceFfiError *safeAfcFileTell(const iDescriptorDevice *device, AfcFileHandle *handle, - off_t *position); + int64_t *position); // Utility functions static QByteArray safeReadAfcFileToByteArray( const iDescriptorDevice *device, const char *path, diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp index cbfee40..3abdaba 100644 --- a/src/settingsmanager.cpp +++ b/src/settingsmanager.cpp @@ -475,4 +475,18 @@ QMap SettingsManager::getAllIdeviceDefaultPairingFiles() const } return macAddresses; } +#endif + +#ifdef WIN32 +void SettingsManager::setWinBackdropType(WIN_BACKDROP type) +{ + m_settings->setValue("winBackdropType", static_cast(type)); + m_settings->sync(); +} + +WIN_BACKDROP SettingsManager::winBackdropType() const +{ + return static_cast( + m_settings->value("winBackdropType", static_cast(MICA)).toInt()); +} #endif \ No newline at end of file diff --git a/src/settingsmanager.h b/src/settingsmanager.h index 153282a..8ed2104 100644 --- a/src/settingsmanager.h +++ b/src/settingsmanager.h @@ -28,6 +28,10 @@ #include #include +#ifdef WIN32 +#include "platform/windows/win_common.h" +#endif + class SettingsManager : public QObject { Q_OBJECT @@ -127,6 +131,10 @@ public: QMap getAllIdeviceDefaultPairingFiles() const; #endif +#ifdef WIN32 + void setWinBackdropType(WIN_BACKDROP type); + WIN_BACKDROP winBackdropType() const; +#endif signals: void favoritePlacesChanged(); void recentLocationsChanged(); diff --git a/src/settingswidget.cpp b/src/settingswidget.cpp index b1c2159..326e65d 100644 --- a/src/settingswidget.cpp +++ b/src/settingswidget.cpp @@ -37,14 +37,23 @@ #include #include +#ifdef WIN32 +#include "platform/windows/win_common.h" +#include +#endif + SettingsWidget::SettingsWidget(QWidget *parent) : QDialog{parent} { +#ifdef WIN32 + m_backDropTypeCombo = nullptr; +#endif setupUI(); loadSettings(); connectSignals(); // due to scrollbar add 10px on windows #ifdef WIN32 resize(sizeHint().width() + 10, sizeHint().height()); + setupWinWindow(this); #endif } @@ -113,7 +122,26 @@ void SettingsWidget::setupUI() themeLayout->addWidget(m_themeCombo); themeLayout->addStretch(); - generalLayout->addLayout(themeLayout); + +#ifdef WIN32 + QOperatingSystemVersion osVersion = QOperatingSystemVersion::current(); + if (osVersion >= QOperatingSystemVersion::Windows11) { + auto *backDropTypeLayout = new QHBoxLayout(); + backDropTypeLayout->addWidget(new QLabel("Backdrop Type:")); + m_backDropTypeCombo = new QComboBox(); + + // "Auto" => no userData => means "no override" + m_backDropTypeCombo->addItem("Auto"); + m_backDropTypeCombo->addItem("Mica", static_cast(MICA)); + m_backDropTypeCombo->addItem("Mica Alt", static_cast(MICA_ALT)); + m_backDropTypeCombo->addItem("Acrylic", static_cast(ACRYLIC)); + + backDropTypeLayout->addWidget(m_backDropTypeCombo); + backDropTypeLayout->addStretch(); + + generalLayout->addLayout(backDropTypeLayout); + } +#endif scrollLayout->addWidget(generalGroup); @@ -191,7 +219,6 @@ void SettingsWidget::setupUI() m_noHoldCheckbox = new QCheckBox("Allow New Connections to Take Over"); airplayLayout->addWidget(m_noHoldCheckbox); - #ifdef __linux__ m_showV4L2CheckBox = new QCheckBox("Show V4L2 Button on AirPlay Widget"); airplayLayout->addWidget(m_showV4L2CheckBox); @@ -302,6 +329,18 @@ void SettingsWidget::loadSettings() #ifdef __linux__ m_showV4L2CheckBox->setChecked(sm->showV4L2()); #endif + +#ifdef WIN32 + if (m_backDropTypeCombo) { + const int typeValue = static_cast(sm->winBackdropType()); + const int index = m_backDropTypeCombo->findData(typeValue); + if (index != -1) { + m_backDropTypeCombo->setCurrentIndex(index); + } else { + m_backDropTypeCombo->setCurrentIndex(0); + } + } +#endif } void SettingsWidget::connectSignals() @@ -356,14 +395,21 @@ void SettingsWidget::connectSignals() connect(m_defaultJailbrokenRootPassword, &QLineEdit::textChanged, this, &SettingsWidget::onSettingChanged); - connect(m_fpsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &SettingsWidget::onSettingChanged); + connect(m_fpsComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &SettingsWidget::onSettingChanged); connect(m_noHoldCheckbox, &QCheckBox::toggled, this, &SettingsWidget::onSettingChanged); #ifdef __linux__ connect(m_showV4L2CheckBox, &QCheckBox::toggled, this, &SettingsWidget::onSettingChanged); #endif +#ifdef WIN32 + if (m_backDropTypeCombo) { + connect(m_backDropTypeCombo, + QOverload::of(&QComboBox::currentIndexChanged), this, + &SettingsWidget::onSettingChanged); + } +#endif } void SettingsWidget::onBrowseButtonClicked() @@ -457,6 +503,18 @@ void SettingsWidget::saveSettings() sm->setShowV4L2(m_showV4L2CheckBox->isChecked()); #endif m_applyButton->setEnabled(false); + +#ifdef WIN32 + if (m_backDropTypeCombo) { + const QVariant data = m_backDropTypeCombo->currentData(); + if (!data.isValid()) { + // Mica + sm->setWinBackdropType(static_cast(2)); + } else { + sm->setWinBackdropType(static_cast(data.toInt())); + } + } +#endif } void SettingsWidget::resetToDefaults() diff --git a/src/settingswidget.h b/src/settingswidget.h index 1ecccd6..c385ff0 100644 --- a/src/settingswidget.h +++ b/src/settingswidget.h @@ -77,6 +77,10 @@ private: QCheckBox *m_showV4L2CheckBox; #endif +#ifdef WIN32 + QComboBox *m_backDropTypeCombo; +#endif + // Buttons QPushButton *m_checkUpdatesButton; QPushButton *m_resetButton; diff --git a/src/statusballoon.cpp b/src/statusballoon.cpp index 2245a6c..bb1f0b1 100644 --- a/src/statusballoon.cpp +++ b/src/statusballoon.cpp @@ -26,6 +26,10 @@ #include #include +#ifdef WIN32 +#include "platform/windows/win_common.h" +#endif + Process::Process(QWidget *parent) : QWidget(parent) {} StatusBalloon *StatusBalloon::sharedInstance() @@ -39,6 +43,10 @@ StatusBalloon::StatusBalloon(QWidget *parent) { setMinimumHeight(300); setMinimumWidth(300); +#ifdef WIN32 + // FIXME: doesnt work the second time we call it + enableAcrylic((HWND)winId()); +#endif // Create main layout m_mainLayout = new QVBoxLayout(); m_mainLayout->setSpacing(8); @@ -77,10 +85,11 @@ void StatusBalloon::connectExportThreadSignals() connect(exportManager->m_exportThread, &ExportManagerThread::fileTransferProgress, this, &StatusBalloon::onFileTransferProgress); - QTimer::singleShot(0, this, [this]() { - // test - startExportProcess("Test Export Process", 10, "/path/to/destination"); - }); + // QTimer::singleShot(0, this, [this]() { + // // test + // startExportProcess("Test Export Process", 10, + // "/path/to/destination"); + // }); } void StatusBalloon::onFileTransferProgress(const QUuid &processId, diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp index 4f9248e..bc06876 100644 --- a/src/toolboxwidget.cpp +++ b/src/toolboxwidget.cpp @@ -37,6 +37,10 @@ #include #include +#ifdef WIN32 +#include +#endif + struct iDescriptorToolWidget { iDescriptorTool tool; QString description; @@ -335,14 +339,35 @@ void ToolboxWidget::updateToolboxStates() bool enabled = !requiresDevice || hasDevice; toolbox->setEnabled(enabled); +// Opacity does not work because of the stylesheet on Windows +#ifndef WIN32 if (enabled) { - toolbox->setStyleSheet("#toolboxFrame { " - "border-radius: 5px; padding: 5px; }"); + toolbox->setStyleSheet("QWidget#toolboxFrame { " + "padding: 5px; }"); } else { - toolbox->setStyleSheet("#toolboxFrame { border-radius: 5px; " + toolbox->setStyleSheet("QWidget#toolboxFrame { " "padding: 5px;" "opacity: 0.45; }"); } +#else + // base style + // toolbox->setStyleSheet("QWidget#toolboxFrame{ padding: 5px; border: " + // "none; outline: none; }"); + + if (enabled) { + // normal look + toolbox->setStyleSheet("QWidget#toolboxFrame { padding: 5px; " + "border: none; outline: none; }"); + toolbox->setCursor(Qt::PointingHandCursor); + } else { + // disabled look: dull bg + border + muted text, no hand cursor + toolbox->setStyleSheet("padding: 5px;" + "border-radius: 8px;" + "background-color: rgba(255,255,255,1);" + "color: #666;"); + toolbox->setCursor(Qt::ArrowCursor); + } +#endif } } diff --git a/src/welcomewidget.cpp b/src/welcomewidget.cpp index cc6f7d8..2d41a27 100644 --- a/src/welcomewidget.cpp +++ b/src/welcomewidget.cpp @@ -78,8 +78,8 @@ void WelcomeWidget::setupUI() m_mainLayout->addSpacing(10); // GitHub link - m_githubLabel = - createStyledLabel("Found an issue? Report it on GitHub", 12, false); + m_githubLabel = createStyledLabel("Found an issue? Report it on GitHub", 12, + false, COLOR_HYPERLINK); m_githubLabel->setWordWrap(false); m_githubLabel->setMaximumWidth(m_imageLabel->sizeHint().width()); m_githubLabel->setCursor(Qt::PointingHandCursor); @@ -88,7 +88,7 @@ void WelcomeWidget::setupUI() QPalette githubPalette = m_githubLabel->palette(); githubPalette.setColor(QPalette::WindowText, - QColor(0, 122, 255)); // Apple blue + COLOR_HYPERLINK); // Apple blue m_githubLabel->setPalette(githubPalette); m_mainLayout->addWidget(m_githubLabel, 0, Qt::AlignCenter); @@ -102,11 +102,13 @@ void WelcomeWidget::setupUI() m_mainLayout->addStretch(1); } +// FIXME: color param is only respected in Windows build ZLabel *WelcomeWidget::createStyledLabel(const QString &text, int fontSize, - bool isBold) + bool isBold, QColor color) { ZLabel *label = new ZLabel(text); +#ifndef WIN32 QFont font = label->font(); if (fontSize > 0) { font.setPointSize(fontSize); @@ -117,6 +119,20 @@ ZLabel *WelcomeWidget::createStyledLabel(const QString &text, int fontSize, label->setFont(font); label->setWordWrap(true); +#else + label->setStyleSheet(mergeStyles( + label, + QString("QLabel {" + " font-size: %1px;" + " font-weight: %2;" + "%3" + "}") + .arg(fontSize > 0 ? QString::number(fontSize) : "inherit") + .arg(isBold ? "bold" : "normal") + // FIXME: handle this better + .arg(color != Qt::black ? QString("color: %1;").arg(color.name()) + : ""))); +#endif return label; } diff --git a/src/welcomewidget.h b/src/welcomewidget.h index 5c9b11e..6d58370 100644 --- a/src/welcomewidget.h +++ b/src/welcomewidget.h @@ -37,7 +37,7 @@ public: private: void setupUI(); ZLabel *createStyledLabel(const QString &text, int fontSize = 0, - bool isBold = false); + bool isBold = false, QColor color = Qt::black); QVBoxLayout *m_mainLayout; ZLabel *m_titleLabel; diff --git a/src/zlineedit.cpp b/src/zlineedit.cpp index d90a47a..f5e9fed 100644 --- a/src/zlineedit.cpp +++ b/src/zlineedit.cpp @@ -32,18 +32,20 @@ void ZLineEdit::setupStyles() { updateStyles(); } void ZLineEdit::updateStyles() { - setStyleSheet("QLineEdit { " - " border: 2px solid " + - qApp->palette().color(QPalette::Midlight).name() + - "; " - " border-radius: 6px; " - " padding: 8px 12px; " - " font-size: 14px; " - "} " - "QLineEdit:focus { " - " border: 2px solid " + - COLOR_ACCENT_BLUE.name() + - "; " - " outline: none; " - "}"); + // FIMXE: seg faults if Qt decides to change pallets due to applied + // stylesheets + // setStyleSheet("QLineEdit { " + // " border: 2px solid " + + // qApp->palette().color(QPalette::Midlight).name() + + // "; " + // " border-radius: 6px; " + // " padding: 8px 12px; " + // " font-size: 14px; " + // "} " + // "QLineEdit:focus { " + // " border: 2px solid " + + // COLOR_ACCENT_BLUE.name() + + // "; " + // " outline: none; " + // "}"); } \ No newline at end of file diff --git a/src/zloadingwidget.cpp b/src/zloadingwidget.cpp index fdbfd04..3925032 100644 --- a/src/zloadingwidget.cpp +++ b/src/zloadingwidget.cpp @@ -27,34 +27,36 @@ ZLoadingWidget::ZLoadingWidget(bool start, QWidget *parent) void ZLoadingWidget::setupContentWidget(QWidget *contentWidget) { - addWidget(contentWidget); // Content widget at index 1 + m_contentWidget = contentWidget; + addWidget(m_contentWidget); } void ZLoadingWidget::setupContentWidget(QLayout *contentLayout) { - QWidget *contentWidget = new QWidget(); - contentWidget->setLayout(contentLayout); + m_contentWidget = new QWidget(); + m_contentWidget->setLayout(contentLayout); - addWidget(contentWidget); // Content widget at index 1 + addWidget(m_contentWidget); } void ZLoadingWidget::setupErrorWidget(QWidget *errorWidget) { - addWidget(errorWidget); // Error widget at index 2 + m_errorWidget = errorWidget; + addWidget(m_errorWidget); } void ZLoadingWidget::setupErrorWidget(QLayout *errorLayout) { - QWidget *errorWidget = new QWidget(); - errorWidget->setLayout(errorLayout); + m_errorWidget = new QWidget(); + m_errorWidget->setLayout(errorLayout); - addWidget(errorWidget); // Error widget at index 2 + addWidget(m_errorWidget); } void ZLoadingWidget::setupErrorWidget(const QString &errorMessage) { - QWidget *errorWidget = new QWidget(); - QVBoxLayout *errorLayout = new QVBoxLayout(errorWidget); + m_errorWidget = new QWidget(); + QVBoxLayout *errorLayout = new QVBoxLayout(m_errorWidget); errorLayout->setAlignment(Qt::AlignCenter); QLabel *errorLabel = new QLabel(errorMessage); @@ -62,7 +64,7 @@ void ZLoadingWidget::setupErrorWidget(const QString &errorMessage) errorLabel->setStyleSheet("QLabel { color: red; }"); errorLayout->addWidget(errorLabel); - addWidget(errorWidget); // Error widget at index 2 + addWidget(m_errorWidget); } void ZLoadingWidget::setupAditionalWidget(QWidget *customWidget) @@ -92,8 +94,9 @@ void ZLoadingWidget::stop(bool showContent) void ZLoadingWidget::showError() { m_loadingIndicator->stop(); - // FIXME: dont use hardcoded index - setCurrentIndex(2); + if (m_errorWidget) { + setCurrentWidget(m_errorWidget); + } } void ZLoadingWidget::showLoading() diff --git a/src/zloadingwidget.h b/src/zloadingwidget.h index 9ddabf4..f046715 100644 --- a/src/zloadingwidget.h +++ b/src/zloadingwidget.h @@ -23,6 +23,8 @@ public: private: class QProcessIndicator *m_loadingIndicator = nullptr; + QWidget *m_contentWidget = nullptr; + QWidget *m_errorWidget = nullptr; signals: }; diff --git a/src/ztabwidget.cpp b/src/ztabwidget.cpp index dddacc5..66ca6a2 100644 --- a/src/ztabwidget.cpp +++ b/src/ztabwidget.cpp @@ -18,18 +18,51 @@ */ #include "ztabwidget.h" +#include "iDescriptor-ui.h" #include #include #include #include +#include +#include #include #include +QRect gliderEndRectForTab(const ZTab *tab) +{ + if (!tab) + return {}; + + // Approximate "title center" using the push-button contents rect center. + QStyleOptionButton opt; + opt.initFrom(tab); + opt.text = tab->text(); + opt.icon = tab->icon(); + opt.iconSize = tab->iconSize(); + + QRect contents = + tab->style()->subElementRect(QStyle::SE_PushButtonContents, &opt, tab); + if (!contents.isValid()) + contents = tab->rect(); + + const int centerX = tab->mapToParent(contents.center()).x(); + + // Half-width glider, clamped so it never exceeds contents width and never + // gets too tiny. + const int rawW = tab->width() / 1.5; + const int maxW = qMax(1, contents.width()); + const int w = qBound(12, qMin(rawW, maxW), tab->width()); + + const int x = centerX - (w / 2); + const int y = tab->pos().y() + tab->height() - 2; + return QRect(x, y, w, 2); +} + ZTab::ZTab(const QString &text, QWidget *parent) : QPushButton(text, parent) { setCheckable(true); #ifndef WIN32 - setFixedHeight(50); + setFixedHeight(40); #else setFixedHeight(40); #endif @@ -40,13 +73,13 @@ ZTab::ZTab(const QString &text, QWidget *parent) : QPushButton(text, parent) ZTabWidget::ZTabWidget(QWidget *parent) : QWidget(parent), m_currentIndex(0) { m_mainLayout = new QVBoxLayout(this); - m_mainLayout->setContentsMargins(0, 0, 0, 0); + m_mainLayout->setContentsMargins(10, 0, 10, 0); m_mainLayout->setSpacing(0); // Create tab bar container m_tabBar = new QWidget(); #ifndef WIN32 - m_tabBar->setFixedHeight(50); + m_tabBar->setFixedHeight(40); #else m_tabBar->setFixedHeight(40); #endif @@ -80,10 +113,17 @@ ZTabWidget::ZTabWidget(QWidget *parent) : QWidget(parent), m_currentIndex(0) void ZTabWidget::setupGlider() { m_glider = new QWidget(m_tabBar); - m_glider->setStyleSheet("QWidget {" - " background-color: #2b5693;" - " border-radius: 1px;" - "}"); + m_glider->setStyleSheet(QString("QWidget {" + " background-color: %1;" + " border-radius: %2px;" + "}") + .arg(COLOR_ACCENT_BLUE.name()) +#ifndef WIN32 + .arg(6) +#else + .arg(2) +#endif + ); m_glider->hide(); // Hide initially until tabs are added } @@ -111,6 +151,9 @@ void ZTabWidget::setCurrentIndex(int index) m_currentIndex = index; m_tabs[index]->setChecked(true); m_stackedWidget->setCurrentIndex(index); +#ifdef WIN32 + animateWidget(m_stackedWidget->currentWidget()); +#endif updateTabStyles(); animateGlider(index); @@ -119,22 +162,30 @@ void ZTabWidget::setCurrentIndex(int index) void ZTabWidget::finalizeStyles() { + if (m_tabs.isEmpty()) + return; + ZTab *tab = m_tabs[0]; if (tab) { tab->setChecked(true); + QTimer::singleShot(0, [this, tab]() { - if (tab) { - m_glider->setFixedSize(tab->size().width(), 2); - int targetX = tab->pos().x(); - int targetY = tab->pos().y() + tab->size().height() - 2; - m_glider->move(targetX, targetY); - m_gliderAnimation = new QPropertyAnimation(m_glider, "pos"); - m_gliderAnimation->setDuration(250); - m_gliderAnimation->setEasingCurve(QEasingCurve::OutCubic); - m_glider->show(); + if (!tab) + return; + + const QRect endRect = gliderEndRectForTab(tab); + + if (m_gliderAnimation) { + m_gliderAnimation->stop(); + delete m_gliderAnimation; + m_gliderAnimation = nullptr; } + + m_glider->setGeometry(endRect); + m_glider->show(); }); } + updateTabStyles(); } @@ -160,7 +211,7 @@ void ZTabWidget::onTabClicked() } } -void ZTabWidget::animateGlider(int index) +void ZTabWidget::animateGlider(int index, bool onResize) { if (index < 0 || index >= m_tabs.count()) return; @@ -169,59 +220,144 @@ void ZTabWidget::animateGlider(int index) if (!targetTab) return; - // Get the actual position and size of the target tab - QPoint targetTabPos = targetTab->pos(); - QSize targetTabSize = targetTab->size(); + const QRect endRect = gliderEndRectForTab(targetTab); - // Set glider width to match tab width and height to 2px for bottom border - m_glider->setFixedSize(targetTabSize.width(), 2); +#ifdef WIN32 + if (onResize || !m_glider->isVisible()) { + if (m_gliderAnimation) { + m_gliderAnimation->stop(); + delete m_gliderAnimation; + m_gliderAnimation = nullptr; + } + m_glider->setGeometry(endRect); + m_glider->show(); + return; + } - // Position glider at the bottom of the target tab - int targetX = targetTabPos.x(); - int targetY = - // targetTabPos.y() + targetTabSize.height() + 6; // Position at bottom - targetTabPos.y() + targetTabSize.height() - 2; // Position at bottom + const QRect startRect = m_glider->geometry(); - if (m_gliderAnimation == nullptr) + const int left = qMin(startRect.left(), endRect.left()); + const int right = qMax(startRect.right(), endRect.right()); + const QRect stretchRect(left, endRect.y(), (right - left + 1), 2); + + if (m_gliderAnimation) { + m_gliderAnimation->stop(); + delete m_gliderAnimation; + m_gliderAnimation = nullptr; + } + + auto *group = new QSequentialAnimationGroup(this); + + auto *expandAnim = new QPropertyAnimation(m_glider, "geometry"); + expandAnim->setDuration(130); + expandAnim->setStartValue(startRect); + expandAnim->setEndValue(stretchRect); + expandAnim->setEasingCurve(QEasingCurve::OutCubic); + + auto *settleAnim = new QPropertyAnimation(m_glider, "geometry"); + settleAnim->setDuration(190); + settleAnim->setStartValue(stretchRect); + settleAnim->setEndValue(endRect); + settleAnim->setEasingCurve(QEasingCurve::OutCubic); + + group->addAnimation(expandAnim); + group->addAnimation(settleAnim); + + m_gliderAnimation = group; + group->start(); +#else + if (m_gliderAnimation == nullptr) { + m_gliderAnimation = new QPropertyAnimation(m_glider, "pos", this); + static_cast(m_gliderAnimation)->setDuration(250); + static_cast(m_gliderAnimation) + ->setEasingCurve(QEasingCurve::OutCubic); + } + + m_glider->setFixedSize(endRect.width(), 2); + m_gliderAnimation->stop(); + static_cast(m_gliderAnimation) + ->setStartValue(m_glider->pos()); + static_cast(m_gliderAnimation) + ->setEndValue(endRect.topLeft()); + m_gliderAnimation->start(); +#endif +} + +void ZTabWidget::animateWidget(QWidget *widget) +{ +#ifdef WIN32 + if (!widget) return; - m_gliderAnimation->stop(); - m_gliderAnimation->setStartValue(m_glider->pos()); - m_gliderAnimation->setEndValue(QPoint(targetX, targetY)); - m_gliderAnimation->start(); + // FIXME: doesn't work on Tool tab because we are using opacity in + // stylesheet + QGraphicsOpacityEffect *opacityEffect = + qobject_cast(widget->graphicsEffect()); + if (!opacityEffect) { + opacityEffect = new QGraphicsOpacityEffect(widget); + widget->setGraphicsEffect(opacityEffect); + } + + QPropertyAnimation *opacityAnim = + new QPropertyAnimation(opacityEffect, "opacity", this); + opacityAnim->setDuration(350); + opacityAnim->setStartValue(0.0); + opacityAnim->setEndValue(1.0); + opacityAnim->setEasingCurve(QEasingCurve::OutCubic); + opacityAnim->start(QAbstractAnimation::DeleteWhenStopped); + + QPropertyAnimation *posAnim = new QPropertyAnimation(widget, "pos", this); + posAnim->setDuration(350); + posAnim->setStartValue(QPoint(widget->pos().x(), widget->pos().y() + 20)); + posAnim->setEndValue(widget->pos()); + posAnim->setEasingCurve(QEasingCurve::OutCubic); + posAnim->start(QAbstractAnimation::DeleteWhenStopped); +#else + Q_UNUSED(widget); +#endif } void ZTabWidget::updateTabStyles() { + const QString accentColor = + +#ifdef WIN32 + COLOR_ACCENT_BLUE.name(); +#else + "#185ee0"; +#endif + for (int i = 0; i < m_tabs.count(); ++i) { ZTab *tab = m_tabs[i]; if (tab->isChecked()) { - tab->setStyleSheet("ZTab {" - " color: #185ee0;" - // " color: #d7e1f4ff;" - " font-weight: 500;" - " font-size: 20px;" - " border: none;" - " outline: none;" - " background-color: transparent;" - "}" - "ZTab:hover {" - " background-color: transparent;" - "}"); + tab->setStyleSheet(QString("ZTab {" + " color: %1;" + // " color: #d7e1f4ff;" + " font-weight: 700;" + " font-size: 20px;" + " border: none;" + " outline: none;" + " background-color: transparent;" + "}" + "ZTab:hover {" + " background-color: transparent;" + "}") + .arg(accentColor)); } else { - tab->setStyleSheet("ZTab {" - " color: #666;" - // " color: #2b5693;" - " font-weight: 500;" - " font-size: 20px;" - " border: none;" - " outline: none;" - " background-color: transparent;" - "}" - "ZTab:hover {" - " color: #185ee0;" - " background-color: transparent;" - "}"); + tab->setStyleSheet(QString("ZTab {" + " color: #666;" + // " color: #2b5693;" + " font-weight: 700;" + " font-size: 20px;" + " border: none;" + " outline: none;" + " background-color: transparent;" + "}" + "ZTab:hover {" + " color: %1;" + " background-color: transparent;" + "}") + .arg(accentColor)); } } } @@ -231,6 +367,6 @@ void ZTabWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); if (m_currentIndex >= 0 && m_currentIndex < m_tabs.count()) { - animateGlider(m_currentIndex); + animateGlider(m_currentIndex, true); } -} \ No newline at end of file +} diff --git a/src/ztabwidget.h b/src/ztabwidget.h index 0ecab38..081aa5a 100644 --- a/src/ztabwidget.h +++ b/src/ztabwidget.h @@ -67,13 +67,14 @@ private: QStackedWidget *m_stackedWidget; QButtonGroup *m_buttonGroup; QWidget *m_glider; - QPropertyAnimation *m_gliderAnimation = nullptr; + QAbstractAnimation *m_gliderAnimation = nullptr; QList m_tabs; QList m_widgets; int m_currentIndex; void setupGlider(); - void animateGlider(int index); + void animateGlider(int index, bool onResize = false); + void animateWidget(QWidget *widget); void updateTabStyles(); };