diff --git a/resources/win.dark.qcss b/resources/win.dark.qcss index 28acc57..9cc64fa 100644 --- a/resources/win.dark.qcss +++ b/resources/win.dark.qcss @@ -1,17 +1,38 @@ QWidget { background: transparent; color: rgb(255, 255, 255); - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; } + +QMessageBox { + background-color: rgba(255, 255, 255, 16); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 8px; +} + +QMessageBox QLabel { + background-color: transparent; + color: rgb(255, 255, 255); +} + +QMessageBox QPushButton { + background-color: rgba(255, 255, 255, 18); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 7px; + min-height: 32px; + min-width: 80px; + padding: 5px 15px; +} + /*MENU*/ QMenuBar { background-color: transparent; color: white; padding: 10px; - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; } @@ -33,7 +54,7 @@ QMenuBar::item:pressed { } QMenu { - background-color: transparent; + background-color: rgba(0, 0, 0, 210); padding-left: 1px; padding-top: 1px; border-radius: 5px; @@ -173,7 +194,7 @@ QCheckBox::indicator:pressed { QCheckBox::indicator:checked { background-color: %1; border: 2px solid %1; - image: url(:/CheckBox/img dark/CheckBox.png); + image: url(:/resources/win/dark/CheckBox.png); } QCheckBox::indicator:checked:pressed { @@ -656,16 +677,47 @@ QComboBox::down-arrow:disabled { image: url(:/resources/win/dark/ComboBoxDisabled.png); } +/*COMBOBOX POPUP*/ +QComboBox QAbstractItemView { + background-color: rgba(0, 0, 0, 210); + border: 1px solid rgba(255, 255, 255, 13); + border-radius: 5px; + padding: 3px; + outline: 0; +} + +QComboBox QAbstractItemView::item { + background-color: transparent; + padding: 5px 10px; + min-height: 26px; + border-radius: 4px; +} + +QComboBox QAbstractItemView::item:selected { + background-color: rgba(255, 255, 255, 16); + color: rgb(255, 255, 255); +} + +QComboBox QAbstractItemView::item:!selected:hover { + background-color: rgba(255, 255, 255, 10); + color: rgb(255, 255, 255); +} + +QComboBox QAbstractItemView::item:disabled { + color: rgb(150, 150, 150); +} + /*LINEEDIT*/ QLineEdit { background-color: rgba(255, 255, 255, 16); border: 1px solid rgba(255, 255, 255, 13); - font-size: 16px; + font-size: 14px; font-family: "Segoe UI", serif; font-weight: 500; border-radius: 7px; border-bottom: 1px solid rgba(255, 255, 255, 150); padding: 5px; + min-height: 30px; } QLineEdit:hover { @@ -790,7 +842,7 @@ QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { QTextEdit { background-color: rgba(255, 255, 255, 16); border: 1px solid rgba(255, 255, 255, 13); - font-size: 16px; + font-size: 14px; font-family: "Segoe UI", serif; font-weight: 500; border-radius: 7px; @@ -824,7 +876,7 @@ QCalendarWidget { QCalendarWidget QToolButton { height: 36px; - font-size: 18px; + font-size: 14px; background-color: rgba(255, 255, 255, 0); margin: 5px; } @@ -1021,7 +1073,7 @@ QTreeView:disabled { /*TOGGLESWITCH*/ #toggleSwitch { color: rgb(255, 255, 255); - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; } @@ -1083,7 +1135,7 @@ QTreeView:disabled { /*HYPERLINKBUTTON*/ #hyperlinkButton { color: %1; - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; border-radius: 5px; background-color: rgba(255, 255, 255, 0); @@ -1106,7 +1158,7 @@ QTreeView:disabled { /*LISTVIEW*/ QListView { background-color: transparent; - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; padding: 7px; @@ -1123,4 +1175,17 @@ QListView::item:selected { color: white; border-radius: 5px; padding-left: 0px; -} \ No newline at end of file +} + +QWidget#navWidget { + border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 13); + background-color: rgba(255, 255, 255, 16); + padding-left: 10px; + padding-right: 10px; +} + + +QWidget#VirtualLocationWidget { + background-color: rgb(0, 0, 0); +} diff --git a/resources/win.light.qcss b/resources/win.light.qcss index 3f34631..7f1a322 100644 --- a/resources/win.light.qcss +++ b/resources/win.light.qcss @@ -1,17 +1,37 @@ QWidget { background: transparent; color: rgb(0, 0, 0); - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; } +QMessageBox { + background-color: #f0f0f0; + border: 1px solid rgba(0, 0, 0, 13); + border-radius: 8px; +} + +QMessageBox QLabel { + background-color: transparent; + color: rgb(0, 0, 0); +} + +QMessageBox QPushButton { + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(0, 0, 0, 13); + border-radius: 7px; + min-height: 32px; + min-width: 80px; + padding: 5px 15px; +} + /*MENU*/ QMenuBar { - background-color: transparent; - color: rgba(0, 0, 0); + background-color: transparent; + color: rgb(0, 0, 0); padding: 10px; - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; } @@ -24,20 +44,20 @@ QMenuBar::item { } QMenuBar::item:selected { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QMenuBar::item:pressed { - background-color: rgb(0, 0, 0, 7); - color: rgb(0, 0, 0, 150); + background-color: rgba(0, 0, 0, 7); + color: rgba(0, 0, 0, 150); } QMenu { - background-color: transparent; + background-color: rgba(0, 0, 0, 7); padding-left: 1px; padding-top: 1px; border-radius: 5px; - border: 1px solid rgb(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); } QMenu::item { @@ -49,11 +69,11 @@ QMenu::item { } QMenu::item:selected { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QMenu::item:pressed { - background-color: rgb(0, 0, 0, 7); + background-color: rgba(0, 0, 0, 7); } QMenu::right-arrow { @@ -63,36 +83,38 @@ QMenu::right-arrow { } QMenuBar:disabled { - color: rgb(0, 0, 0, 150); + color: rgba(0, 0, 0, 150); } QMenu::item:disabled { - color: rgb(0, 0, 0, 150); + color: rgba(0, 0, 0, 150); background-color: transparent; } /*PUSHBUTTON*/ QPushButton { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(0, 0, 0, 13); border-radius: 7px; min-height: 38px; max-height: 38px; + padding-left: 7px; + padding-right: 7px; } QPushButton:hover { - background-color: rgb(0, 0, 0, 10); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 10); + border: 1px solid rgba(0, 0, 0, 13); } QPushButton::pressed { - color: rgb(0, 0, 0, 150); + color: rgba(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); + color: rgba(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } /*RADIOBUTTON*/ @@ -106,16 +128,16 @@ QRadioButton::indicator { height: 22px; border-radius: 13px; border: 2px solid #999999; - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); margin-right: 5px; } QRadioButton::indicator:hover { - background-color: rgb(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); } QRadioButton::indicator:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); border: 2px solid #bbbbbb; image: url(:/resources/win/light/RadioButton.png); } @@ -136,12 +158,12 @@ QRadioButton::indicator:checked:pressed { } QRadioButton:disabled { - color: rgb(0, 0, 0, 110); + color: rgba(0, 0, 0, 110); } QRadioButton::indicator:disabled { border: 2px solid #bbbbbb; - background-color: rgb(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); } /*CHECKBOX*/ @@ -155,16 +177,16 @@ QCheckBox::indicator { height: 22px; border-radius: 5px; border: 2px solid #999999; - background-color: rgb(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); margin-right: 5px; } QCheckBox::indicator:hover { - background-color: rgb(0, 0, 0, 15); + background-color: rgba(0, 0, 0, 15); } QCheckBox::indicator:pressed { - background-color: rgb(0, 0, 0, 24); + background-color: rgba(0, 0, 0, 24); border: 2px solid #bbbbbb; } @@ -180,33 +202,33 @@ QCheckBox::indicator:checked:pressed { } QCheckBox:disabled { - color: rgb(0, 0, 0, 110); + color: rgba(0, 0, 0, 110); } QCheckBox::indicator:disabled { border: 2px solid #bbbbbb; - background-color: rgb(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); } /*GROUPBOX*/ QGroupBox { border-radius: 5px; - border: 1px solid rgb(0, 0, 0, 13); - margin-top: 36px; + border: 1px solid rgba(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 10); + margin-top: 30px; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; - background-color: rgb(0, 0, 0, 10); - padding: 7px 15px; + /* padding: 7px 15px; margin-left: 5px; border-top-left-radius: 5px; - border-top-right-radius: 5px; + border-top-right-radius: 5px; */ } QGroupBox::title::disabled { - color: rgb(0, 0, 0, 150); + color: rgba(0, 0, 0, 150); } /*TABWIDGET*/ @@ -218,7 +240,7 @@ QWidget { } QTabWidget::pane { - border: 1px solid rgb(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); border-radius: 5px; } @@ -233,43 +255,43 @@ QTabBar::tab { } QTabBar::tab:hover { - background-color: rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 13); border-top-left-radius: 5px; border-top-right-radius: 5px; } QTabBar::tab:selected { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); border-top-left-radius: 5px; border-top-right-radius: 5px; } QTabBar::tab:disabled { - color: rgb(0, 0, 0, 150) + color: rgba(0, 0, 0, 150) } /*SPINBOX*/ QSpinBox { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(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); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 5); + border: 1px solid rgba(0, 0, 0, 10); + color: rgba(0, 0, 0, 200); border-bottom: 2px solid %1; } @@ -287,11 +309,11 @@ QSpinBox::up-button { } QSpinBox::up-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QSpinBox::up-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QSpinBox::down-button { @@ -308,11 +330,11 @@ QSpinBox::down-button { } QSpinBox::down-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QSpinBox::down-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QSpinBox::drop-down { @@ -321,9 +343,9 @@ QSpinBox::drop-down { } QSpinBox:disabled { - color: rgb(0, 0, 0, 110); - background-color: rgb(0, 0, 0, 13); - border: 1px solid rgb(0, 0, 0, 5); + color: rgba(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } QSpinBox::up-button:disabled { @@ -336,26 +358,26 @@ QSpinBox::down-button:disabled { /*DOUBLESPINBOX*/ QDoubleSpinBox { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(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); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 5); + border: 1px solid rgba(0, 0, 0, 10); + color: rgba(0, 0, 0, 200); border-bottom: 2px solid %1; } @@ -373,11 +395,11 @@ QDoubleSpinBox::up-button { } QDoubleSpinBox::up-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QDoubleSpinBox::up-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QDoubleSpinBox::down-button { @@ -394,11 +416,11 @@ QDoubleSpinBox::down-button { } QDoubleSpinBox::down-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QDoubleSpinBox::down-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QDoubleSpinBox::drop-down { @@ -407,9 +429,9 @@ QDoubleSpinBox::drop-down { } QDoubleSpinBox:disabled { - color: rgb(0, 0, 0, 110); - background-color: rgb(0, 0, 0, 13); - border: 1px solid rgb(0, 0, 0, 5); + color: rgba(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } QDoubleSpinBox::up-button:disabled { @@ -422,26 +444,26 @@ QDoubleSpinBox::down-button:disabled { /*DATETIMEEDIT*/ QDateTimeEdit { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(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); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 5); + border: 1px solid rgba(0, 0, 0, 10); + color: rgba(0, 0, 0, 200); border-bottom: 2px solid %1; } @@ -459,11 +481,11 @@ QDateTimeEdit::up-button { } QDateTimeEdit::up-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QDateTimeEdit::up-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QDateTimeEdit::down-button { @@ -480,11 +502,11 @@ QDateTimeEdit::down-button { } QDateTimeEdit::down-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QDateTimeEdit::down-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QDateTimeEdit::drop-down { @@ -493,9 +515,9 @@ QDateTimeEdit::drop-down { } QDateTimeEdit:disabled { - color: rgb(0, 0, 0, 110); - background-color: rgb(0, 0, 0, 13); - border: 1px solid rgb(0, 0, 0, 5); + color: rgba(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } QDateTimeEdit::up-button:disabled { @@ -514,7 +536,7 @@ QSlider:vertical { QSlider::groove:vertical { width: 5px; - background-color: rgb(0, 0, 0, 100); + background-color: rgba(0, 0, 0, 100); border-radius: 2px; } @@ -546,7 +568,7 @@ QSlider::handle:vertical:pressed { } QSlider::groove:vertical:disabled { - background-color: rgb(0, 0, 0, 75); + background-color: rgba(0, 0, 0, 75); } QSlider::handle:vertical:disabled { @@ -562,7 +584,7 @@ QSlider:horizontal { QSlider::groove:horizontal { height: 5px; - background-color: rgb(0, 0, 0, 100); + background-color: rgba(0, 0, 0, 100); border-radius: 2px; } @@ -594,7 +616,7 @@ QSlider::handle:horizontal:pressed { } QSlider::groove:horizontal:disabled { - background-color: rgb(0, 0, 0, 75); + background-color: rgba(0, 0, 0, 75); } QSlider::handle:horizontal:disabled { @@ -617,8 +639,8 @@ QProgressBar::chunk { /*COMBOBOX*/ QComboBox { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(0, 0, 0, 13); border-radius: 5px; padding-left: 10px; min-height: 38px; @@ -626,12 +648,12 @@ QComboBox { } QComboBox:hover { - background-color: rgb(0, 0, 0, 13); - border: 1px solid rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); } QComboBox::pressed { - border: 1px solid rgb(0, 0, 0, 10); + border: 1px solid rgba(0, 0, 0, 10); } QComboBox::down-arrow { @@ -644,61 +666,94 @@ QComboBox::drop-down { } QComboBox:disabled { - color: rgb(0, 0, 0, 110); - background-color: rgb(0, 0, 0, 13); - border: 1px solid rgb(0, 0, 0, 5); + color: rgba(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } QComboBox::down-arrow:disabled { image: url(:/resources/win/light/ComboBoxDisabled.png); } + +/*COMBOBOX POPUP*/ +QComboBox QAbstractItemView { + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(0, 0, 0, 13); + border-radius: 5px; + padding: 3px; + outline: 0; +} + +QComboBox QAbstractItemView::item { + background-color: transparent; + padding: 5px 10px; + min-height: 26px; + border-radius: 4px; + color: rgb(0, 0, 0); +} + +QComboBox QAbstractItemView::item:selected { + background-color: rgba(0, 0, 0, 13); + color: rgb(0, 0, 0); +} + +QComboBox QAbstractItemView::item:!selected:hover { + background-color: rgba(0, 0, 0, 10); + color: rgb(0, 0, 0); +} + +QComboBox QAbstractItemView::item:disabled { + color: rgba(0, 0, 0, 150); +} + /*LINEEDIT*/ QLineEdit { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); - font-size: 16px; + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(0, 0, 0, 13); + font-size: 14px; font-family: "Segoe UI", serif; font-weight: 500; border-radius: 7px; - border-bottom: 1px solid rgb(0, 0, 0, 100); + border-bottom: 1px solid rgba(0, 0, 0, 100); padding-top: 0px; padding-left: 5px; + min-height: 30px; } 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); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 5); + border-top: 1px solid rgba(0, 0, 0, 13); + border-left: 1px solid rgba(0, 0, 0, 13); + border-right: 1px solid rgba(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); + color: rgba(0, 0, 0, 150); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } /*SCROLLVERTICAL*/ QScrollBar:vertical { - border: 6px solid rgb(0, 0, 0, 0); + border: 6px solid rgba(0, 0, 0, 0); margin: 14px 0px 14px 0px; width: 16px; } QScrollBar:vertical:hover { - border: 5px solid rgb(0, 0, 0, 0); + border: 5px solid rgba(0, 0, 0, 0); } QScrollBar::handle:vertical { - background-color: rgb(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 110); border-radius: 2px; min-height: 25px; } @@ -737,17 +792,17 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { /*SCROLLHORIZONTAL*/ QScrollBar:horizontal { - border: 6px solid rgb(0, 0, 0, 0); + border: 6px solid rgba(0, 0, 0, 0); margin: 0px 14px 0px 14px; height: 16px; } QScrollBar:horizontal:hover { - border: 5px solid rgb(0, 0, 0, 0); + border: 5px solid rgba(0, 0, 0, 0); } QScrollBar::handle:horizontal { - background-color: rgb(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 110); border-radius: 2px; min-width: 25px; } @@ -786,34 +841,34 @@ QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { /*TEXTEDIT*/ QTextEdit { - background-color: rgb(0, 0, 0, 7); - border: 1px solid rgb(0, 0, 0, 13); - font-size: 16px; + background-color: rgba(0, 0, 0, 7); + border: 1px solid rgba(0, 0, 0, 13); + font-size: 14px; font-family: "Segoe UI", serif; font-weight: 500; border-radius: 7px; - border-bottom: 1px solid rgb(0, 0, 0, 100); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); + border-bottom: 1px solid rgba(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); + background-color: rgba(0, 0, 0, 5); + border-top: 1px solid rgba(0, 0, 0, 13); + border-left: 1px solid rgba(0, 0, 0, 13); + border-right: 1px solid rgba(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); + color: rgba(0, 0, 0, 110); + background-color: rgba(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 5); } /*CALENDAR*/ @@ -822,14 +877,14 @@ QCalendarWidget { QCalendarWidget QToolButton { height: 36px; - font-size: 18px; - background-color: rgb(0, 0, 0, 0); + font-size: 14px; + background-color: rgba(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); + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(0, 0, 0, 13); border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-left-radius: 0px; @@ -852,12 +907,12 @@ QCalendarWidget QMenu { } #qt_calendar_prevmonth:hover, #qt_calendar_nextmonth:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); border-radius: 5px; } #qt_calendar_prevmonth:pressed, #qt_calendar_nextmonth:pressed { - background-color: rgb(0, 0, 0, 7); + background-color: rgba(0, 0, 0, 7); border-radius: 5px; } @@ -868,12 +923,12 @@ QCalendarWidget QMenu { } #qt_calendar_yearbutton:hover, #qt_calendar_monthbutton:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); border-radius: 5px; } #qt_calendar_yearbutton:pressed, #qt_calendar_monthbutton:pressed { - background-color: rgb(0, 0, 0, 7); + background-color: rgba(0, 0, 0, 7); border-radius: 5px; } @@ -886,9 +941,9 @@ QCalendarWidget QSpinBox { } QCalendarWidget QSpinBox::focus { - background-color: rgb(0, 0, 0, 5); - border: 1px solid rgb(0, 0, 0, 10); - color: rgb(0, 0, 0, 200); + background-color: rgba(0, 0, 0, 5); + border: 1px solid rgba(0, 0, 0, 10); + color: rgba(0, 0, 0, 200); border-bottom: 2px solid %1; } @@ -906,11 +961,11 @@ QCalendarWidget QSpinBox::up-button { } QCalendarWidget QSpinBox::up-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QCalendarWidget QSpinBox::up-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QCalendarWidget QSpinBox::down-button { @@ -927,22 +982,22 @@ QCalendarWidget QSpinBox::down-button { } QCalendarWidget QSpinBox::down-button:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } QCalendarWidget QSpinBox::down-button:pressed { - background-color: rgb(0, 0, 0, 5); + background-color: rgba(0, 0, 0, 5); } QCalendarWidget QWidget { - alternate-background-color: rgb(0, 0, 0, 0); + alternate-background-color: rgba(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: 1px solid rgba(0, 0, 0, 10); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-left-radius: 5px; @@ -954,7 +1009,7 @@ 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: 1px solid rgba(0, 0, 0, 13); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-left-radius: 5px; @@ -962,7 +1017,7 @@ QCalendarWidget QAbstractItemView:disabled { } #qt_calendar_yearbutton:disabled, #qt_calendar_monthbutton:disabled { - color: rgb(0, 0, 0, 110); + color: rgba(0, 0, 0, 110); } #qt_calendar_prevmonth:disabled { @@ -976,7 +1031,7 @@ QCalendarWidget QAbstractItemView:disabled { /*TREEWIDGET*/ QTreeView { background-color: transparent; - border: 1px solid rgb(0, 0, 0, 13); + border: 1px solid rgba(0, 0, 0, 13); border-radius: 5px; outline: 0; padding-right: 5px; @@ -989,14 +1044,14 @@ QTreeView::item { QTreeView::item:selected { color: rgb(0, 0, 0); - background-color: rgb(0, 0, 0, 7); + background-color: rgba(0, 0, 0, 7); border-radius: 5px; margin-bottom: 3px; padding-left: 0px; } QTreeView::item:!selected:hover { - background-color: rgb(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 13); border-radius: 5px; margin-bottom: 3px; padding-left: 0px; @@ -1013,7 +1068,7 @@ QTreeView::branch:open:has-children:has-siblings { } QTreeView:disabled { - color: rgb(0, 0, 0, 110); + color: rgba(0, 0, 0, 110); } /*TOGGLESWITCH*/ @@ -1029,7 +1084,7 @@ QTreeView:disabled { height: 22px; border-radius: 13px; border: 2px solid #999999; - background-color: rgb(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); image: url(:/resources/win/light/ToggleSwitchOff.png); margin-right: 5px; padding-right: 25px; @@ -1037,12 +1092,12 @@ QTreeView:disabled { } #toggleSwitch::indicator:hover { - background-color: rgb(0, 0, 0, 15); + background-color: rgba(0, 0, 0, 15); image: url(:/resources/win/light/ToggleSwitchOffHover.png); } #toggleSwitch::indicator:pressed { - background-color: rgb(0, 0, 0, 24); + background-color: rgba(0, 0, 0, 24); width: 26px; padding-right: 21px; image: url(:/resources/win/light/ToggleSwitchOffPressed.png); @@ -1070,7 +1125,7 @@ QTreeView:disabled { } #toggleSwitch:disabled { - color: rgb(0, 0, 0, 110); + color: rgba(0, 0, 0, 110); } #toggleSwitch::indicator:disabled { @@ -1089,22 +1144,22 @@ QTreeView:disabled { } #hyperlinkButton:hover { - background-color: rgb(0, 0, 0, 10); + background-color: rgba(0, 0, 0, 10); } #hyperlinkButton::pressed { - background-color: rgb(0, 0, 0, 7); + background-color: rgba(0, 0, 0, 7); color: %1; } #hyperlinkButton:disabled { - color: rgb(0, 0, 0, 110) + color: rgba(0, 0, 0, 110); } /*LISTVIEW*/ QListView { background-color: transparent; - font-size: 17px; + font-size: 14px; font-family: "Segoe UI Variable Small", serif; font-weight: 400; padding: 7px; @@ -1121,4 +1176,17 @@ QListView::item:selected { color: black; border-radius: 5px; padding-left: 0px; -} \ No newline at end of file +} + +QWidget#navWidget { + border-radius: 20px; + border: 1px solid rgba(0, 0, 0, 13); + background-color: rgba(0, 0, 0, 10); + padding-left: 10px; + padding-right: 10px; +} + +QWidget#VirtualLocationWidget { + background-color: rgb(255, 255, 255); +} + diff --git a/src/afcexplorerwidget.cpp b/src/afcexplorerwidget.cpp index 6fd5b50..dd59edf 100644 --- a/src/afcexplorerwidget.cpp +++ b/src/afcexplorerwidget.cpp @@ -593,14 +593,11 @@ void AfcExplorerWidget::setupFileExplorer() navLayout->setContentsMargins(0, 0, 0, 0); navLayout->setSpacing(0); - // Create navigation buttons using ClickableIconWidget QWidget *explorerLeftSideNavButtons = new QWidget(); QHBoxLayout *leftNavLayout = new QHBoxLayout(explorerLeftSideNavButtons); - // explorerLeftSideNavButtons->setStyleSheet("border-right: 1px solid - // red;"); + leftNavLayout->setContentsMargins(0, 0, 0, 0); leftNavLayout->setSpacing(1); - // rename to ziconwidget m_backButton = new ZIconWidget( QIcon(":/resources/icons/MaterialSymbolsArrowLeftAlt.png"), "Go Back"); m_backButton->setEnabled(false); @@ -654,9 +651,21 @@ void AfcExplorerWidget::setupFileExplorer() // File list m_fileList = new QListWidget(); m_fileList->setSelectionMode(QAbstractItemView::ExtendedSelection); - - QScrollBar *vBar = m_fileList->QAbstractScrollArea::verticalScrollBar(); - vBar->setStyleSheet(styleSheet()); +#ifdef WIN32 + m_fileList->setStyleSheet(R"( + QScrollBar:vertical { + border: 6px solid rgba(0, 0, 0, 0); + margin: 14px 0px 14px 0px; + width: 16px; + background-color: transparent; + } + QScrollBar::handle:vertical { + background-color: rgba(0, 0, 0, 110); + border-radius: 2px; + min-height: 25px; + } + )"); +#endif fileListLayout->addWidget(m_fileList); // Create error widget @@ -720,8 +729,10 @@ void AfcExplorerWidget::setupFileExplorer() } updateNavigationButtons(); - updateButtonStates(); // Initialize button states + updateButtonStates(); +#ifndef WIN32 updateNavStyles(); +#endif } void AfcExplorerWidget::onAddToFavoritesClicked() @@ -744,6 +755,7 @@ void AfcExplorerWidget::onAddToFavoritesClicked() } } +#ifndef WIN32 void AfcExplorerWidget::updateNavStyles() { if (!m_navWidget || !m_addressBar) @@ -768,7 +780,8 @@ void AfcExplorerWidget::updateNavStyles() .arg(bgColor.lighter().name()) .arg(accentColor.name()); - m_navWidget->setStyleSheet(navStyles); + if (m_navWidget->styleSheet() != navStyles) + m_navWidget->setStyleSheet(navStyles); // Update address bar styles to complement the nav widget QString addressBarStyles = @@ -779,9 +792,10 @@ void AfcExplorerWidget::updateNavStyles() .arg(borderColor.lighter().name()) .arg(isDark ? QColor(Qt::black).name() : QColor(Qt::white).name()) .arg(COLOR_ACCENT_BLUE.name()); - - m_addressBar->setStyleSheet(addressBarStyles); + if (m_addressBar->styleSheet() != addressBarStyles) + m_addressBar->setStyleSheet(addressBarStyles); } +#endif void AfcExplorerWidget::updateButtonStates() { diff --git a/src/afcexplorerwidget.h b/src/afcexplorerwidget.h index 51f0c66..b448e18 100644 --- a/src/afcexplorerwidget.h +++ b/src/afcexplorerwidget.h @@ -114,9 +114,10 @@ private: const char *local_path); int importFileToDevice(AfcClientHandle *afc, const char *device_path, const char *local_path); - void updateNavStyles(); void updateButtonStates(); void goUp(); +#ifndef WIN32 + void updateNavStyles(); protected: void changeEvent(QEvent *event) override @@ -126,6 +127,6 @@ protected: } QWidget::changeEvent(event); } +#endif }; - #endif // AFCEXPLORER_H diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 4224477..0a98ad6 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -446,18 +446,8 @@ void AppContext::addDevice(iDescriptor::Uniq uniq, } qDebug() << "Device initialized: " << uniq; - /* - We need this because wireless devices get initialized - with Mac addresses. Even though a Mac address is unique, we - are better off using the "UDID" as the unique identifier. - This is only required for wireless devices, Usb devices - already use the UDID but it doesn't hurt to set it for them - as well - */ - uniq.set(initResult->deviceInfo.UniqueDeviceID, false); - iDescriptorDevice *device = new iDescriptorDevice{ - .udid = uniq.get().toStdString(), + .udid = initResult->deviceInfo.UniqueDeviceID, .conn_type = conn_type, .provider = initResult->provider, .deviceInfo = initResult->deviceInfo, @@ -502,24 +492,36 @@ int AppContext::getConnectedDeviceCount() const // #endif } -void AppContext::removeDevice(QString _udid) +void AppContext::removeDevice(iDescriptor::Uniq uniq) { - const std::string udid = _udid.toStdString(); - qDebug() << "AppContext::removeDevice device with UUID:" - << QString::fromStdString(udid); + qDebug() << "AppContext::removeDevice device with" + << (uniq.isMac() ? "MAC" : "UDID") << uniq.get(); - if (m_pendingDevices.contains(_udid)) { - m_pendingDevices.removeAll(_udid); - emit devicePairingExpired(_udid); + std::string udid = uniq.isUdid() ? uniq.get().toStdString() : ""; + QString q_udid = QString::fromStdString(udid); + + if (uniq.isMac()) { + const iDescriptorDevice *device = getDeviceByMacAddress(uniq.get()); + if (device) { + udid = device->udid; + q_udid = QString::fromStdString(udid); + } else { + qDebug() << "Device with MAC " << uniq << " not found."; + } + } + + if (m_pendingDevices.contains(q_udid)) { + m_pendingDevices.removeAll(q_udid); + emit devicePairingExpired(q_udid); emit deviceChange(); return; } else { - qDebug() << "Device with UUID " + _udid + + qDebug() << "Device with UUID " + q_udid + " not found in pending devices."; } if (!m_devices.contains(udid)) { - qDebug() << "Device with UUID " + _udid + + qDebug() << "Device with UUID " + q_udid + " not found in normal devices."; return; } diff --git a/src/appcontext.h b/src/appcontext.h index 4461a30..4817851 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -90,7 +90,7 @@ signals: void currentDeviceSelectionChanged(const DeviceSelection &selection); void deviceHeartbeatFailed(const QString &macAddress, int tries); public slots: - void removeDevice(QString udid); + void removeDevice(iDescriptor::Uniq uniq); void addDevice(iDescriptor::Uniq udid, DeviceMonitorThread::IdeviceConnectionType connType, AddType addType, QString wifiMacAddress = QString(), diff --git a/src/appdownloaddialog.h b/src/appdownloaddialog.h index 2772494..6b96f9d 100644 --- a/src/appdownloaddialog.h +++ b/src/appdownloaddialog.h @@ -20,7 +20,7 @@ #ifndef APPDOWNLOADDIALOG_H #define APPDOWNLOADDIALOG_H -#include "appdownloadbasedialog.h" +#include "base/appdownload.h" #include "iDescriptor-ui.h" #include "zloadingwidget.h" #include diff --git a/src/appinstalldialog.cpp b/src/appinstalldialog.cpp index 5d4a4b8..24d60ad 100644 --- a/src/appinstalldialog.cpp +++ b/src/appinstalldialog.cpp @@ -19,7 +19,6 @@ #include "appinstalldialog.h" #include "appcontext.h" -#include "appdownloadbasedialog.h" #include "iDescriptor.h" #include "servicemanager.h" #include diff --git a/src/appinstalldialog.h b/src/appinstalldialog.h index 5b955e0..ecb3ea8 100644 --- a/src/appinstalldialog.h +++ b/src/appinstalldialog.h @@ -20,7 +20,7 @@ #ifndef APPINSTALLDIALOG_H #define APPINSTALLDIALOG_H -#include "appdownloadbasedialog.h" +#include "base/appdownload.h" #include "iDescriptor.h" #include #include diff --git a/src/appswidget.cpp b/src/appswidget.cpp index c0d7388..222efc9 100644 --- a/src/appswidget.cpp +++ b/src/appswidget.cpp @@ -19,7 +19,6 @@ #include "appswidget.h" #include "appcontext.h" -#include "appdownloadbasedialog.h" #include "appdownloaddialog.h" #include "appinstalldialog.h" #include "appstoremanager.h" @@ -87,8 +86,8 @@ void AppsWidget::setupUI() QWidget *headerWidget = new QWidget(); headerWidget->setFixedHeight(60); - headerWidget->setStyleSheet( - "border-bottom: 1px solid #363d32; border-radius: 0px;"); + // headerWidget->setStyleSheet( + // "border-bottom: 1px solid #363d32; border-radius: 0px;"); QHBoxLayout *headerLayout = new QHBoxLayout(headerWidget); headerLayout->setContentsMargins(20, 10, 20, 10); @@ -210,11 +209,13 @@ void AppsWidget::handleInit() m_statusLabel->setText("Failed to initialize"); m_loginButton->setText("Failed to initialize"); m_loginButton->setEnabled(false); +#ifndef WIN32 m_loginButton->setStyleSheet( "background-color: #ccc; color: #666; " "border: " "none; border-radius: " "4px; padding: 8px 16px; font-size: 14px;"); +#endif return; } /* @@ -606,6 +607,7 @@ void AppsWidget::createAppCard( // App name with sponsor indicator QHBoxLayout *nameLayout = new QHBoxLayout(); QLabel *nameLabel = new QLabel(name); + // nameLabel->font().setSize(16); nameLabel->setStyleSheet("font-size: 16px;"); nameLabel->setWordWrap(true); nameLayout->addWidget(nameLabel); diff --git a/src/appdownloadbasedialog.cpp b/src/base/appdownload.cpp similarity index 98% rename from src/appdownloadbasedialog.cpp rename to src/base/appdownload.cpp index 64b7d28..4091a5b 100644 --- a/src/appdownloadbasedialog.cpp +++ b/src/base/appdownload.cpp @@ -17,8 +17,7 @@ * along with this program. If not, see . */ -#include "appdownloadbasedialog.h" -#include "appstoremanager.h" +#include "appdownload.h" #include #include #include @@ -59,6 +58,9 @@ AppDownloadBaseDialog::AppDownloadBaseDialog(const QString &appName, { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(20, 20, 20, 20); +#ifdef WIN32 + setupWinWindow(this); +#endif } void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId, diff --git a/src/appdownloadbasedialog.h b/src/base/appdownload.h similarity index 97% rename from src/appdownloadbasedialog.h rename to src/base/appdownload.h index 3104b4f..80192d1 100644 --- a/src/appdownloadbasedialog.h +++ b/src/base/appdownload.h @@ -20,6 +20,8 @@ #ifndef APPDOWNLOADBASEDIALOG_H #define APPDOWNLOADBASEDIALOG_H +#include "../appstoremanager.h" +#include "../iDescriptor-ui.h" #include #include #include diff --git a/src/base/tool.cpp b/src/base/tool.cpp index 1978828..056077a 100644 --- a/src/base/tool.cpp +++ b/src/base/tool.cpp @@ -23,13 +23,15 @@ #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); +#elif defined(WIN32) + setupWinWindow(this); + setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); +#else + #endif } diff --git a/src/base/tool.h b/src/base/tool.h index fe0f280..491ab89 100644 --- a/src/base/tool.h +++ b/src/base/tool.h @@ -30,12 +30,7 @@ #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/batterywidget.cpp b/src/batterywidget.cpp index 15b2404..b659a4a 100644 --- a/src/batterywidget.cpp +++ b/src/batterywidget.cpp @@ -137,9 +137,13 @@ void BatteryWidget::paintEvent(QPaintEvent *) pen.setColor(palette().color(QPalette::Text)); painter.setPen(pen); +#ifndef WIN32 QFont textFont = QFont(); - textFont.setPixelSize(widgetFrame.height() / 1.65); textFont.setWeight(QFont::Bold); +#else + QFont textFont = QFont("Segoe UI Variable Small", 12); +#endif + textFont.setPixelSize(widgetFrame.height() / 1.65); painter.setFont(textFont); QFontMetrics fm(textFont); QString percentageLevelString = QString("%1%").arg(m_value); diff --git a/src/cableinfowidget.cpp b/src/cableinfowidget.cpp index 073c175..6ed6586 100644 --- a/src/cableinfowidget.cpp +++ b/src/cableinfowidget.cpp @@ -26,6 +26,10 @@ #include #include +#ifdef WIN32 +#include "platform/windows/win_common.h" +#endif + CableInfoWidget::CableInfoWidget(iDescriptorDevice *device, QWidget *parent) : Tool(parent), m_device(device), m_response(nullptr) { @@ -43,6 +47,9 @@ CableInfoWidget::CableInfoWidget(iDescriptorDevice *device, QWidget *parent) void CableInfoWidget::setupUI() { setWindowTitle("Cable Information - iDescriptor"); + + setMinimumSize(500, 400); + m_mainLayout = new QVBoxLayout(); m_mainLayout->setSpacing(20); m_mainLayout->setContentsMargins(20, 20, 20, 20); @@ -81,7 +88,7 @@ void CableInfoWidget::setupUI() m_loadingWidget = new ZLoadingWidget(true, this); m_loadingWidget->setupContentWidget(m_mainLayout); - QVBoxLayout *layout = new QVBoxLayout(contentWidget()); + QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_loadingWidget); diff --git a/src/core/services/get_file_tree.cpp b/src/core/services/get_file_tree.cpp index 28afbb8..224cb5f 100644 --- a/src/core/services/get_file_tree.cpp +++ b/src/core/services/get_file_tree.cpp @@ -37,7 +37,7 @@ AFCFileTree get_file_tree(const iDescriptorDevice *device, bool checkDir, // Use safe wrapper to read directory IdeviceFfiError *err = ServiceManager::safeAfcReadDirectory( - device, path.c_str(), &dirs, count, altAfc); + device, path.c_str(), &dirs, &count, altAfc); if (err) { qDebug() << "Failed to read directory:" << path.c_str() @@ -94,7 +94,8 @@ AFCFileTree get_file_tree(const iDescriptorDevice *device, bool checkDir, // it's a dir IdeviceFfiError *link_err = ServiceManager::safeAfcReadDirectory( - device, fullPath.c_str(), &dir_contents, count, altAfc); + device, fullPath.c_str(), &dir_contents, &count, + altAfc); if (!link_err) { isDir = true; diff --git a/src/core/services/init_device.cpp b/src/core/services/init_device.cpp index b5827c2..23c17c2 100644 --- a/src/core/services/init_device.cpp +++ b/src/core/services/init_device.cpp @@ -120,16 +120,13 @@ void parseOldDevice(PlistNavigator &ioreg, DeviceInfo &d) parseOldDeviceBattery(ioreg, d); } -// this is reused in the ui in deviceinfowidget void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d) { d.batteryInfo.isCharging = ioreg["IsCharging"].getBool(); d.batteryInfo.fullyCharged = ioreg["FullyCharged"].getBool(); - qDebug() << "Stalebatteryinfo:" - << ioreg["BatteryData"]["StateOfCharge"].getUInt(); - /* data is stale here so we need to calculate */ + /* data is sometimes not accurate here so we need to calculate */ // d.batteryInfo.currentBatteryLevel = // ioreg["BatteryData"]["StateOfCharge"].getUInt(); diff --git a/src/devdiskimagehelper.cpp b/src/devdiskimagehelper.cpp index 1d7380a..57326d0 100644 --- a/src/devdiskimagehelper.cpp +++ b/src/devdiskimagehelper.cpp @@ -34,6 +34,9 @@ DevDiskImageHelper::DevDiskImageHelper(const iDescriptorDevice *device, : QDialog(parent), m_device(device) { setAttribute(Qt::WA_DeleteOnClose); +#ifdef WIN32 + setupWinWindow(this); +#endif setWindowTitle("Developer Disk Image - iDescriptor"); setupUI(); diff --git a/src/devdiskimagehelper.h b/src/devdiskimagehelper.h index 2c1b9ec..a6e425f 100644 --- a/src/devdiskimagehelper.h +++ b/src/devdiskimagehelper.h @@ -20,6 +20,7 @@ #ifndef DEVDISKIMAGEHELPER_H #define DEVDISKIMAGEHELPER_H +#include "iDescriptor-ui.h" #include "iDescriptor.h" #include #include diff --git a/src/deviceimagewidget.cpp b/src/deviceimagewidget.cpp index fa45a27..2468841 100644 --- a/src/deviceimagewidget.cpp +++ b/src/deviceimagewidget.cpp @@ -317,7 +317,11 @@ QPixmap DeviceImageWidget::createCompositeImage() const QString currentTime = QDateTime::currentDateTime().toString("hh:mm"); QFont timeFont; +#ifndef WIN32 timeFont.setFamily("SF Pro Display, Helvetica, Arial"); +#else + timeFont.setFamily("Segoe UI"); +#endif int fontSize = screenRect.width() / 5; timeFont.setPointSize(fontSize); timeFont.setWeight(QFont::Light); diff --git a/src/deviceinfowidget.cpp b/src/deviceinfowidget.cpp index c72dac6..88b89e2 100644 --- a/src/deviceinfowidget.cpp +++ b/src/deviceinfowidget.cpp @@ -112,7 +112,6 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, // Right side: Info Table QWidget *infoContainer = new QWidget(); - // 2. Change the horizontal size policy from Expanding to Preferred infoContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); QVBoxLayout *infoLayout = new QVBoxLayout(infoContainer); @@ -127,7 +126,12 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, new QLabel(QString::fromStdString(device->deviceInfo.productType)); devProductType->setToolTip( QString::fromStdString(device->deviceInfo.marketingName)); +#ifndef WIN32 devProductType->setStyleSheet("font-size: 1rem; font-weight: bold;"); +#else + devProductType->setStyleSheet( + mergeStyles(devProductType, "font-size: 18px; font-weight: 500;")); +#endif QLabel *diskCapacityLabel = new QLabel( QString::number(device->deviceInfo.diskInfo.totalDiskCapacity / @@ -137,7 +141,6 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, diskCapacityLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); diskCapacityLabel->setAttribute(Qt::WA_StyledBackground, true); - // background-color: rgba(0, 255, 30, 0.5); diskCapacityLabel->setStyleSheet(QString("background-color: %1;" "padding: 2px 4px;" "color : white;" @@ -151,9 +154,11 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, m_chargingStatusLabel->setStyleSheet(mergeStyles( m_chargingStatusLabel, (device->deviceInfo.batteryInfo.isCharging - ? QString("QLabel#ChargingStatusLabel { color: %1; }") + ? QString( + "QLabel#ChargingStatusLabel { color: %1; font-size: 14px; }") .arg(COLOR_GREEN.name()) - : QString("QLabel#ChargingStatusLabel { color: %1; }") + : QString( + "QLabel#ChargingStatusLabel { color: %1; font-size: 14px; }") .arg(qApp->palette().color(QPalette::WindowText).name())))); // Create the layout without a parent widget QHBoxLayout *chargingLayout = new QHBoxLayout(); @@ -246,8 +251,6 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, infoItems.append({"Device Class:", createValueLabel(QString::fromStdString( device->deviceInfo.deviceClass))}); - infoItems.append({"Device Color:", createValueLabel(QString::fromStdString( - device->deviceInfo.deviceColor))}); infoItems.append( {"Jailbroken:", createValueLabel(QString::fromStdString( device->deviceInfo.jailbroken ? "Yes" : "No"))}); @@ -273,20 +276,22 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, {"Firmware Version:", createValueLabel(QString::fromStdString( device->deviceInfo.firmwareVersion))}); - // // FIXME: Battery Info - // // QWidget *batteryWidget = new QWidget(); - // // QHBoxLayout *batteryLayout = new QHBoxLayout(batteryWidget); - // // batteryLayout->setContentsMargins(0, 0, 0, 0); - // // batteryLayout->setSpacing(5); - // // batteryLayout->addWidget(new - // // QLabel(device->deviceInfo.batteryInfo.health)); QPushButton - // *moreButton = - // // new QPushButton("More"); connect(moreButton, &QPushButton::clicked, - // this, - // // &DeviceInfoWidget::onBatteryMoreClicked); - // // batteryLayout->addWidget(moreButton); - // // batteryLayout->addStretch(); - // // infoItems.append({"Battery Health:", batteryWidget}); + // FIXME: Battery Info + QWidget *batteryWidget = new QWidget(); + QHBoxLayout *batteryLayout = new QHBoxLayout(batteryWidget); + batteryLayout->setContentsMargins(0, 0, 0, 0); + batteryLayout->setSpacing(5); + batteryLayout->addWidget(new QLabel(device->deviceInfo.batteryInfo.health)); + QPushButton *moreButton = new QPushButton("More"); + moreButton->setStyleSheet(mergeStyles( + moreButton, + "QPushButton { height : 20px; min-height: 20px; padding: 2px " + "8px; font-size: 10px; }")); + connect(moreButton, &QPushButton::clicked, this, + &DeviceInfoWidget::onBatteryMoreClicked); + batteryLayout->addWidget(moreButton); + batteryLayout->addStretch(); + infoItems.append({"Battery Health:", batteryWidget}); infoItems.append( {"Production Device:", @@ -362,10 +367,10 @@ DeviceInfoWidget::DeviceInfoWidget(const iDescriptorDevice *device, mainLayout->addLayout(rightSideLayout); mainLayout->addStretch(); - // m_updateTimer = new QTimer(this); - // connect(m_updateTimer, &QTimer::timeout, this, - // &DeviceInfoWidget::updateBatteryInfo); - // m_updateTimer->start(30000); // Update every 30 seconds + m_updateTimer = new QTimer(this); + connect(m_updateTimer, &QTimer::timeout, this, + &DeviceInfoWidget::updateBatteryInfo); + m_updateTimer->start(30000); // Update every 30 seconds } DeviceInfoWidget::~DeviceInfoWidget() {} @@ -385,47 +390,50 @@ void DeviceInfoWidget::onBatteryMoreClicked() void DeviceInfoWidget::updateBatteryInfo() { - // qDebug() << "Updating battery info..."; - // plist_t diagnostics = nullptr; - // get_battery_info(m_device->deviceInfo.rawProductType, m_device->device, - // m_device->deviceInfo.is_iPhone, diagnostics); + qDebug() << "Updating battery info..."; + plist_t diagnostics = nullptr; + // DONT BLOCK + get_battery_info(m_device->diagRelay.get(), diagnostics); - // if (!diagnostics) { - // qDebug() << "Failed to get diagnostics plist."; - // return; - // } - // /*DATA*/ - // DeviceInfo &d = m_device->deviceInfo; - // qDebug() << "old device" << d.oldDevice; - // PlistNavigator ioreg = PlistNavigator(diagnostics)["IORegistry"]; - // // if (d.oldDevice) - // // parseOldDeviceBattery(ioreg, d); - // // else - // // parseDeviceBattery(ioreg, d); - // /*UI*/ - // updateChargingStatusIcon(); - // m_chargingWattsWithCableTypeLabel->setText( - // QString::number(d.batteryInfo.watts) + "W" + "/" + - // (d.batteryInfo.usbConnectionType == BatteryInfo::ConnectionType::USB - // ? "USB" - // : "USB-C")); + if (!diagnostics) { + qDebug() << "Failed to get diagnostics plist."; + return; + } + /*DATA*/ + DeviceInfo &d = const_cast(m_device->deviceInfo); + qDebug() << "old device" << d.oldDevice; + PlistNavigator ioreg = PlistNavigator(diagnostics); + if (d.oldDevice) + ServiceManager::safeParseOldDeviceBattery(m_device, ioreg, d); + else + ServiceManager::safeParseDeviceBattery(m_device, ioreg, d); + /*UI*/ + updateChargingStatusIcon(); + m_chargingWattsWithCableTypeLabel->setText( + QString::number(d.batteryInfo.watts) + "W" + "/" + + (d.batteryInfo.usbConnectionType == BatteryInfo::ConnectionType::USB + ? "USB" + : "USB-C")); - // m_batteryWidget->updateContext( - // d.batteryInfo.isCharging, - // qBound(1, d.batteryInfo.currentBatteryLevel, 100)); + m_batteryWidget->updateContext( + d.batteryInfo.isCharging, + qBound(1, d.batteryInfo.currentBatteryLevel, 100)); + // FIXME: does diag c++ wrappers free this already ? + // plist_free(diagnostics); } void DeviceInfoWidget::updateChargingStatusIcon() { - // if (m_device->deviceInfo.batteryInfo.isCharging) { - // m_chargingStatusLabel->setText("Charging"); - // m_chargingStatusLabel->setStyleSheet( - // QString("color: %1;").arg(COLOR_GREEN.name())); - // m_lightningIconLabel->show(); + if (m_device->deviceInfo.batteryInfo.isCharging) { + m_chargingStatusLabel->setText("Charging"); + // FIXME + // m_chargingStatusLabel->setStyleSheet( + // QString("color: %1;").arg(COLOR_GREEN.name())); + m_lightningIconLabel->show(); - // } else { - // m_chargingStatusLabel->setText("Not Charging"); - // m_chargingStatusLabel->setStyleSheet(""); - // m_lightningIconLabel->hide(); - // } + } else { + m_chargingStatusLabel->setText("Not Charging"); + // m_chargingStatusLabel->setStyleSheet(""); + m_lightningIconLabel->hide(); + } } diff --git a/src/deviceinfowidget.h b/src/deviceinfowidget.h index 0f7d7e4..932fbd9 100644 --- a/src/deviceinfowidget.h +++ b/src/deviceinfowidget.h @@ -23,6 +23,7 @@ #include "deviceimagewidget.h" #include "iDescriptor-ui.h" #include "iDescriptor.h" +#include "servicemanager.h" #include #include #include diff --git a/src/devicesidebarwidget.cpp b/src/devicesidebarwidget.cpp index e47c4c7..3abed26 100644 --- a/src/devicesidebarwidget.cpp +++ b/src/devicesidebarwidget.cpp @@ -171,7 +171,9 @@ void DeviceSidebarItem::setupUI() void DeviceSidebarItem::setSelected(bool selected) { m_selected = selected; + bool dark = isDarkMode(); +#ifndef WIN32 if (selected) { setStyleSheet( "QFrame#DeviceSidebarItem { background-color: rgba(255, " @@ -181,6 +183,27 @@ void DeviceSidebarItem::setSelected(bool selected) "QFrame#DeviceSidebarItem { background-color: rgba(255, " "255, 255, 16); }"); } +#else + if (selected) { + if (!dark) { + setStyleSheet("QFrame#DeviceSidebarItem { background-color: " + "rgba(0, 0, 0, 30); }"); + } else { + setStyleSheet( + "QFrame#DeviceSidebarItem { background-color: rgba(255, " + "255, 255, 45); }"); + } + } else { + if (!dark) { + setStyleSheet("QFrame#DeviceSidebarItem { background-color: " + "rgba(0, 0, 0, 10); }"); + } else { + setStyleSheet( + "QFrame#DeviceSidebarItem { background-color: rgba(255, " + "255, 255, 16); }"); + } + } +#endif } void DeviceSidebarItem::setCollapsed(bool collapsed) diff --git a/src/diagnosewidget.cpp b/src/diagnosewidget.cpp index d2ebd06..22e9ce6 100644 --- a/src/diagnosewidget.cpp +++ b/src/diagnosewidget.cpp @@ -298,7 +298,9 @@ void DiagnoseWidget::checkDependencies(bool autoExpand) QString("All dependencies are installed/activated (%1/%2)") .arg(installedCount) .arg(totalCount)); - m_summaryLabel->setStyleSheet("color: green; font-weight: bold;"); + m_summaryLabel->setStyleSheet( + QString("color: %1; font-weight: bold;") + .arg(COLOR_GREEN.name())); if (m_isExpanded && autoExpand) { onToggleExpand(); } @@ -307,7 +309,8 @@ void DiagnoseWidget::checkDependencies(bool autoExpand) QString("Missing dependencies (%1/%2 installed)") .arg(installedCount) .arg(totalCount)); - m_summaryLabel->setStyleSheet("color: red; font-weight: bold;"); + m_summaryLabel->setStyleSheet( + QString("color: %1; font-weight: bold;").arg(COLOR_RED.name())); if (!m_isExpanded && autoExpand) { onToggleExpand(); } diff --git a/src/dirpickerlabel.cpp b/src/dirpickerlabel.cpp new file mode 100644 index 0000000..843626e --- /dev/null +++ b/src/dirpickerlabel.cpp @@ -0,0 +1,34 @@ +#include "dirpickerlabel.h" + +DirPickerLabel::DirPickerLabel(QWidget *parent, const QString &calloutString) + : QWidget{parent} +{ + // Directory selection UI + QHBoxLayout *dirLayout = new QHBoxLayout(); + QLabel *dirTextLabel = new QLabel(calloutString); + dirTextLabel->setStyleSheet("font-size: 14px;"); + dirLayout->addWidget(dirTextLabel); + + m_dirLabel = new ZLabel(this); + m_dirLabel->setText(m_outputDir); + m_dirLabel->setStyleSheet("font-size: 14px; color: #007AFF;"); + connect(m_dirLabel, &ZLabel::clicked, this, [this]() { + QDesktopServices::openUrl(QUrl::fromLocalFile(m_outputDir)); + }); + m_dirLabel->setCursor(Qt::PointingHandCursor); + dirLayout->addWidget(m_dirLabel, 1); + + m_dirButton = new QPushButton("Choose..."); + // m_dirButton->setStyleSheet("font-size: 14px; padding: 4px 12px;"); + connect(m_dirButton, &QPushButton::clicked, this, [this]() { + QString dir = QFileDialog::getExistingDirectory( + this, "Select Directory to Save IPA", m_outputDir); + if (!dir.isEmpty()) { + m_outputDir = dir; + m_dirLabel->setText(m_outputDir); + } + }); + dirLayout->addWidget(m_dirButton); + + setLayout(dirLayout); +} diff --git a/src/dirpickerlabel.h b/src/dirpickerlabel.h new file mode 100644 index 0000000..3b7c777 --- /dev/null +++ b/src/dirpickerlabel.h @@ -0,0 +1,28 @@ +#ifndef DIRPICKERLABEL_H +#define DIRPICKERLABEL_H + +#include "iDescriptor-ui.h" +#include +#include +#include +#include +#include +#include +#include + +class DirPickerLabel : public QWidget +{ +public: + explicit DirPickerLabel( + QWidget *parent = nullptr, + const QString &calloutString = QString("Export to:")); + QString getOutputDir() const { return m_outputDir; } + +private: + ZLabel *m_dirLabel; + QPushButton *m_dirButton; + QString m_outputDir = + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); +}; + +#endif // DIRPICKERLABEL_H diff --git a/src/diskusagewidget.cpp b/src/diskusagewidget.cpp index cf5333a..4379dc2 100644 --- a/src/diskusagewidget.cpp +++ b/src/diskusagewidget.cpp @@ -141,6 +141,8 @@ void DiskUsageWidget::setupUI() m_othersBar->setObjectName("othersBar"); m_freeBar->setObjectName("freeBar"); + bool dark = isDarkMode(); + // Set colors m_systemBar->setStyleSheet( "QWidget#systemBar { background-color: #a1384d; border: 1px solid" @@ -159,11 +161,12 @@ void DiskUsageWidget::setupUI() "QWidget#othersBar { background-color: #a28729; border: 1px solid " "#c4a32d; border-radius:0px; padding: 0; margin: 0; }"); m_freeBar->setStyleSheet( - "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; }"); + QString("QWidget#freeBar { background-color: %1; border: " + "1px solid " + "#4f4f4f4f; padding: 0; margin: 0; border-radius:0px; " + "border-top-right-radius: 3px; " + "border-bottom-right-radius: 3px; }") + .arg(dark ? "rgba(255, 255, 255, 10)" : "rgba(0, 0, 0, 25)")); // remove padding margin from layout m_systemBar->setContentsMargins(0, 0, 0, 0); @@ -519,8 +522,8 @@ void DiskUsageWidget::fetchData() size_t total_size = 0; AfcFileHandle *afcHandle = nullptr; - err = ServiceManager::safeAfcFileOpen( - m_device, "/PhotoData/Photos.sqlite", AfcRdOnly, &afcHandle); + err = ServiceManager::safeAfcFileOpen(m_device, PHOTOS_SQLITE_DB_PATH, + AfcRdOnly, &afcHandle); if (err != nullptr) { qDebug() << "Failed to open Photos.sqlite on device:" @@ -531,6 +534,20 @@ void DiskUsageWidget::fetchData() return result; } + AfcFileInfo fileInfo = {}; + err = ServiceManager::safeAfcGetFileInfo( + m_device, PHOTOS_SQLITE_DB_PATH, &fileInfo); + if (err != nullptr) { + qDebug() << "Failed to get file info for Photos.sqlite:" + << "Error Code:" << err->code + << "Message:" << err->message; + idevice_error_free(err); + ServiceManager::safeAfcFileClose(m_device, afcHandle); + result["galleryUsage"] = QVariant::fromValue(uint64_t(0)); + return result; + } + db_size = fileInfo.size; + while (true) { uint8_t *chunk = nullptr; size_t chunk_size = 0; @@ -551,7 +568,24 @@ void DiskUsageWidget::fetchData() memcpy(db_data + total_size, chunk, chunk_size); total_size += chunk_size; } - ServiceManager::safeAfcFileClose(m_device, afcHandle); + err = ServiceManager::safeAfcFileClose(m_device, afcHandle); + if (err != nullptr) { + qDebug() << "Failed to close Photos.sqlite on device:" + << "Error Code:" << err->code + << "Message:" << err->message; + idevice_error_free(err); + } + + if (total_size != db_size) { + qDebug() + << "Warning: Read size does not match expected file size for " + "Photos.sqlite. Read:" + << total_size << "Expected:" << db_size; + result["galleryUsage"] = QVariant::fromValue(uint64_t(0)); + + return result; + } + qDebug() << "Total Photos.sqlite size read:" << total_size; // HACK: File is in WAL mode (byte 18 == 0x02). diff --git a/src/exportalbum.cpp b/src/exportalbum.cpp new file mode 100644 index 0000000..6a79f0b --- /dev/null +++ b/src/exportalbum.cpp @@ -0,0 +1,213 @@ +#include "exportalbum.h" + +ExportAlbum::ExportAlbum(const iDescriptorDevice *device, + const QStringList &paths, QWidget *parent) + : QDialog(parent), m_device(device), m_listCount(paths.size()) +{ + setWindowTitle("Export Album"); + setMaximumSize(600, 400); +#ifdef WIN32 + setupWinWindow(this); +#endif + + m_loadingWidget = new ZLoadingWidget(true, this); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(m_loadingWidget); + + getTotalPhotoCount(paths); + connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this, + [this](const std::string &udid, const std::string &macAddress, + const std::string &ipAddress, bool wasWireless) { + if (udid == m_device->udid) { + m_exiting = true; + QTimer::singleShot(0, this, [this]() { close(); }); + } + }); + + QWidget *contentWidget = new QWidget(this); + QVBoxLayout *contentLayout = new QVBoxLayout(contentWidget); + + m_infoLabel = new QLabel(this); + contentLayout->addWidget(m_infoLabel); + + QHBoxLayout *buttonLayout = new QHBoxLayout(); + QPushButton *cancelButton = new QPushButton("Cancel", this); + QPushButton *exportButton = new QPushButton("Export", this); + buttonLayout->addWidget(exportButton); + buttonLayout->addWidget(cancelButton); + m_dirPickerLabel = new DirPickerLabel(this); + + contentLayout->addWidget(m_dirPickerLabel); + + QHBoxLayout *sizeLayout = new QHBoxLayout(); + + m_totalSizeExportLabel = new QLabel("Total size to export: 0 MB", this); + sizeLayout->addWidget(m_totalSizeExportLabel); + + m_loadingIndicator = new QProcessIndicator(this); + m_loadingIndicator->setType(QProcessIndicator::line_rotate); + m_loadingIndicator->setFixedSize(32, 16); + sizeLayout->addWidget(m_loadingIndicator); + sizeLayout->addStretch(); + + contentLayout->addLayout(sizeLayout); + contentLayout->addLayout(buttonLayout); + + connect(cancelButton, &QPushButton::clicked, this, [this]() { + m_exiting = true; + QTimer::singleShot(0, this, [this]() { close(); }); + }); + connect(exportButton, &QPushButton::clicked, this, [this, exportButton]() { + m_exiting = true; + exportButton->setEnabled(false); + QTimer::singleShot(0, this, [this]() { + startExport(); + accept(); + }); + }); + + m_loadingWidget->setupContentWidget(contentWidget); + + connect(this, &QDialog::finished, this, [this](int) { + m_exiting = true; + deleteLater(); + }); +} + +void ExportAlbum::getTotalPhotoCount(const QStringList &paths) +{ + QFutureWatcher> *watcher = + new QFutureWatcher>(this); + + connect(watcher, &QFutureWatcher>::finished, this, + [this, watcher]() { + std::pair result = watcher->result(); + qDebug() << "Total photo count:" << result.second << "with" + << (result.first ? 0 : 1) << "errors"; + + if (result.first) { + updateInfoLabel(result.second); + calculateTotalExportSize(); + m_loadingWidget->stop(); + } else { + QMessageBox::warning( + nullptr, "Error", + "Failed to read directory: cannot export album(s)"); + reject(); + } + }); + + watcher->setFuture(QtConcurrent::run([this, paths]() { + size_t count = 0; + bool errorOccurred = false; + for (const QString &path : paths) { + size_t innerCount = 0; + char **items = nullptr; + + IdeviceFfiError *err = ServiceManager::safeAfcReadDirectory( + m_device, path.toStdString().c_str(), &items, &innerCount); + + if (err) { + qDebug() << "Failed to read directory:" + << path.toStdString().c_str() + << "Error:" << err->message << "Code:" << err->code; + + errorOccurred = true; + idevice_error_free(err); + } else { + int index = 0; + for (size_t i = 0; i < innerCount; ++i) { + const char *item = items[i]; + if (!item) { + continue; + } + QString fileName = QString::fromUtf8(item); + + if (fileName.endsWith(".") || fileName.endsWith("..")) { + continue; + } + + QString filePath = path + "/" + QString::fromUtf8(item); + + m_exportItems.append( + ExportItem(filePath, fileName, m_device->udid, index)); + ++index; + } + free_directory_listing(items, innerCount); + count += innerCount; + } + } + return std::make_pair(!errorOccurred, count); + })); +} + +void ExportAlbum::updateInfoLabel(size_t photoCount) +{ + m_infoLabel->setText(QString("Are you sure you want to export %1 album(s) " + "with %2 photo(s)/video(s) ?") + .arg(m_listCount) + .arg(photoCount)); +} + +void ExportAlbum::startExport() +{ + // qDebug() << "Starting export of selected files:" << exportItems.size() + // << "items to" << exportDir; + ExportManager::sharedInstance()->startExport( + m_device, m_exportItems, m_dirPickerLabel->getOutputDir()); +} + +void ExportAlbum::calculateTotalExportSize() +{ + m_totalExportSize = 0; + m_loadingIndicator->start(); + + auto timer = new QTimer(this); + timer->setInterval(500); + + connect(timer, &QTimer::timeout, this, [this]() { + m_totalSizeExportLabel->setText( + QString("Total size to export: %1") + .arg(iDescriptor::Utils::formatSize(m_totalExportSize.load()))); + }); + + timer->start(); + + QThreadPool::globalInstance()->start([this, timer]() { + for (const ExportItem &item : m_exportItems) { + if (m_exiting.load()) { + return; + } + AfcFileInfo info = {}; + IdeviceFfiError *err = ServiceManager::safeAfcGetFileInfo( + m_device, item.sourcePathOnDevice.toStdString().c_str(), &info); + + if (err) { + qDebug() << "Failed to get file info for:" + << item.sourcePathOnDevice << "Error:" << err->message + << "Code:" << err->code; + idevice_error_free(err); + } else { + this->m_totalExportSize += info.size; + afc_file_info_free(&info); + } + } + + QMetaObject::invokeMethod( + this, + [this, timer]() { + if (m_exiting.load()) { + return; + } + timer->stop(); + timer->deleteLater(); + this->m_totalSizeExportLabel->setText( + QString("Total size to export: %1") + .arg(iDescriptor::Utils::formatSize( + this->m_totalExportSize.load()))); + this->m_loadingIndicator->stop(); + this->m_loadingIndicator->hide(); + }, + Qt::QueuedConnection); + }); +} \ No newline at end of file diff --git a/src/exportalbum.h b/src/exportalbum.h new file mode 100644 index 0000000..b5d610b --- /dev/null +++ b/src/exportalbum.h @@ -0,0 +1,44 @@ +#ifndef EXPORTALBUM_H +#define EXPORTALBUM_H + +#include "appcontext.h" +#include "dirpickerlabel.h" +#include "exportmanager.h" +#include "iDescriptor-ui.h" +#include "iDescriptor.h" +#include "qprocessindicator.h" +#include "servicemanager.h" +#include "zloadingwidget.h" +#include +#include +#include +#include +#include +#include + +class ExportAlbum : public QDialog +{ + Q_OBJECT +public: + explicit ExportAlbum(const iDescriptorDevice *device, + const QStringList &paths, QWidget *parent = nullptr); + +private: + ZLoadingWidget *m_loadingWidget; + const iDescriptorDevice *m_device; + QLabel *m_infoLabel; + size_t m_listCount; + QList m_exportItems; + DirPickerLabel *m_dirPickerLabel; + QLabel *m_totalSizeExportLabel; + QProcessIndicator *m_loadingIndicator = nullptr; + std::atomic m_totalExportSize{0}; + std::atomic m_exiting{false}; + void getTotalPhotoCount(const QStringList &paths); + void updateInfoLabel(size_t photoCount); + // startExport(const QStringList &paths, const QString &exportDir); + void startExport(); + void calculateTotalExportSize(); +}; + +#endif // EXPORTALBUM_H diff --git a/src/gallerywidget.cpp b/src/gallerywidget.cpp index 2b23791..950c2a9 100644 --- a/src/gallerywidget.cpp +++ b/src/gallerywidget.cpp @@ -19,6 +19,7 @@ #include "gallerywidget.h" #include "exportmanager.h" +#include "iDescriptor-ui.h" #include "iDescriptor.h" #include "mediapreviewdialog.h" #include "photomodel.h" @@ -121,7 +122,7 @@ void GalleryWidget::setupControlsLayout() // Sort order combo box QLabel *sortLabel = new QLabel("Sort:"); - sortLabel->setStyleSheet("font-weight: bold;"); + sortLabel->setStyleSheet(mergeStyles(sortLabel, "font-weight: 600;")); m_sortComboBox = new QComboBox(); m_sortComboBox->addItem("Newest First", static_cast(PhotoModel::NewestFirst)); @@ -133,7 +134,7 @@ void GalleryWidget::setupControlsLayout() // Filter combo box QLabel *filterLabel = new QLabel("Filter:"); - filterLabel->setStyleSheet("font-weight: bold;"); + filterLabel->setStyleSheet(mergeStyles(filterLabel, "font-weight: 600;")); m_filterComboBox = new QComboBox(); m_filterComboBox->addItem("All Media", static_cast(PhotoModel::All)); m_filterComboBox->addItem("Images Only", @@ -147,14 +148,16 @@ void GalleryWidget::setupControlsLayout() // Export buttons m_exportSelectedButton = new QPushButton("Export Selected"); - m_exportSelectedButton->setEnabled(false); // Initially disabled + m_exportSelectedButton->setEnabled(false); m_exportSelectedButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); m_exportAllButton = new QPushButton("Export All"); + m_exportAllButton->setEnabled(false); // Back button - m_backButton = new QPushButton("←"); - m_backButton->setToolTip("Back to Albums"); + m_backButton = new ZIconWidget( + QIcon(":/resources/icons/MaterialSymbolsArrowLeftAlt.png"), + "Back to Albums"); m_backButton->setMaximumWidth(30); m_backButton->hide(); // Hidden initially @@ -168,7 +171,7 @@ void GalleryWidget::setupControlsLayout() &GalleryWidget::onExportSelected); connect(m_exportAllButton, &QPushButton::clicked, this, &GalleryWidget::onExportAll); - connect(m_backButton, &QPushButton::clicked, this, + connect(m_backButton, &ZIconWidget::clicked, this, &GalleryWidget::onBackToAlbums); // Add widgets to layout @@ -226,6 +229,31 @@ void GalleryWidget::onFilterChanged() void GalleryWidget::onExportSelected() { + // if we are exporting from album selection view + if (m_loadingWidget->currentWidget() == m_albumSelectionWidget) { + + QModelIndexList selectedIndexes = + m_albumListView->selectionModel()->selectedIndexes(); + // QStringList filePaths = + // m_albumModel->getSelectedFilePaths(selectedIndexes); + + QStringList paths; + for (const QModelIndex &index : selectedIndexes) { + if (index.isValid() && + index.row() < m_albumListView->model()->rowCount()) { + paths.append(index.data(Qt::UserRole).toString()); + } else { + qDebug() << "Invalid index in selection:" << index; + } + } + // /DCIM/100APPLE + qDebug() << "Selected file paths:" << paths; + + auto *exportAlbum = new ExportAlbum(m_device, paths, this); + exportAlbum->show(); + return; + } + if (!m_model || !m_listView->selectionModel()->hasSelection()) { QMessageBox::information(this, "No Selection", "Please select photos to export."); @@ -247,7 +275,6 @@ void GalleryWidget::onExportSelected() return; } - // Convert QStringList to QList QList exportItems; // FIXME: index int index = 0; @@ -267,52 +294,64 @@ void GalleryWidget::onExportSelected() void GalleryWidget::onExportAll() { + // if we are exporting from album selection view + if (m_loadingWidget->currentWidget() == m_albumSelectionWidget) { + + // gel all available albums + QStringList paths; + for (int row = 0; row < m_albumListView->model()->rowCount(); ++row) { + QModelIndex index = m_albumListView->model()->index(row, 0); + if (index.isValid()) { + paths.append(index.data(Qt::UserRole).toString()); + } + } + + auto *exportAlbum = new ExportAlbum(m_device, paths, this); + exportAlbum->show(); + return; + } + if (!m_model) return; - // if (ExportManager::sharedInstance()->isExporting()) { - // QMessageBox::information(this, "Export in Progress", - // "An export is already in progress."); - // return; - // } + QStringList filePaths = m_model->getFilteredFilePaths(); - // QStringList filePaths = m_model->getFilteredFilePaths(); + if (filePaths.isEmpty()) { + QMessageBox::information(this, "No Items", "No items to export."); + return; + } - // if (filePaths.isEmpty()) { - // QMessageBox::information(this, "No Items", "No items to export."); - // return; - // } + QString message = + QString("Export all %1 items currently shown?").arg(filePaths.size()); + int reply = QMessageBox::question(this, "Export All", message, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); - // QString message = - // QString("Export all %1 items currently - // shown?").arg(filePaths.size()); - // int reply = QMessageBox::question(this, "Export All", message, - // QMessageBox::Yes | QMessageBox::No, - // QMessageBox::No); + if (reply != QMessageBox::Yes) { + return; + } - // if (reply != QMessageBox::Yes) { - // return; - // } + QString exportDir = selectExportDirectory(); + if (exportDir.isEmpty()) { + return; + } - // QString exportDir = selectExportDirectory(); - // if (exportDir.isEmpty()) { - // return; - // } + // FIXME: index + int index = 0; + QList exportItems; + for (const QString &filePath : filePaths) { + QString fileName = filePath.split('/').last(); + exportItems.append( + ExportItem(filePath, fileName, m_device->udid, index)); + ++index; + } - // // Convert QStringList to QList - // QList exportItems; - // for (const QString &filePath : filePaths) { - // QString fileName = filePath.split('/').last(); - // exportItems.append(ExportItem(filePath, fileName)); - // } + qDebug() << "Starting export of:" << exportItems.size() << "items to" + << exportDir; - // qDebug() << "Starting export of all filtered files:" << - // exportItems.size() - // << "items to" << exportDir; - - // // Start export and the manager will show its own dialog - // ExportManager::sharedInstance()->startExport(m_device, exportItems, - // exportDir); + // Start export and the manager will show its own dialog + ExportManager::sharedInstance()->startExport(m_device, exportItems, + exportDir); } QString GalleryWidget::selectExportDirectory() @@ -440,12 +479,10 @@ void GalleryWidget::loadAlbumList(const AFCFileTree &dcimTree) qDebug() << "DCIM directory read successfully, found" << dcimTree.entries.size() << "entries"; - auto *albumModel = new QStandardItemModel(this); + m_albumModel = new QStandardItemModel(this); for (const MediaEntry &entry : dcimTree.entries) { QString albumName = QString::fromStdString(entry.name); - qDebug() << "DCIM entry:" << albumName << "(isDir:" << entry.isDir - << ")"; // Check if it's a directory and matches common iOS photo album patterns if (entry.isDir && @@ -459,15 +496,23 @@ void GalleryWidget::loadAlbumList(const AFCFileTree &dcimTree) item->setData(fullPath, Qt::UserRole); // Store full path item->setIcon(QIcon::fromTheme("folder")); - albumModel->appendRow(item); + m_albumModel->appendRow(item); loadAlbumThumbnailAsync(fullPath, item); } } - m_albumListView->setModel(albumModel); + m_albumListView->setModel(m_albumModel); m_loadingWidget->stop(); m_loadingWidget->switchToWidget(m_albumSelectionWidget); + m_exportAllButton->setEnabled(m_albumModel->rowCount() > 0); + + connect(m_albumListView->selectionModel(), + &QItemSelectionModel::selectionChanged, this, [this]() { + bool hasSelection = + m_albumListView->selectionModel()->hasSelection(); + m_exportSelectedButton->setEnabled(hasSelection); + }); } void GalleryWidget::onAlbumSelected(const QString &albumPath) @@ -528,7 +573,6 @@ void GalleryWidget::setControlsEnabled(bool enabled) m_filterComboBox->setEnabled(enabled); m_exportSelectedButton->setEnabled( enabled && m_listView && m_listView->selectionModel()->hasSelection()); - m_exportAllButton->setEnabled(enabled); } /* @@ -667,4 +711,4 @@ void GalleryWidget::onPhotoContextMenu(const QPoint &pos) GalleryWidget::~GalleryWidget() { qDebug() << "GalleryWidget destructor called"; -} \ No newline at end of file +} diff --git a/src/gallerywidget.h b/src/gallerywidget.h index 096cc25..b45ba48 100644 --- a/src/gallerywidget.h +++ b/src/gallerywidget.h @@ -20,6 +20,8 @@ #ifndef GALLERYWIDGET_H #define GALLERYWIDGET_H +#include "exportalbum.h" +#include "iDescriptor-ui.h" #include "iDescriptor.h" #include "photomodel.h" #include "zloadingwidget.h" @@ -34,6 +36,7 @@ class QVBoxLayout; class QStackedWidget; class QLabel; class QStandardItem; +class QStandardItemModel; QT_END_NAMESPACE class ExportManager; @@ -88,13 +91,14 @@ private: QWidget *m_photoGalleryWidget; QListView *m_listView; PhotoModel *m_model; + QStandardItemModel *m_albumModel; // Control widgets QComboBox *m_sortComboBox; QComboBox *m_filterComboBox; QPushButton *m_exportSelectedButton; QPushButton *m_exportAllButton; - QPushButton *m_backButton; + ZIconWidget *m_backButton; // Export manager ExportManager *m_exportManager; diff --git a/src/heartbeat.h b/src/heartbeat.h index 5874a76..522034c 100644 --- a/src/heartbeat.h +++ b/src/heartbeat.h @@ -10,8 +10,8 @@ class HeartbeatThread : public QThread { Q_OBJECT public: - HeartbeatThread(HeartbeatClientHandle *heartbeat, QString macAddress, - QObject *parent = nullptr) + HeartbeatThread(HeartbeatClientHandle *heartbeat, + iDescriptor::Uniq macAddress, QObject *parent = nullptr) : QThread(parent), m_hb(Heartbeat::adopt(heartbeat)), m_macAddress(macAddress) { @@ -88,11 +88,11 @@ public: private: Heartbeat m_hb; bool m_initialCompleted = false; - QString m_macAddress; + iDescriptor::Uniq m_macAddress; unsigned int m_tries = 0; signals: void heartbeatFailed(const QString &macAddress, unsigned int tries = 0); - void heartbeatThreadExited(const QString &macAddress); + void heartbeatThreadExited(const iDescriptor::Uniq &uniq); }; #endif // HEARTBEATTHREAD_H \ No newline at end of file diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 852ac2c..75ab0f9 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -237,7 +237,7 @@ QString HttpServer::generateJsonManifest() const return doc.toJson(); } -QString HttpServer::getLocalIP() const +QString HttpServer::getLocalIP() { foreach (const QNetworkInterface &netIf, QNetworkInterface::allInterfaces()) { diff --git a/src/httpserver.h b/src/httpserver.h index 2a8780c..ebafc87 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -39,6 +39,8 @@ public: void stop(); int getPort() const; QString getJsonFileName() const { return jsonFileName; } + static QString getLocalIP(); + signals: void serverStarted(); void serverError(const QString &error); @@ -64,7 +66,6 @@ private: void sendJsonManifest(QTcpSocket *socket); QString generateJsonManifest() const; QString getMimeType(const QString &filePath) const; - QString getLocalIP() const; }; #endif // HTTPSERVER_H diff --git a/src/iDescriptor-ui.h b/src/iDescriptor-ui.h index 7092de8..690ee82 100644 --- a/src/iDescriptor-ui.h +++ b/src/iDescriptor-ui.h @@ -44,7 +44,11 @@ #include "./platform/windows/win_common.h" #endif -#define COLOR_GREEN QColor(0, 180, 0) // Green +#ifndef WIN32 +#define COLOR_GREEN QColor(0, 180, 0) // Green +#else +#define COLOR_GREEN QColor("#FF008000") +#endif #define COLOR_ORANGE QColor(255, 140, 0) // Orange #define COLOR_RED QColor(255, 0, 0) // Red #define COLOR_BLUE QColor("#2b5693") diff --git a/src/iDescriptor.h b/src/iDescriptor.h index d4a935e..95fa529 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -81,6 +80,7 @@ #define TimeoutErrorCode -71 #define DISK_IMAGE_TYPE_DEVELOPER "Developer" +#define PHOTOS_SQLITE_DB_PATH "/PhotoData/Photos.sqlite" #define HEARTBEAT_RETRY_LIMIT 2 @@ -480,8 +480,8 @@ struct ImageInfo { void get_battery_info(DiagnosticsRelay *diagRelay, plist_t &diagnostics); -// void parseOldDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); -// void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); +void parseOldDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); +void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); void fetchAppIconFromApple( QNetworkAccessManager *manager, const QString &bundleId, @@ -702,4 +702,4 @@ inline QString formatFileSize(qint64 bytes) inline QString formatTransferRate(qint64 bytesPerSecond) { return formatFileSize(bytesPerSecond) + "/s"; -} \ No newline at end of file +} diff --git a/src/ifusewidget.cpp b/src/ifusewidget.cpp index 28067c3..7f7f84e 100644 --- a/src/ifusewidget.cpp +++ b/src/ifusewidget.cpp @@ -34,7 +34,7 @@ #endif iFuseWidget::iFuseWidget(iDescriptorDevice *device, QWidget *parent) - : QWidget(parent), m_mainLayout(nullptr), m_ifuseProcess(nullptr), + : Tool(parent), m_mainLayout(nullptr), m_ifuseProcess(nullptr), m_device(device) { setupUI(); diff --git a/src/ifusewidget.h b/src/ifusewidget.h index 22b3ef8..aba8ffc 100644 --- a/src/ifusewidget.h +++ b/src/ifusewidget.h @@ -37,7 +37,7 @@ #include #include -class iFuseWidget : public QWidget +class iFuseWidget : public Tool { Q_OBJECT diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp index 8898e9c..3ea8ef1 100644 --- a/src/installedappswidget.cpp +++ b/src/installedappswidget.cpp @@ -39,13 +39,18 @@ AppTabWidget::AppTabWidget(const QString &appName, const QString &bundleId, const QString &version, const QPixmap &icon, QWidget *parent) - : QGroupBox(parent), m_appName(appName), m_bundleId(bundleId), + : QWidget(parent), m_appName(appName), m_bundleId(bundleId), m_version(version), m_selected(false) { +#ifndef WIN32 setFixedHeight(60); +#else + setMinimumHeight(60); +#endif setMinimumWidth(100); setCursor(Qt::PointingHandCursor); - + setAttribute(Qt::WA_StyledBackground, true); + setObjectName("AppTabWidget"); setupUI(icon); } @@ -118,20 +123,28 @@ void AppTabWidget::mousePressEvent(QMouseEvent *event) void AppTabWidget::updateStyles() { QString style; +#ifndef WIN32 QColor bgColor = isDarkMode() ? qApp->palette().color(QPalette::Light) : qApp->palette().color(QPalette::Dark); +#else + QColor bgColor = + isDarkMode() ? QColor(255, 255, 255, 25) : QColor(0, 0, 0, 25); +#endif if (m_selected) { - style = "QGroupBox { background-color: " + COLOR_ACCENT_BLUE.name() + - "; border-radius: " - "10px; border : 1px solid " + - bgColor.lighter().name() + "; }"; + style = + "#AppTabWidget { background-color: " + COLOR_ACCENT_BLUE.name() + + "; border-radius: " + "10px; border : 1px solid " + + bgColor.lighter().name() + "; }"; } else { - style = "QGroupBox { background-color: " + bgColor.name() + + style = "#AppTabWidget { background-color: " + + bgColor.name(QColor::HexArgb) + "; border-radius: 10px; border: 1px solid " + bgColor.lighter().name() + "; }"; } // prevent infinite loop if (style != styleSheet()) { + qDebug() << "Style" << style; setStyleSheet(style); } } diff --git a/src/installedappswidget.h b/src/installedappswidget.h index 2716601..40d3916 100644 --- a/src/installedappswidget.h +++ b/src/installedappswidget.h @@ -43,7 +43,7 @@ #include #include -class AppTabWidget : public QGroupBox +class AppTabWidget : public QWidget { Q_OBJECT @@ -70,7 +70,7 @@ protected: if (event->type() == QEvent::PaletteChange) { updateStyles(); } - QGroupBox::changeEvent(event); + QWidget::changeEvent(event); }; private: diff --git a/src/livescreenwidget.cpp b/src/livescreenwidget.cpp index b0338b2..d3b94fe 100644 --- a/src/livescreenwidget.cpp +++ b/src/livescreenwidget.cpp @@ -30,11 +30,11 @@ #include #include -// todo add a retry button when failed LiveScreenWidget::LiveScreenWidget(iDescriptorDevice *device, QWidget *parent) - : QWidget{parent}, m_device(device) + : Tool{parent}, m_device(device) { setWindowTitle("Live Screen - iDescriptor"); + setAttribute(Qt::WA_DeleteOnClose); unsigned int deviceMajorVersion = m_device->deviceInfo.parsedDeviceVersion.major; diff --git a/src/livescreenwidget.h b/src/livescreenwidget.h index f3714ac..95d12f9 100644 --- a/src/livescreenwidget.h +++ b/src/livescreenwidget.h @@ -20,6 +20,7 @@ #ifndef LIVESCREEN_H #define LIVESCREEN_H +#include "iDescriptor-ui.h" #include "iDescriptor.h" #include "servicemanager.h" #include @@ -72,7 +73,7 @@ private: iDescriptorDevice *m_device; }; -class LiveScreenWidget : public QWidget +class LiveScreenWidget : public Tool { Q_OBJECT public: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6e57631..c5e914d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -25,6 +25,7 @@ #include "ifusediskunmountbutton.h" #include "ifusemanager.h" #include "jailbrokenwidget.h" +#include "releasechangelogdialog.h" #include "toolboxwidget.h" #include "welcomewidget.h" #include @@ -155,19 +156,30 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) statusLayout->addWidget(statusBalloon->getButton()); - ZIconWidget *welcomeMenu = new ZIconWidget( + ZIconWidget *welcomeMenuSwitch = new ZIconWidget( QIcon(":/resources/icons/LetsIconsHorizontalDownLeftMainLight.png"), "Switch to Welcome Menu"); - connect(welcomeMenu, &ZIconWidget::clicked, this, [this, welcomeMenu]() { - if (m_mainStackedWidget->currentIndex() != 0) { - welcomeMenu->setToolTip("Switch to Connected Devices"); - return m_mainStackedWidget->setCurrentIndex(0); - } - welcomeMenu->setToolTip("Switch to Welcome Menu"); - m_mainStackedWidget->setCurrentIndex(1); - }); + connect(welcomeMenuSwitch, &ZIconWidget::clicked, this, + [this, welcomeMenuSwitch]() { + if (m_mainStackedWidget->currentIndex() != 0) { + welcomeMenuSwitch->setToolTip( + "Switch to Connected Devices"); + return m_mainStackedWidget->setCurrentIndex(0); + } + welcomeMenuSwitch->setToolTip("Switch to Welcome Menu"); + m_mainStackedWidget->setCurrentIndex(1); + }); - statusLayout->addWidget(welcomeMenu); + connect(m_ZTabWidget, &ZTabWidget::currentChanged, this, + [welcomeMenuSwitch](int index) { + if (index != 0) { + return welcomeMenuSwitch->hide(); + } + + welcomeMenuSwitch->show(); + }); + + statusLayout->addWidget(welcomeMenuSwitch); statusLayout->addStretch(1); statusLayout->setContentsMargins(0, 0, 0, 0); @@ -220,23 +232,16 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) qDebug() << "Subscribed to recovery device events successfully."; #endif - // idevice_error_t res = idevice_event_subscribe(handleCallback, - // nullptr); if (res != IDEVICE_E_SUCCESS) { - // qDebug() << "ERROR: Unable to subscribe to device events. Error - // code:" - // << res; - // } - // qDebug() << "Subscribed to device events successfully."; createMenus(); - // UpdateProcedure updateProcedure; - // bool packageManagerManaged = false; - // bool isPortable = false; - // bool skipPrerelease = true; - // #ifdef WIN32 - // isPortable = !is_iDescriptorInstalled(); - // qDebug() << "isPortable=" << isPortable; - // #endif + UpdateProcedure updateProcedure; + bool packageManagerManaged = false; + bool isPortable = false; + bool skipPrerelease = true; +#ifdef WIN32 + isPortable = !is_iDescriptorInstalled(); + qDebug() << "isPortable=" << isPortable; +#endif /* struct UpdateProcedure { @@ -247,75 +252,87 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) QString boxText; }; */ - // switch (ZUpdater::detectPlatform()) { - // case Platform::Windows: - // updateProcedure = UpdateProcedure{ - // !isPortable, - // isPortable, - // !isPortable, - // isPortable ? "New portable version downloaded, app location - // will " - // "be shown after this message" - // : "The application will now quit to install the - // update.", - // isPortable ? "New portable version downloaded" - // : "Do you want to install the downloaded update - // now?", - // }; - // break; - // // todo: adjust for pkg managers - // case Platform::MacOS: - // updateProcedure = UpdateProcedure{ - // true, - // false, - // true, - // "The application will now quit and open .dmg file downloaded - // to " - // "\"Downloads\" from there you can drag it to Applications to - // " "install.", "Update downloaded would you like to quit and - // install the update?", - // }; - // break; - // case Platform::Linux: - // // currently only on linux (arch aur) is enabled - // #ifdef PACKAGE_MANAGER_MANAGED - // packageManagerManaged = true; - // #endif - // updateProcedure = UpdateProcedure{ - // true, - // false, - // true, - // "AppImages we ship are not updateable. New version is - // downloaded " "to " - // "\"Downloads\". You can start using the new version by - // launching " "it " "from there. You can delete this AppImage - // version if you like.", "Update downloaded would you like to - // quit and open the new " "version?", - // }; - // break; - // default: - // updateProcedure = UpdateProcedure{ - // false, false, false, "", "", - // }; - // } + switch (ZUpdater::detectPlatform()) { + case Platform::Windows: + updateProcedure = UpdateProcedure{ + !isPortable, + isPortable, + !isPortable, + isPortable ? "New portable version downloaded, app location will " + "be shown after this message" + : "The application will now quit to install the update.", + isPortable ? "New portable version downloaded" + : "Do you want to install the downloaded update now?", + }; + break; + // todo: adjust for pkg managers + case Platform::MacOS: + updateProcedure = UpdateProcedure{ + true, + false, + true, + "The application will now quit and open .dmg file downloaded to " + "\"Downloads\" from there you can drag it to Applications to " + "install.", + "Update downloaded would you like to quit and install the update?", + }; + break; + case Platform::Linux: + // currently only on linux (arch aur) is enabled +#ifdef PACKAGE_MANAGER_MANAGED + packageManagerManaged = true; +#endif + updateProcedure = UpdateProcedure{ + true, + false, + true, + "AppImages we ship are not updateable. New version is downloaded " + "to " + "\"Downloads\". You can start using the new version by launching " + "it " + "from there. You can delete this AppImage version if you like.", + "Update downloaded would you like to quit and open the new " + "version?", + }; + break; + default: + updateProcedure = UpdateProcedure{ + false, false, false, "", "", + }; + } - // m_updater = new ZUpdater("iDescriptor/iDescriptor", APP_VERSION, - // "iDescriptor", updateProcedure, isPortable, - // packageManagerManaged, skipPrerelease, - // this); - // #if defined(PACKAGE_MANAGER_MANAGED) && defined(__linux__) - // m_updater->setPackageManagerManagedMessage( - // QString( - // "You seem to have installed iDescriptor using a package - // manager. " "Please use %1 to update it.") - // .arg(PACKAGE_MANAGER_HINT)); - // #endif + m_updater = new ZUpdater("iDescriptor/iDescriptor", APP_VERSION, + "iDescriptor", updateProcedure, isPortable, + packageManagerManaged, skipPrerelease, this); +#if defined(PACKAGE_MANAGER_MANAGED) && defined(__linux__) + m_updater->setPackageManagerManagedMessage( + QString( + "You seem to have installed iDescriptor using a package manager. " + "Please use %1 to update it.") + .arg(PACKAGE_MANAGER_HINT)); +#endif - // SettingsManager::sharedInstance()->doIfEnabled( - // SettingsManager::Setting::AutoCheckUpdates, [this]() { - // qDebug() << "Checking for updates..."; - // m_updater->checkForUpdates(); - // }); + QString lastAppVersion = SettingsManager::sharedInstance()->appVersion(); + bool shouldShowReleaseChangelog = true; + SettingsManager::sharedInstance()->setAppVersion(APP_VERSION); + + if (shouldShowReleaseChangelog) { + connect( + m_updater, &ZUpdater::dataAvailable, this, + [this](const QJsonDocument data, bool isUpdateAvailable) { + if (!isUpdateAvailable) { + ReleaseChangelogDialog dialog(data, this); + dialog.exec(); + } + }, + Qt::SingleShotConnection); + } + + SettingsManager::sharedInstance()->doIfEnabled( + SettingsManager::Setting::AutoCheckUpdates, [this]() { + qDebug() << "Checking for updates..."; + m_updater->checkForUpdates(); + }); m_deviceMonitor = new DeviceMonitorThread(this); connect( @@ -343,9 +360,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) } case DeviceMonitorThread::IDEVICE_DEVICE_REMOVE: { - QMetaObject::invokeMethod(AppContext::sharedInstance(), - "removeDevice", Qt::QueuedConnection, - Q_ARG(QString, udid)); + QMetaObject::invokeMethod( + AppContext::sharedInstance(), "removeDevice", + Qt::QueuedConnection, + Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(udid))); break; } diff --git a/src/mediapreviewdialog.cpp b/src/mediapreviewdialog.cpp index 5c075c2..fbcdac4 100644 --- a/src/mediapreviewdialog.cpp +++ b/src/mediapreviewdialog.cpp @@ -55,6 +55,9 @@ MediaPreviewDialog::MediaPreviewDialog(const iDescriptorDevice *device, m_afcClient(afcClient) { setWindowTitle(QFileInfo(filePath).fileName() + " - iDescriptor"); +#ifdef WIN32 + setupWinWindow(this); +#endif // Make dialog fullscreen setWindowState(Qt::WindowMaximized); @@ -112,6 +115,7 @@ void MediaPreviewDialog::setupImageView() // Controls layout m_controlsLayout = new QHBoxLayout(); m_controlsLayout->setContentsMargins(10, 5, 10, 5); + m_controlsLayout->setSpacing(10); m_zoomInBtn = new QPushButton("Zoom In", this); m_zoomOutBtn = new QPushButton("Zoom Out", this); diff --git a/src/networkdevicestoconnectwidget.cpp b/src/networkdevicestoconnectwidget.cpp index b1ef6b1..98e788b 100644 --- a/src/networkdevicestoconnectwidget.cpp +++ b/src/networkdevicestoconnectwidget.cpp @@ -90,15 +90,14 @@ NetworkDeviceCard::NetworkDeviceCard(const NetworkDevice &device, infoLayout->addWidget(m_connectButton); infoLayout->addSpacing(5); - // Status indicator QLabel *statusIndicator = new QLabel("●"); - QFont statusFont = statusIndicator->font(); - statusFont.setPointSize(12); - statusIndicator->setFont(statusFont); - QPalette statusPalette = statusIndicator->palette(); - statusPalette.setColor(QPalette::WindowText, - QColor(52, 199, 89)); // iOS green - statusIndicator->setPalette(statusPalette); + statusIndicator->setStyleSheet( + QString("QLabel { font-size: 14px; color: %1; }") +#ifdef WIN32 + .arg(COLOR_ACCENT_BLUE.name())); +#else + .arg(COLOR_GREEN.name())); +#endif infoLayout->addWidget(statusIndicator); diff --git a/src/networkdeviceswidget.cpp b/src/networkdeviceswidget.cpp index efb79b9..417403f 100644 --- a/src/networkdeviceswidget.cpp +++ b/src/networkdeviceswidget.cpp @@ -33,8 +33,8 @@ NetworkDevicesWidget::NetworkDevicesWidget(QWidget *parent) : Tool(parent) { setWindowTitle("Network Devices - iDescriptor"); - resize(500, 600); - setMinimumSize(400, 300); + setMinimumSize(300, 300); + setMaximumSize(500, 500); setupUI(); #ifdef __linux__ m_networkProvider = new AvahiService(this); @@ -162,15 +162,14 @@ void NetworkDevicesWidget::createDeviceCard(const NetworkDevice &device) infoLayout->addWidget(portLabel); infoLayout->addStretch(); - // Status indicator QLabel *statusIndicator = new QLabel("●"); - QFont statusFont = statusIndicator->font(); - statusFont.setPointSize(12); - statusIndicator->setFont(statusFont); - QPalette statusPalette = statusIndicator->palette(); - statusPalette.setColor(QPalette::WindowText, - QColor(52, 199, 89)); // iOS green - statusIndicator->setPalette(statusPalette); + statusIndicator->setStyleSheet( + QString("QLabel { font-size: 14px; color: %1; }") +#ifdef WIN32 + .arg(COLOR_ACCENT_BLUE.name())); +#else + .arg(COLOR_GREEN.name())); +#endif infoLayout->addWidget(statusIndicator); diff --git a/src/photoimportdialog.cpp b/src/photoimportdialog.cpp index d2b71db..6a8248b 100644 --- a/src/photoimportdialog.cpp +++ b/src/photoimportdialog.cpp @@ -36,8 +36,11 @@ PhotoImportDialog::PhotoImportDialog(const QStringList &files, QWidget *parent) { setupUI(); setModal(true); - resize(600, 700); + resize(600, 600); setWindowTitle("Import Photos to iDevice - iDescriptor"); +#ifdef WIN32 + setupWinWindow(this); +#endif } PhotoImportDialog::~PhotoImportDialog() @@ -181,7 +184,7 @@ void PhotoImportDialog::init() void PhotoImportDialog::onServerStarted() { - QString localIP = getLocalIP(); + QString localIP = HttpServer::getLocalIP(); int port = m_httpServer->getPort(); QString jsonFileName = m_httpServer->getJsonFileName(); QString url = @@ -267,23 +270,6 @@ void PhotoImportDialog::generateQRCode(const QString &url) QRcode_free(qrcode); } -QString PhotoImportDialog::getLocalIP() const -{ - foreach (const QNetworkInterface &interface, - QNetworkInterface::allInterfaces()) { - if (interface.flags().testFlag(QNetworkInterface::IsUp) && - !interface.flags().testFlag(QNetworkInterface::IsLoopBack)) { - foreach (const QNetworkAddressEntry &entry, - interface.addressEntries()) { - if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { - return entry.ip().toString(); - } - } - } - } - return "127.0.0.1"; -} - void PhotoImportDialog::toggleInstructionMode() { if (m_instructionStack->currentIndex() == 0) { diff --git a/src/photoimportdialog.h b/src/photoimportdialog.h index 566d3fb..4109d93 100644 --- a/src/photoimportdialog.h +++ b/src/photoimportdialog.h @@ -21,6 +21,7 @@ #define PHOTOIMPORTDIALOG_H #include "httpserver.h" +#include "iDescriptor-ui.h" #include #include #include @@ -68,7 +69,6 @@ private: void setupUI(); void generateQRCode(const QString &url); - QString getLocalIP() const; }; #endif // PHOTOIMPORTDIALOG_H diff --git a/src/photomodel.cpp b/src/photomodel.cpp index 024e05c..ffa113b 100644 --- a/src/photomodel.cpp +++ b/src/photomodel.cpp @@ -182,7 +182,7 @@ bool PhotoModel::populatePhotoPaths() char **files = nullptr; size_t count = 0; err = - ServiceManager::safeAfcReadDirectory(m_device, photoDir, &files, count); + ServiceManager::safeAfcReadDirectory(m_device, photoDir, &files, &count); if (err) { qDebug() << "Failed to read photo directory:" << photoDir << "Error:" << err->message; diff --git a/src/platform/windows/win_common.h b/src/platform/windows/win_common.h index 1403175..5d5b749 100644 --- a/src/platform/windows/win_common.h +++ b/src/platform/windows/win_common.h @@ -104,4 +104,20 @@ inline void setupWinWindow(QWidget *window) } else { enableAcrylic(hwnd); } -} \ No newline at end of file +} + +enum CornerPreference : int { + Corner_Default = DWMWCP_DEFAULT, + Corner_NoRound = DWMWCP_DONOTROUND, + Corner_Round = DWMWCP_ROUND, + Corner_RoundSmall = DWMWCP_ROUNDSMALL +}; +inline void SetCorner(HWND hwnd, CornerPreference corner) +{ + if (corner != Corner_Default) { + DWM_WINDOW_CORNER_PREFERENCE cp = + static_cast(corner); + DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cp, + sizeof(cp)); + } +} diff --git a/src/qballoontip.cpp b/src/qballoontip.cpp index e42f569..411d93a 100644 --- a/src/qballoontip.cpp +++ b/src/qballoontip.cpp @@ -130,41 +130,45 @@ void QBalloonTip::balloon(const QPoint &pos, int msecs) if (!screen) { screen = QGuiApplication::primaryScreen(); } - QRect scr = screen->availableGeometry(); - const int border = 1; - const int ah = 18, ao = 18, aw = 18, rc = 7; - // bool arrowAtTop = (pos.y() + sh.height() + ah < scr.height()); - // bool arrowAtLeft = (pos.x() + sh.width() - ao < scr.width()); - // setContentsMargins(border + 3, border + (arrowAtTop ? ah : 0) + 2, - // border + 3, border + (arrowAtTop ? 0 : ah) + 2); - updateGeometry(); - QSize sz = sizeHint(); QRect screenRect = screen->availableGeometry(); - // Calculate the total required size for the balloon widget, including - // potential arrow space. Assuming the arrow takes up 'ah' height at either - // the top or bottom of the widget. - QSize sh_total = QSize(sz.width(), sz.height() + ah); + // Ensure layout is up to date so we get the correct size + ensurePolished(); + adjustSize(); - // Determine the desired X position: center the balloon horizontally on - // pos.x 'pos' is the global bottom-center of your button. - int targetX = pos.x() - sh_total.width() / 2; - // Clamp X position to screen bounds - targetX = qBound(screenRect.left(), targetX, - screenRect.right() - sh_total.width()); + QSize balloonSize = size(); + if (!balloonSize.isValid() || balloonSize.isEmpty()) { + balloonSize = sizeHint(); + } - // Determine the desired Y position: Place the bottom of the balloon at - // pos.y() (button's bottom) This makes the balloon appear ABOVE the button. - int targetY = pos.y() - sh_total.height(); - // Clamp Y position to screen bounds - targetY = qBound(screenRect.top(), targetY, - screenRect.bottom() - sh_total.height()); + // Arrow dimensions + const int arrowGap = 40; // gap between balloon and anchor + const int margin = 5; // margin from screen edges - // Apply the calculated position - move(targetX, targetY); + // Calculate horizontal position - center on the anchor point + int balloonX = pos.x() - balloonSize.width() / 2; + + // Clamp to screen bounds with margin + balloonX = qBound(screenRect.left() + margin, balloonX, + screenRect.right() - balloonSize.width() - margin); + + // Calculate vertical position - prefer above the anchor point + int balloonY; + int spaceAbove = pos.y() - screenRect.top(); + + if (spaceAbove >= balloonSize.height() + arrowGap + margin) { + // Show above the anchor point (preferred) + balloonY = pos.y() - balloonSize.height() - arrowGap; + } else { + // Not enough space above, show below + balloonY = pos.y() + arrowGap; + } + + balloonY = qBound(screenRect.top() + margin, balloonY, + screenRect.bottom() - balloonSize.height() - margin); + + setGeometry(balloonX, balloonY, balloonSize.width(), balloonSize.height()); - // if (msecs > 0) - // timer = startTimer(msecs); show(); raise(); activateWindow(); @@ -192,11 +196,20 @@ bool QBalloonTip::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast(event); - // Check if click is outside the balloon - if (!geometry().contains(mouseEvent->globalPos())) { + + if (m_button) { + if (QWidget *clickedWidget = qobject_cast(obj)) { + if (clickedWidget == m_button || + m_button->isAncestorOf(clickedWidget)) { + return false; + } + } + } + + if (m_visible && !geometry().contains(mouseEvent->globalPos())) { m_visible = false; close(); - return false; + return true; } } else if (event->type() == QEvent::WindowDeactivate) { // Close when window loses focus diff --git a/src/qballoontip.h b/src/qballoontip.h index ae5a721..5894092 100644 --- a/src/qballoontip.h +++ b/src/qballoontip.h @@ -1,6 +1,7 @@ #ifndef QBALLOONTIP_H #define QBALLOONTIP_H +#include "iDescriptor-ui.h" #include #include #include @@ -16,6 +17,9 @@ public: void updateBalloonPosition(const QPoint &pos); void toggleBaloon(const QPoint &pos, int timeout, bool forceVisible); void balloon(const QPoint &, int msecs); + ZIconWidget *getButton() { return m_button; } + ZIconWidget *m_button = + new ZIconWidget(QIcon(":/resources/icons/UimProcess.png"), "Processes"); signals: void messageClicked(); diff --git a/src/querymobilegestaltwidget.cpp b/src/querymobilegestaltwidget.cpp index 2c57857..eda6230 100644 --- a/src/querymobilegestaltwidget.cpp +++ b/src/querymobilegestaltwidget.cpp @@ -46,8 +46,12 @@ QueryMobileGestaltWidget::QueryMobileGestaltWidget(iDescriptorDevice *device, void QueryMobileGestaltWidget::setupUI() { setWindowTitle("Query MobileGestalt - iDescriptor"); +#ifdef WIN32 + resize(600, 500); + setMaximumSize(800, 600); +#else setMinimumSize(800, 600); - +#endif // Main layout mainLayout = new QVBoxLayout(this); diff --git a/src/releasechangelogdialog.cpp b/src/releasechangelogdialog.cpp index 811054c..da09075 100644 --- a/src/releasechangelogdialog.cpp +++ b/src/releasechangelogdialog.cpp @@ -36,16 +36,19 @@ ReleaseChangelogDialog::ReleaseChangelogDialog(QJsonDocument data, : QDialog(parent) { setupUI(data); +#ifdef WIN32 + setupWinWindow(this); +#endif } ReleaseChangelogDialog::~ReleaseChangelogDialog() {} void ReleaseChangelogDialog::setupUI(const QJsonDocument &data) { - setWindowTitle("iDescriptor - Release Changelog"); + setWindowTitle("Release Changelog"); setModal(true); - setMinimumSize(500, 250); - resize(600, 300); + setMinimumSize(400, 250); + resize(500, 400); m_mainLayout = new QVBoxLayout(this); m_mainLayout->setContentsMargins(20, 20, 20, 20); diff --git a/src/servicemanager.cpp b/src/servicemanager.cpp index 9f79b56..1d125b2 100644 --- a/src/servicemanager.cpp +++ b/src/servicemanager.cpp @@ -23,12 +23,12 @@ IdeviceFfiError *ServiceManager::safeAfcReadDirectory( const iDescriptorDevice *device, const char *path, char ***dirs, - size_t count, std::optional altAfc) + size_t *count, std::optional altAfc) { return executeAfcClientOperation( device, - [path, dirs, &count, device](AfcClientHandle *client) { - return afc_list_directory(client, path, dirs, &count); + [path, dirs, count, device](AfcClientHandle *client) { + return afc_list_directory(client, path, dirs, count); }, altAfc); } @@ -416,4 +416,26 @@ IdeviceFfiError *ServiceManager::enableDevMode(const iDescriptorDevice *device) } return err; }); -} \ No newline at end of file +} + +IdeviceFfiError * +ServiceManager::safeParseOldDeviceBattery(const iDescriptorDevice *device, + PlistNavigator &ioreg, DeviceInfo &d) +{ + return executeOperation( + device, [device, &ioreg, &d]() -> IdeviceFfiError * { + parseOldDeviceBattery(ioreg, d); + return nullptr; + }); +} + +IdeviceFfiError * +ServiceManager::safeParseDeviceBattery(const iDescriptorDevice *device, + PlistNavigator &ioreg, DeviceInfo &d) +{ + return executeOperation( + device, [device, &ioreg, &d]() -> IdeviceFfiError * { + parseDeviceBattery(ioreg, d); + return nullptr; + }); +} diff --git a/src/servicemanager.h b/src/servicemanager.h index 96f790c..f0fa37c 100644 --- a/src/servicemanager.h +++ b/src/servicemanager.h @@ -241,7 +241,7 @@ public: // Specific AFC operation wrappers static IdeviceFfiError *safeAfcReadDirectory( const iDescriptorDevice *device, const char *path, char ***dirs, - size_t count, std::optional altAfc = std::nullopt); + size_t *count, std::optional altAfc = std::nullopt); static IdeviceFfiError * safeAfcGetFileInfo(const iDescriptorDevice *device, const char *path, @@ -307,6 +307,12 @@ public: static IdeviceFfiError * revealDeveloperModeOptionInUI(const iDescriptorDevice *device); static IdeviceFfiError *enableDevMode(const iDescriptorDevice *device); + static IdeviceFfiError * + safeParseOldDeviceBattery(const iDescriptorDevice *device, + PlistNavigator &ioreg, DeviceInfo &d); + static IdeviceFfiError * + safeParseDeviceBattery(const iDescriptorDevice *device, + PlistNavigator &ioreg, DeviceInfo &d); }; #endif // SERVICEMANAGER_H diff --git a/src/settingswidget.cpp b/src/settingswidget.cpp index 326e65d..6bb9292 100644 --- a/src/settingswidget.cpp +++ b/src/settingswidget.cpp @@ -407,7 +407,10 @@ void SettingsWidget::connectSignals() if (m_backDropTypeCombo) { connect(m_backDropTypeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, - &SettingsWidget::onSettingChanged); + [this]() { + m_restartRequired = true; + onSettingChanged(); + }); } #endif } diff --git a/src/sponsorwidget.cpp b/src/sponsorwidget.cpp index 75730e8..f979f9f 100644 --- a/src/sponsorwidget.cpp +++ b/src/sponsorwidget.cpp @@ -18,14 +18,10 @@ */ #include "sponsorwidget.h" -#include "iDescriptor.h" // Include for REPO_URL -#include "sponsorappcard.h" -#include -#include -#include -SponsorWidget::SponsorWidget(QWidget *parent) : QWidget(parent) +SponsorWidget::SponsorWidget(QWidget *parent) : Tool(parent) { + setMaximumSize(600, 400); setLayout(new QVBoxLayout(this)); QLabel *sponsorTitle = new QLabel("Would you like to sponsor us?"); sponsorTitle->setStyleSheet("font-weight: bold; font-size: 16pt;"); diff --git a/src/sponsorwidget.h b/src/sponsorwidget.h index e835205..cc0767c 100644 --- a/src/sponsorwidget.h +++ b/src/sponsorwidget.h @@ -20,9 +20,15 @@ #ifndef SPONSORWIDGET_H #define SPONSORWIDGET_H +#include "base/tool.h" +#include "iDescriptor.h" +#include "sponsorappcard.h" +#include +#include +#include #include -class SponsorWidget : public QWidget +class SponsorWidget : public Tool { Q_OBJECT public: diff --git a/src/statusballoon.cpp b/src/statusballoon.cpp index 0678831..371f8d0 100644 --- a/src/statusballoon.cpp +++ b/src/statusballoon.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ BalloonProcess::BalloonProcess(ProcessItem *item, QWidget *parent) { auto *layout = new QVBoxLayout(this); layout->setSpacing(6); - layout->setContentsMargins(0, 0, 0, 0); + layout->setContentsMargins(15, 15, 15, 15); m_lastBytesTransferred = 0; m_lastUpdateTime = QDateTime::currentDateTime(); @@ -152,16 +153,18 @@ StatusBalloon::StatusBalloon(QWidget *parent) : QBalloonTip(parent) setMinimumHeight(300); setMinimumWidth(300); #ifdef WIN32 - // FIXME: doesnt work the second time we call it - enableAcrylic((HWND)winId()); + setAttribute(Qt::WA_TranslucentBackground); #endif - // Create main layout m_mainLayout = new QVBoxLayout(); m_mainLayout->setSpacing(8); - m_mainLayout->setContentsMargins(0, 0, 0, 0); + m_mainLayout->setContentsMargins(5, 5, 5, 5); + + m_noProcesesLabel = + new QLabel("Export & Import processes will appear here", this); // Header label m_headerLabel = new QLabel("Processes"); + m_headerLabel->hide(); QFont headerFont = m_headerLabel->font(); headerFont.setPointSize(headerFont.pointSize() + 2); headerFont.setBold(true); @@ -354,6 +357,7 @@ void StatusBalloon::updateHeader() else if (item->status == ProcessStatus::Failed) failed++; } + int total = running + completed + failed; QString headerText = QString("Processes: %1 running").arg(running); if (completed > 0 || failed > 0) { @@ -363,14 +367,23 @@ void StatusBalloon::updateHeader() } } m_headerLabel->setText(headerText); + + if (total == 0) { + m_headerLabel->hide(); + m_noProcesesLabel->show(); + return; + } else { + m_headerLabel->show(); + m_noProcesesLabel->hide(); + } } void StatusBalloon::handleShow(bool forceVisible) { - QPoint pos = m_button->mapToGlobal( + QPoint buttonBottomCenter = m_button->mapToGlobal( QPoint(m_button->width() / 2, m_button->height())); - toggleBaloon(pos, -1, forceVisible); + toggleBaloon(buttonBottomCenter, -1, forceVisible); } bool StatusBalloon::isProcessRunning(const QUuid &processId) const @@ -491,4 +504,27 @@ void StatusBalloon::handleJobUpdate(ProcessItem *item) item->processWidget->updateStats(); item->processWidget->updateButtons(); -} \ No newline at end of file +} + +#ifdef WIN32 +void StatusBalloon::showEvent(QShowEvent *event) +{ + QBalloonTip::showEvent(event); + // HWND changes after hide/show, so have reapply acrylic here + enableMica((HWND)winId()); + SetCorner((HWND)winId(), CornerPreference::Corner_Round); +} +#endif + +void StatusBalloon::resizeEvent(QResizeEvent *event) +{ + QBalloonTip::resizeEvent(event); + + if (!m_noProcesesLabel) + return; + + m_noProcesesLabel->adjustSize(); + int x = (width() - m_noProcesesLabel->width()) / 2; + int y = (height() - m_noProcesesLabel->height()) / 2; + m_noProcesesLabel->move(x, y); +} diff --git a/src/statusballoon.h b/src/statusballoon.h index 288dd2b..7836b30 100644 --- a/src/statusballoon.h +++ b/src/statusballoon.h @@ -81,10 +81,16 @@ public: bool isProcessRunning(const QUuid &processId) const; bool hasActiveProcesses() const; bool isCancelRequested(const QUuid &processId) const; - ZIconWidget *getButton() { return m_button; } void onCancelClicked(); void onOpenFolderClicked(); +protected: +#ifdef WIN32 + void showEvent(QShowEvent *event) override; +// void paintEvent(QPaintEvent *event) override; +#endif + void resizeEvent(QResizeEvent *event) override; + private: void updateHeader(); void handleShow(bool forceVisible = false); @@ -104,8 +110,6 @@ private: QMap m_processes; QUuid m_currentProcessId; mutable QMutex m_processesMutex; - - ZIconWidget *m_button = - new ZIconWidget(QIcon(":/resources/icons/UimProcess.png"), "Processes"); + QLabel *m_noProcesesLabel; }; #endif // STATUSBALLOON_H diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp index 14e9104..5f6d0fb 100644 --- a/src/toolboxwidget.cpp +++ b/src/toolboxwidget.cpp @@ -452,7 +452,6 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool, bool requiresDevice) case iDescriptorTool::LiveScreen: { LiveScreenWidget *liveScreen = new LiveScreenWidget(device); - liveScreen->setAttribute(Qt::WA_DeleteOnClose); liveScreen->show(); } break; case iDescriptorTool::RecoveryMode: { diff --git a/src/virtuallocationwidget.cpp b/src/virtuallocationwidget.cpp index ab82626..1ed8288 100644 --- a/src/virtuallocationwidget.cpp +++ b/src/virtuallocationwidget.cpp @@ -45,12 +45,17 @@ // FIXME: on macOS setupToolFrame in Tool widget does nothing // probably because we are using a QQuickWidget VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent) - : Tool(parent), m_device(device) + : QWidget(parent), m_device(device) { setWindowTitle("Virtual Location - iDescriptor"); setMinimumSize(600, 400); resize(800, 600); +#ifdef WIN32 + setObjectName("VirtualLocationWidget"); + setAttribute(Qt::WA_StyledBackground, true); +#endif + // Create the main layout QHBoxLayout *mainLayout = new QHBoxLayout(this); mainLayout->setContentsMargins(10, 10, 10, 10); diff --git a/src/virtuallocationwidget.h b/src/virtuallocationwidget.h index 7dd89ea..e3b63a1 100644 --- a/src/virtuallocationwidget.h +++ b/src/virtuallocationwidget.h @@ -31,7 +31,7 @@ #include #include -class VirtualLocation : public Tool +class VirtualLocation : public QWidget { Q_OBJECT diff --git a/src/welcomewidget.cpp b/src/welcomewidget.cpp index 0204394..69952e7 100644 --- a/src/welcomewidget.cpp +++ b/src/welcomewidget.cpp @@ -79,7 +79,7 @@ void WelcomeWidget::setupUI() // GitHub link m_githubLabel = createStyledLabel("Found an issue? Report it on GitHub", 12, - false, COLOR_HYPERLINK); + true, COLOR_HYPERLINK); m_githubLabel->setWordWrap(false); m_githubLabel->setMaximumWidth(m_imageLabel->sizeHint().width()); m_githubLabel->setCursor(Qt::PointingHandCursor); diff --git a/src/ztabwidget.cpp b/src/ztabwidget.cpp index 66ca6a2..c443c33 100644 --- a/src/ztabwidget.cpp +++ b/src/ztabwidget.cpp @@ -332,8 +332,11 @@ void ZTabWidget::updateTabStyles() if (tab->isChecked()) { tab->setStyleSheet(QString("ZTab {" " color: %1;" - // " color: #d7e1f4ff;" - " font-weight: 700;" +// " color: #d7e1f4ff;" +#ifdef WIN32 + "font-family : \"Segoe UI\", serif;" +#endif + " font-weight: 600;" " font-size: 20px;" " border: none;" " outline: none;" @@ -346,8 +349,11 @@ void ZTabWidget::updateTabStyles() } else { tab->setStyleSheet(QString("ZTab {" " color: #666;" - // " color: #2b5693;" - " font-weight: 700;" + // " color: #2b5693;" +#ifdef WIN32 + "font-family : \"Segoe UI\", serif;" +#endif + " font-weight: 600;" " font-size: 20px;" " border: none;" " outline: none;"