diff --git a/wingetui/__init__.py b/wingetui/__init__.py index 862de7381..1842b6e9a 100644 --- a/wingetui/__init__.py +++ b/wingetui/__init__.py @@ -126,7 +126,7 @@ def forceContinue(): self.loadStatus = 1000 # Override loading status skipButton.clicked.connect(forceContinue) - Thread(target=lambda: (time.sleep(15), self.callInMain.emit(skipButton.show)), daemon=True).start() + self.textEnterAnim = QVariantAnimation(self) self.textEnterAnim.setStartValue(0) @@ -209,94 +209,20 @@ def forceContinue(): def increaseStep(): self.loadStatus += 1 self.finishedPreloadingStep.connect(increaseStep) - if getSettings("AskedAbout3PackageManagers") == False or "--welcomewizard" in sys.argv: - self.askAboutPackageManagers(onclose=lambda: Thread(target=self.loadPreUIComponents, daemon=True).start()) + if getSettings("ShownWelcomeWizard") == False or "--welcomewizard" in sys.argv or "--welcome" in sys.argv: + self.askAboutPackageManagers(onclose=lambda: (Thread(target=self.loadPreUIComponents, daemon=True).start(), Thread(target=lambda: (time.sleep(15), self.callInMain.emit(skipButton.show)), daemon=True).start())) else: Thread(target=self.loadPreUIComponents, daemon=True).start() + Thread(target=lambda: (time.sleep(15), self.callInMain.emit(skipButton.show)), daemon=True).start() self.loadingText.setText(_("Checking for other running instances...")) except Exception as e: raise e def askAboutPackageManagers(self, onclose: object): - self.w = NotClosableWidget() - self.w.setObjectName("micawin") - self.w.setWindowFlag(Qt.WindowType.Window) - self.w.setWindowTitle("\x20") - pixmap = QPixmap(4, 4) - pixmap.fill(Qt.GlobalColor.transparent) - self.w.setWindowIcon(pixmap) - self.w.setAutoFillBackground(True) - self.w.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, False) - self.w.setWindowFlag(Qt.WindowType.WindowMinimizeButtonHint, False) - self.w.setWindowFlag(Qt.WindowType.WindowCloseButtonHint, False) - self.w.setWindowModality(Qt.WindowModality.WindowModal) - - self.w.setMinimumWidth(750) - self.w.setContentsMargins(20, 0, 20, 10) - mainLayout = QVBoxLayout() - label = (QLabel("
"+_("Welcome to WingetUI")+"
"+_("You may now choose your weapons")+"
")) - label.setAlignment(Qt.AlignmentFlag.AlignCenter) - label.setWordWrap(True) - mainLayout.addWidget(label) - label = (QLabel(_("WingetUI is based on package managers. They are the engines used to load, install update and remove software from your computer. Please select the desired package managers and hit \"Apply\" to continue. The default ones are Winget and Chocolatey"))) - label.setAlignment(Qt.AlignmentFlag.AlignCenter) - label.setWordWrap(True) - mainLayout.addWidget(label) - - winget = WelcomeWizardPackageManager(_("Enable {pm}").format(pm="Winget"), _("Microsoft's official package manager. It contains well known software such as browsers, PDF readers, windows add-ons and other utilities, as well as other less-known but useful software, such as Microsoft Visual C++ Redistributables. Packages from Winget have been carefully validated"), getMedia("winget")) - winget.setChecked(True) - scoop = WelcomeWizardPackageManager(_("Enable {pm}").format(pm="Scoop"), _("From scoop you will be able to download utilities that might not be suitable for everybody. Install CLI utilities such as nano, sudo or nmap for Windows. And with the ability to add custom buckets, you will be able to download unlimited amounts of different utilities, apps, fonts, games, and any other thing you can dream of."), getMedia("scoop")) - scoop.setChecked(False) - if (getSettings("ScoopAlreadySetup") or getSettings("ScoopEnabledByAssistant")) and not getSettings("DisableScoop"): - scoop.setChecked(True) - choco = WelcomeWizardPackageManager(_("Enable {pm}").format(pm="Chocolatey"), _("The package manager for Windows by default. With more than {0} packages on their repositories, you will find anything you want to install. From Firefox to Sysinternals, almost every package is available to download from Chocolatey servers").format("9500"), getMedia("choco")) - choco.setChecked(True) - - mainLayout.addSpacing(20) - mainLayout.addWidget(winget) - mainLayout.addWidget(scoop) - mainLayout.addWidget(choco) - mainLayout.addSpacing(20) - - mainLayout.addStretch() - - blayout = QHBoxLayout() - mainLayout.addLayout(blayout) - blayout.addStretch() - - def performSelectionAndContinue(): - self.w.close() - setSettings("AskedAbout3PackageManagers", True) - setSettings("DisableWinget", not winget.isChecked()) - setSettings("DisableScoop", not scoop.isChecked()) - setSettings("ScoopEnabledByAssistant", scoop.isChecked()) - setSettings("DisableChocolatey", not choco.isChecked()) - if choco.isChecked() and shutil.which("choco") != None: - setSettings("UseSystemChocolatey", True) - if scoop.isChecked() and shutil.which("scoop") == None: - os.startfile(os.path.join(realpath, "resources/install_scoop.cmd")) - else: - onclose() - - okbutton = QPushButton(_("Apply and start WingetUI")) - okbutton.setFixedSize(190, 30) - okbutton.setObjectName("AccentButton") - okbutton.clicked.connect(performSelectionAndContinue) - blayout.addWidget(okbutton) - - w = QWidget(self.w) - w.setObjectName("mainbg") - w.setLayout(mainLayout) - l = QHBoxLayout() - l.addWidget(w) - self.w.setLayout(l) - - r = ApplyMica(self.w.winId(), MICAMODE.DARK if isDark() else MICAMODE.LIGHT) - if r != 0: - self.w.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False) - self.w.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground, False) - self.w.setStyleSheet(darkCSS.replace("mainbg", "transparent" if r == 0x0 else "#202020") if isDark() else lightCSS.replace("mainbg", "transparent" if r == 0x0 else "#f5f5f5")) - self.w.show() + import welcome + self.ww = welcome.WelcomeWindow(callback=lambda: (self.popup.show(), onclose())) + self.popup.hide() + self.ww.show() def loadPreUIComponents(self): try: diff --git a/wingetui/customWidgets.py b/wingetui/customWidgets.py index f5d28bc0b..8788bfbb1 100644 --- a/wingetui/customWidgets.py +++ b/wingetui/customWidgets.py @@ -534,22 +534,22 @@ def __init__(self, text, description, image) -> None: mainw.setContentsMargins(0, 0, 0, 0) mainw.setObjectName("bgwidget") mainw.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) - self.checkbox = SectionCheckBox(text, mainw, margin=0, bigfont=True) + self.checkbox = QCheckBox(text) self.checkbox.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, False) - self.checkbox.stateChanged.connect(lambda v: (self.description.setEnabled(v), self.image.setEnabled(v))) - self.checkbox.setFixedHeight(30) - self.description = QLabel(description) + self.checkbox.stateChanged.connect(lambda v: (self.image.setEnabled(v))) + self.checkbox.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum) + self.description = ClickableLabel(description) + self.description.clicked.connect(self.checkbox.click) self.description.setWordWrap(True) - self.description.setEnabled(False) self.image = QLabel() - self.image.setPixmap(QPixmap(image).scaledToHeight(64, Qt.TransformationMode.SmoothTransformation)) + self.image.setPixmap(QPixmap(image).scaledToHeight(48, Qt.TransformationMode.SmoothTransformation)) h = QHBoxLayout() v = QVBoxLayout() v.addWidget(self.checkbox) v.addWidget(self.description, stretch=1) h.addLayout(v, stretch=1) h.addWidget(self.image) - h.setContentsMargins(16, 16, 16, 16) + h.setContentsMargins(12, 8, 16, 8) h2 = QHBoxLayout() h.addStretch() mainw.setLayout(h) @@ -560,9 +560,9 @@ def __init__(self, text, description, image) -> None: mainw.setFixedWidth(600) self.setLayout(h2) if isDark(): - self.setStyleSheet("""#bgwidget{background-color: rgba(255, 255, 255, 5%); border: 1px solid #101010; padding: 16px; border-radius: 16px;}""") + self.setStyleSheet("""#bgwidget{background-color: rgba(255, 255, 255, 5%); border: 1px solid #101010; padding: 8px; border-radius: 8px;}""") else: - self.setStyleSheet("""#bgwidget{background-color: rgba(255, 255, 255, 50%); border: 1px solid #eeeeee; padding: 16px; border-radius: 16px;}""") + self.setStyleSheet("""#bgwidget{background-color: rgba(255, 255, 255, 50%); border: 1px solid #eeeeee; padding: 8px; border-radius: 8px;}""") def setChecked(self, v: bool) -> None: self.checkbox.setChecked(v) diff --git a/wingetui/genericCustomWidgets.py b/wingetui/genericCustomWidgets.py index 80bbaa443..11d2d4915 100644 --- a/wingetui/genericCustomWidgets.py +++ b/wingetui/genericCustomWidgets.py @@ -310,10 +310,11 @@ def calculateSize(self) -> None: """ Recalculates minimum height """ - if self.getFullHeight() >= self.maxHeight: - self.setFixedHeight(self.maxHeight) - else: - self.setFixedHeight(self.getFullHeight() if self.getFullHeight() > 20 else 4) + if self.resizeBar: + if self.getFullHeight() >= self.maxHeight: + self.setFixedHeight(self.maxHeight) + else: + self.setFixedHeight(self.getFullHeight() if self.getFullHeight() > 20 else 4) def getFullHeight(self) -> int: """ @@ -325,15 +326,16 @@ def removeItem(self, item: QWidget): self.vlayout.removeWidget(item) self.rss() self.itemCount = self.vlayout.count() - if self.itemCount <= 0: + if self.itemCount <= 0 and self.resizeBar: globals.trayIcon.setIcon(QIcon(getMedia("greyicon"))) self.resizeBar.hide() def addItem(self, item: QWidget): self.vlayout.addWidget(item) self.itemCount = self.vlayout.count() - self.resizeBar.show() - globals.trayIcon.setIcon(QIcon(getMedia("icon"))) + if self.resizeBar: + self.resizeBar.show() + globals.trayIcon.setIcon(QIcon(getMedia("icon"))) class TreeWidgetItemWithQAction(QTreeWidgetItem): itemAction: QAction = QAction @@ -1191,6 +1193,16 @@ def closeEvent(self, event: QCloseEvent) -> None: globals.app.restoreOverrideCursor() return super().closeEvent(event) +class ClickableLabel(QLabel): + clicked = Signal() + + def __init__(self, text: str = "", parent: QWidget = None): + super().__init__(text, parent) + self.setMouseTracking(True) + + def mousePressEvent(self, ev: QMouseEvent) -> None: + self.clicked.emit() + return super().mousePressEvent(ev) if __name__ == "__main__": import __init__ \ No newline at end of file diff --git a/wingetui/resources/admin_color.png b/wingetui/resources/admin_color.png new file mode 100644 index 000000000..cb50115a8 Binary files /dev/null and b/wingetui/resources/admin_color.png differ diff --git a/wingetui/resources/agreement.png b/wingetui/resources/agreement.png new file mode 100644 index 000000000..b910632ad Binary files /dev/null and b/wingetui/resources/agreement.png differ diff --git a/wingetui/resources/choco_color.png b/wingetui/resources/choco_color.png new file mode 100644 index 000000000..1d7dba16f Binary files /dev/null and b/wingetui/resources/choco_color.png differ diff --git a/wingetui/resources/coffee.png b/wingetui/resources/coffee.png new file mode 100644 index 000000000..e35943496 Binary files /dev/null and b/wingetui/resources/coffee.png differ diff --git a/wingetui/resources/console_color.png b/wingetui/resources/console_color.png new file mode 100644 index 000000000..119386fac Binary files /dev/null and b/wingetui/resources/console_color.png differ diff --git a/wingetui/resources/empty.png b/wingetui/resources/empty.png new file mode 100644 index 000000000..cc1fdac99 Binary files /dev/null and b/wingetui/resources/empty.png differ diff --git a/wingetui/resources/finish.png b/wingetui/resources/finish.png new file mode 100644 index 000000000..d92ae0c96 Binary files /dev/null and b/wingetui/resources/finish.png differ diff --git a/wingetui/resources/github.png b/wingetui/resources/github.png new file mode 100644 index 000000000..5a3de8f81 Binary files /dev/null and b/wingetui/resources/github.png differ diff --git a/wingetui/resources/hacker.png b/wingetui/resources/hacker.png new file mode 100644 index 000000000..88311335d Binary files /dev/null and b/wingetui/resources/hacker.png differ diff --git a/wingetui/resources/kofi.png b/wingetui/resources/kofi.png new file mode 100644 index 000000000..fdcb58c30 Binary files /dev/null and b/wingetui/resources/kofi.png differ diff --git a/wingetui/resources/next_black.png b/wingetui/resources/next_black.png new file mode 100644 index 000000000..58f5a6d13 Binary files /dev/null and b/wingetui/resources/next_black.png differ diff --git a/wingetui/resources/next_white.png b/wingetui/resources/next_white.png new file mode 100644 index 000000000..706d31347 Binary files /dev/null and b/wingetui/resources/next_white.png differ diff --git a/wingetui/resources/node_color.png b/wingetui/resources/node_color.png new file mode 100644 index 000000000..4d0534678 Binary files /dev/null and b/wingetui/resources/node_color.png differ diff --git a/wingetui/resources/pip_color.png b/wingetui/resources/pip_color.png new file mode 100644 index 000000000..b6ab6963b Binary files /dev/null and b/wingetui/resources/pip_color.png differ diff --git a/wingetui/resources/previous_black.png b/wingetui/resources/previous_black.png new file mode 100644 index 000000000..5ffc7f156 Binary files /dev/null and b/wingetui/resources/previous_black.png differ diff --git a/wingetui/resources/previous_white.png b/wingetui/resources/previous_white.png new file mode 100644 index 000000000..30103c12c Binary files /dev/null and b/wingetui/resources/previous_white.png differ diff --git a/wingetui/resources/rocket.png b/wingetui/resources/rocket.png new file mode 100644 index 000000000..a911edc86 Binary files /dev/null and b/wingetui/resources/rocket.png differ diff --git a/wingetui/resources/scoop_color.png b/wingetui/resources/scoop_color.png new file mode 100644 index 000000000..83e900a2f Binary files /dev/null and b/wingetui/resources/scoop_color.png differ diff --git a/wingetui/resources/shield_green.png b/wingetui/resources/shield_green.png new file mode 100644 index 000000000..3ee4f646c Binary files /dev/null and b/wingetui/resources/shield_green.png differ diff --git a/wingetui/resources/shield_question.png b/wingetui/resources/shield_question.png new file mode 100644 index 000000000..1f02ec3e2 Binary files /dev/null and b/wingetui/resources/shield_question.png differ diff --git a/wingetui/resources/shield_red.png b/wingetui/resources/shield_red.png new file mode 100644 index 000000000..ac8653270 Binary files /dev/null and b/wingetui/resources/shield_red.png differ diff --git a/wingetui/resources/shield_reload.png b/wingetui/resources/shield_reload.png new file mode 100644 index 000000000..42a708707 Binary files /dev/null and b/wingetui/resources/shield_reload.png differ diff --git a/wingetui/resources/shield_yellow.png b/wingetui/resources/shield_yellow.png new file mode 100644 index 000000000..1b79237a5 Binary files /dev/null and b/wingetui/resources/shield_yellow.png differ diff --git a/wingetui/resources/simple_user.png b/wingetui/resources/simple_user.png new file mode 100644 index 000000000..75b77b118 Binary files /dev/null and b/wingetui/resources/simple_user.png differ diff --git a/wingetui/resources/update_pc_color.png b/wingetui/resources/update_pc_color.png new file mode 100644 index 000000000..29353b068 Binary files /dev/null and b/wingetui/resources/update_pc_color.png differ diff --git a/wingetui/resources/winget_color.png b/wingetui/resources/winget_color.png new file mode 100644 index 000000000..77cda9d59 Binary files /dev/null and b/wingetui/resources/winget_color.png differ diff --git a/wingetui/resources/youtube.png b/wingetui/resources/youtube.png new file mode 100644 index 000000000..f568b59d8 Binary files /dev/null and b/wingetui/resources/youtube.png differ diff --git a/wingetui/tools.py b/wingetui/tools.py index 3c780ef54..a889376c6 100644 --- a/wingetui/tools.py +++ b/wingetui/tools.py @@ -181,7 +181,6 @@ def isDark() -> bool: return False return SYSTEM_THEME_ON_LAUNCH == 0 - def queueProgram(id: str): globals.pending_programs.append(id) diff --git a/wingetui/welcome.py b/wingetui/welcome.py new file mode 100644 index 000000000..ce94f97b8 --- /dev/null +++ b/wingetui/welcome.py @@ -0,0 +1,1254 @@ +from threading import Thread + +import os +import time +import ctypes +from PySide6.QtGui import * +from PySide6.QtCore import * +from PySide6.QtWidgets import * +from customWidgets import * + +import globals + +from languages import * +from tools import * +from tools import _ + +from win32mica import * + + +dwm = ctypes.windll.dwmapi + +class WelcomeWindow(QMainWindow): + callback: object = None + def __init__(self, callback: object) -> None: + super().__init__() + self.callback = callback + + self.switched = False + + self.widgetOrder = ( + FirstRunSlide(), + PackageManagersSlide(), + AdministratorPreferences(), + UpdatesPreferences(), + LastSlide(), + ) + + for w in self.widgetOrder: + w.hide() + + for w in self.widgetOrder: + w.next.connect(self.nextWidget) + w.previous.connect(self.previousWidget) + w.skipped.connect(self.lastWidget) + w.finished.connect(self.close) + + + self.currentIndex = -1 + + self.setFixedSize((800), (600)) + self.bgWidget = QStackedWidget(self) + self.bgWidget.setObjectName("BackgroundWidget") + self.setWindowFlag(Qt.WindowMinimizeButtonHint, False) + self.setWindowFlag(Qt.WindowCloseButtonHint, False) + self.setAutoFillBackground(True) + self.setAttribute(Qt.WA_TranslucentBackground) + self.setWindowTitle(_("Welcome to WingetUI")) + self.setWindowTitle(" ") + self.setWindowIcon(QIcon(getPath("empty.png"))) + self.setCentralWidget(self.bgWidget) + + ApplyMica(self.winId().__int__(), isDark()) + + colors = getColors() + + if isDark(): + self.bgWidget.setStyleSheet(f""" + * {{ + color: #eeeeee; + background-color: transparent; + border-radius: 4px; + font-family: "Segoe UI Variable Text" + }} + #BackgroundWidget {{ + border: 0 solid rgba(80, 80, 80, 25%); + padding: 20px; + background-color: transparent; + border-radius: 0; + padding-left: 30px; + padding-right: 30px; + }} + QLabel {{ + background-color: transparent; + }} + #SampleItem {{ + font-family: "Segoe UI Variable Text"; + width: 100px; + background-color: rgba(80, 80, 80, 7%); + padding: 20px; + border-radius: 8px; + border: 1px solid rgba(100, 100, 100, 15%); + border-top: 1px solid rgba(100, 100, 100, 15%); + height: 25px; + }} + #FramelessSampleItem {{ + font-family: "Segoe UI Variable Text"; + width: 100px; + background-color: transparent; + padding: 20px; + border-radius: 8px; + border: none; + height: 25px; + }} + QPushButton {{ + font-family: "Segoe UI Variable Text"; + font-size: 9pt; + width: 100px; + background-color: rgba(60, 60, 60, 25%); + border: 1px solid rgba(100, 100, 100, 25%); + border-top: 1px solid rgba(100, 100, 100, 25%); + border-radius: 8px; + height: 30px; + }} + QPushButton:hover {{ + background-color: rgba(77, 77, 77, 50%); + border: 1px solid rgba(89, 89, 89, 50%); + border-top: 1px solid rgba(95, 95, 95, 50%); + }} + QPushButton:pressed {{ + background-color: rgba(89, 89, 89, 50%); + border: 1px solid rgba(95, 95, 95, 50%); + border-top: 1px solid rgba(99, 99, 99 , 50%); + }} + #AccentButton{{ + color: black; + background-color: rgb({colors[1]}); + border-color: rgb({colors[1]}); + border-bottom-color: rgb({colors[2]}); + }} + #AccentButton:hover{{ + background-color: rgba({colors[1]}, 80%); + border-color: rgb({colors[1]}); + border-bottom-color: rgb({colors[2]}); + }} + #AccentButton:disabled{{ + background-color: #212121; + border-color: #303030; + border-bottom-color: #363636; + }} + #FocusSelector {{ + border: 4px solid rgb({colors[1]}); + border-radius: 16px; + background-color: transparent;/*rgb({colors[1]})*/; + }} + QLabel {{ + border: none; + border-radius: 6px; + }} + #TitleLabel {{ + font-size: 26pt; + }} + QCheckBox {{ + font-family: Segoe UI Variable Display; + font-weight: bold; + font-size: 15pt; + }} + QCheckBox::indicator{{ + height: 20px; + width: 20px; + }} + QTreeView::indicator:unchecked,QCheckBox::indicator:unchecked {{ + background-color: rgba(30, 30, 30, 25%); + border: 1px solid #444444; + border-radius: 6px; + }} + QTreeView::indicator:disabled,QCheckBox::indicator:disabled {{ + background-color: rgba(30, 30, 30, 5%); + color: #dddddd; + border: 1px solid rgba(255, 255, 255, 5%); + border-radius: 6px; + }} + QTreeView::indicator:unchecked:hover,QCheckBox::indicator:unchecked:hover {{ + background-color: #2a2a2a; + border: 1px solid #444444; + border-radius: 6px; + }} + QTreeView::indicator:checked,QCheckBox::indicator:checked {{ + border: 1px solid #444444; + background-color: rgba({colors[1]}, 80%); + border-radius: 6px; + image: url("{getMedia("tick")}"); + }} + QTreeView::indicator:disabled,QCheckBox::indicator:checked:disabled {{ + border: 1px solid #444444; + background-color: #303030; + color: #dddddd; + border-radius:6px; + }} + QTreeView::indicator:checked:hover,QCheckBox::indicator:checked:hover {{ + border: 1px solid #444444; + background-color: rgb({colors[2]}); + border-radius: 6px; + }} + QScrollBar {{ + background: transparent; + margin: 4px; + margin-left: 0; + width: 16px; + height: 20px; + border: none; + border-radius: 5px; + }} + QScrollBar:horizontal {{ + margin-bottom: 0; + padding-bottom: 0; + height: 12px; + }} + QScrollBar::handle {{ + margin: 3px; + min-height: 20px; + min-width: 20px; + border-radius: 3px; + background: rgba(80, 80, 80, 40%); + }} + QScrollBar::handle:hover {{ + margin: 3px; + border-radius: 3px; + background: rgba(112, 112, 112, 35%); + }} + QScrollBar::add-line {{ + height: 0; + width: 0; + subcontrol-position: bottom; + subcontrol-origin: margin; + }} + QScrollBar::sub-line {{ + height: 0; + width: 0; + subcontrol-position: top; + subcontrol-origin: margin; + }} + QScrollBar::up-arrow, QScrollBar::down-arrow {{ + background: none; + }} + QScrollBar::add-page, QScrollBar::sub-page {{ + background: none; + }} + """) + else: + self.bgWidget.setStyleSheet(f""" + * {{ + color: black; + background-color: transparent; + border-radius: 4px; + font-family: "Segoe UI Variable Text" + }} + #BackgroundWidget {{ + border: 0 solid #eeeeee; + padding: 20px; + background-color: transparent; + border-radius: 0; + padding-left: 30px; + padding-right: 30px; + }} + QLabel {{ + background-color: none; + }} + #SampleItem {{ + font-family: "Segoe UI Variable Text"; + width: 100px; + background-color: rgba(255, 255, 255, 70%); + padding: 20px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 70%); + height: 25px; + }} + #FramelessSampleItem {{ + font-family: "Segoe UI Variable Text"; + width: 100px; + background-color: transparent; + padding: 20px; + border-radius: 8px; + border: none; + height: 25px; + }} + QPushButton {{ + font-family: "Segoe UI Variable Text"; + font-size: 9pt; + width: 100px; + background-color: #ffffff; + border-radius: 8px; + border: 1px solid rgba(230, 230, 230, 80%); + height: 30px; + border-bottom: 1px solid rgba(220, 220, 220, 100%); + }} + QPushButton:hover {{ + background-color: rgba(240, 240, 240, 50%); + border: 1px solid rgba(220, 220, 220, 80%); + border-bottom: 1px solid rgba(200, 200, 200, 100%); + }} + QPushButton:pressed {{ + background-color: rgba(89, 89, 89, 50%); + border: 1px solid rgba(95, 95, 95, 50%); + border-top: 1px solid rgba(99, 99, 99 , 50%); + }} + #AccentButton{{ + color: black; + background-color: rgb({colors[1]}); + border: 1px solid rgba(230, 230, 230, 80%); + height: 30px; + border-bottom: 1px solid rgba(220, 220, 220, 100%); + }} + #AccentButton:hover{{ + background-color: rgba({colors[2]}, 80%); + border: 1px solid rgba(95, 95, 95, 50%); + border-top: 1px solid rgba(99, 99, 99 , 50%); + }} + #AccentButton:disabled{{ + background-color: transparent; + border-color: #eeeeee; + border-bottom-color: #eeeeee; + }} + #FocusSelector {{ + border: 4px solid rgb({colors[1]}); + border-radius: 16px; + background-color: transparent; + }} + QLabel {{ + border: none; + border-radius: 6px; + }} + #TitleLabel {{ + font-size: 26pt; + }} + QCheckBox {{ + font-weight: bold; + font-dize: 15pt; + }} + QCheckBox::indicator{{ + height: 20px; + width: 20px; + }} + QTreeView::indicator:unchecked,QCheckBox::indicator:unchecked {{ + background-color: rgba(255, 255, 255, 25%); + border: 1px solid rgba(0, 0, 0, 10%); + border-radius: 6px; + }} + QTreeView::indicator:disabled,QCheckBox::indicator:disabled {{ + background-color: rgba(240, 240, 240, 0%); + color: #444444; + border: 1px solid rgba(0, 0, 0, 5%); + border-radius: 6px; + }} + QTreeView::indicator:unchecked:hover,QCheckBox::indicator:unchecked:hover {{ + background-color: rgba(0, 0, 0, 5%); + border: 1px solid rgba(0, 0, 0, 20%); + border-radius: 6px; + }} + QTreeView::indicator:checked,QCheckBox::indicator:checked {{ + border: 1px solid rgb({colors[3]}); + background-color: rgb({colors[2]}); + border-radius: 6px; + image: url("{getMedia("tick")}"); + }} + QTreeView::indicator:checked:disabled,QCheckBox::indicator:checked:disabled {{ + border: 1px solid #444444; + background-color: #303030; + color: #444444; + border-radius: 6px; + }} + QTreeView::indicator:checked:hover,QCheckBox::indicator:checked:hover {{ + border: 1px solid rgb({colors[3]}); + background-color: rgb({colors[3]}); + border-radius: 6px; + }} + QScrollBar {{ + background: transparent; + margin: 4px; + margin-left: 0; + width: 16px; + height: 20px; + border: none; + border-radius: 5px; + }} + QScrollBar:horizontal {{ + margin-bottom: 0; + padding-bottom: 0; + height: 12px; + }} + QScrollBar:vertical {{ + background: rgba(255, 255, 255, 0%); + margin: 4px; + width: 16px; + border: none; + border-radius: 5px; + }} + QScrollBar::handle:vertical {{ + margin: 3px; + border-radius: 3px; + min-height: 20px; + background: rgba(90, 90, 90, 25%); + }} + QScrollBar::handle:vertical:hover {{ + margin: 3px; + border-radius: 3px; + background: rgba(90, 90, 90, 35%); + }} + QScrollBar::add-line:vertical {{ + height: 0; + subcontrol-position: bottom; + subcontrol-origin: margin; + }} + QScrollBar::sub-line:vertical {{ + height: 0; + subcontrol-position: top; + subcontrol-origin: margin; + }} + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {{ + background: none; + }} + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {{ + background: none; + }} + """) + + + self.nextWidget(anim=False) + + self.show() + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + + def setWidget(self, w: QWidget, back=False, anim=True) -> None: + self.bgWidget.setCurrentIndex(self.bgWidget.addWidget(w)) + if anim: + if back: + w.invertedinAnim() + else: + w.inAnim() + + def nextWidget(self, anim: bool = True) -> None: + if self.currentIndex == len(self.widgetOrder)-1: + self.close() + else: + self.currentIndex += 1 + w: BasicNavWidget = self.widgetOrder[self.currentIndex] + self.setWidget(w, anim=anim) + + def previousWidget(self) -> None: + if self.currentIndex == 0: + try: + raise ValueError("The specified index is not present in the list of wizard widgets") + except Exception as e: + report(e) + else: + self.currentIndex -= 1 + w: BasicNavWidget = self.widgetOrder[self.currentIndex] + self.setWidget(w, back=True) + + def lastWidget(self) -> None: + self.currentIndex = len(self.widgetOrder)-1 + w: BasicNavWidget = self.widgetOrder[-1] + self.setWidget(w) + + def close(self) -> bool: + if self.callback: + self.callback() + return super().close() + +class BasicNavWidget(QWidget): + next = Signal() + previous = Signal() + finished = Signal() + skipped = Signal() + centralWidget: QWidget = None + + def __init__(self, parent: bool = None, startEnabled: bool = False, closeEnabled: bool = False, finishEnabled: bool = False, nextGreyed: bool = False, noNavBar: bool = False) -> None: + super().__init__(parent=parent) + self.l = QVBoxLayout() + self.setLayout(self.l) + + if(isDark()): + self.iconMode = "white" + self.negIconMode = "black" + else: + self.iconMode = "black" + self.negIconMode = "white" + + self.navLayout = QHBoxLayout() + if not noNavBar: + if closeEnabled: + closeButton = QPushButton(_("Skip")) + closeButton.setIconSize(QSize(12, 12)) + closeButton.setFixedSize((96), (36)) + closeButton.setIcon(QIcon(getPath(f"close_{self.iconMode}.png"))) + closeButton.clicked.connect(lambda: self.outAnim(self.skipped.emit)) + self.navLayout.addWidget(closeButton) + self.navLayout.addStretch() + if startEnabled: + startButton = QPushButton(_("Start")) + startButton.setLayoutDirection(Qt.RightToLeft) + startButton.setIconSize(QSize(12, 12)) + startButton.setFixedSize((96), (36)) + startButton.setIcon(QIcon(getPath(f"next_black.png"))) + startButton.clicked.connect(lambda: self.outAnim(self.next.emit)) + startButton.setObjectName("AccentButton") + self.navLayout.addWidget(startButton) + else: + backButton = QPushButton("") + backButton.setFixedSize((36), (36)) + backButton.clicked.connect(lambda: self.invertedOutAnim(self.previous.emit)) + backButton.setIcon(QIcon(getPath(f"previous_{self.iconMode}.png"))) + backButton.setIconSize(QSize(12, 12)) + self.navLayout.addWidget(backButton) + if finishEnabled: + finishButton = QPushButton(_("Finish")) + finishButton.setObjectName("AccentButton") + finishButton.setFixedSize((96), (36)) + finishButton.setIconSize(QSize(12, 12)) + finishButton.setLayoutDirection(Qt.RightToLeft) + finishButton.clicked.connect(lambda: self.outAnim(self.finished.emit)) + self.navLayout.addWidget(finishButton) + else: + self.nextButton = QPushButton("") + self.nextButton.setEnabled(not nextGreyed) + self.nextButton.setIconSize(QSize(12, 12)) + self.nextButton.setFixedSize((36), (36)) + self.nextButton.clicked.connect(lambda:self.outAnim(self.next.emit)) + self.nextButton.setIcon(QIcon(getPath(f"next_black.png"))) + self.nextButton.setObjectName("AccentButton") + self.navLayout.addWidget(self.nextButton) + + def enableNextButton(self) -> None: + self.nextButton.setEnabled(True) + + def nextWidget(self): + self.outAnim(self.next.emit) + + def lastWidget(self): + self.outAnim(self.skipped.emit) + + def setCentralWidget(self, w: QWidget) -> QWidget: + self.centralWidget = w + self.l.addWidget(w, stretch=1) + self.l.addLayout(self.navLayout, stretch=0) + self.opacityEffect = QGraphicsOpacityEffect(self.centralWidget) + self.centralWidget.setGraphicsEffect(self.opacityEffect) + self.opacityEffect.setOpacity(0) + + def inAnim(self) -> None: + anim = QVariantAnimation(self.centralWidget) + anim.setStartValue(0) + anim.setEndValue(100) + anim.valueChanged.connect(lambda v: self.opacityEffect.setOpacity(v/100)) + anim.setEasingCurve(QEasingCurve.OutQuad) + anim.setDuration(200) + anim.start() + + bgAnim = QPropertyAnimation(self.centralWidget, b"pos", self.centralWidget) + pos = self.centralWidget.pos() + pos.setX(pos.x()+(self.centralWidget.width()/20)) + bgAnim.setStartValue(pos) + bgAnim.setEasingCurve(QEasingCurve.OutQuad) + bgAnim.setEndValue(self.centralWidget.pos()) + bgAnim.setDuration(200) + bgAnim.start() + + def invertedinAnim(self) -> None: + anim = QVariantAnimation(self) + anim.setStartValue(0) + anim.setEndValue(100) + anim.valueChanged.connect(lambda v: self.opacityEffect.setOpacity(v/100)) + anim.setEasingCurve(QEasingCurve.OutQuad) + anim.setDuration(20) + anim.start() + + bgAnim = QPropertyAnimation(self.centralWidget, b"pos", self.centralWidget) + pos = self.centralWidget.pos() + pos.setX(self.centralWidget.x()-(self.centralWidget.width()/20)) + bgAnim.setStartValue(pos) + bgAnim.setEndValue(self.centralWidget.pos()) + bgAnim.setEasingCurve(QEasingCurve.OutQuad) + bgAnim.setDuration(200) + bgAnim.start() + + def outAnim(self, f) -> None: + anim = QVariantAnimation(self) + anim.setStartValue(100) + anim.setEndValue(0) + anim.valueChanged.connect(lambda v: self.opacityEffect.setOpacity(v/100)) + anim.setEasingCurve(QEasingCurve.InQuad) + anim.setDuration(100) + anim.start() + anim.finished.connect(f) + + def invertedOutAnim(self, f) -> None: + anim = QVariantAnimation(self) + anim.setStartValue(100) + anim.setEndValue(0) + anim.valueChanged.connect(lambda v: self.opacityEffect.setOpacity(v/100)) + anim.setEasingCurve(QEasingCurve.InQuad) + anim.setDuration(100) + anim.start() + anim.finished.connect(f) + + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + + def window(self) -> WelcomeWindow: + return super().window() + +class IconLabel(QWidget): + def __init__(self, size=96, frame=True) -> None: + super().__init__() + self.setAttribute(Qt.WA_StyledBackground) + if frame: + self.setObjectName("SampleItem") + else: + self.setObjectName("FramelessSampleItem") + self.iconSize = size + self.setLayout(QHBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.iconLabel = QLabel() + self.iconLabel.setMinimumHeight((self.iconSize+40)) + self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.setMinimumHeight((self.iconSize)) + self.textLabel = QLabel() + self.textLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse) + self.textLabel.setWordWrap(True) + self.textLabel.setStyleSheet("font-size: 10pt;") + self.textLabel.setOpenExternalLinks(True) + if frame: self.layout().addSpacing((40/96*self.iconSize)) + self.layout().addWidget(self.iconLabel, stretch=0) + self.layout().addSpacing((30/96*self.iconSize)) + self.layout().addWidget(self.textLabel, stretch=1) + if frame: self.layout().addSpacing((30/96*self.iconSize)) + + def setText(self, text: str) -> None: + self.textLabel.setText(text) + + def setIcon(self, path: str) -> None: + self.iconLabel.setPixmap(QIcon(getPath(path)).pixmap((self.iconSize), (self.iconSize))) + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + +class ButtonLabel(QWidget): + clicked = Signal() + def __init__(self, size=96) -> None: + super().__init__() + self.setAttribute(Qt.WA_StyledBackground) + self.setObjectName("SampleItem") + self.iconSize = size + self.setLayout(QHBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.iconLabel = QLabel() + self.iconLabel.setMinimumHeight((self.iconSize+40)) + self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.setMinimumHeight((self.iconSize)) + self.textLabel = QLabel() + self.textLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse) + self.textLabel.setWordWrap(True) + self.textLabel.setStyleSheet("font-size: 10pt;") + self.textLabel.setOpenExternalLinks(True) + self.button = QPushButton() + self.button.clicked.connect(self.clicked.emit) + self.layout().addSpacing((40/96*self.iconSize)) + self.layout().addWidget(self.iconLabel, stretch=0) + self.layout().addSpacing((20/96*self.iconSize)) + self.layout().addWidget(self.textLabel, stretch=1) + self.layout().addSpacing((20/96*self.iconSize)) + self.layout().addWidget(self.button, stretch=0) + self.layout().addSpacing((40/96*self.iconSize)) + + def setText(self, text: str) -> None: + self.textLabel.setText(text) + + def setButtonText(self, t: str) -> None: + self.button.setText(t) + + def setIcon(self, path: str) -> None: + self.iconLabel.setPixmap(QIcon(getPath(path)).pixmap((self.iconSize), (self.iconSize))) + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + +class ClickableLabel(QLabel): + clicked = Signal() + def __init__(self) -> None: + super().__init__() + self.setMouseTracking(True) + + def mousePressEvent(self, ev) -> None: + self.clicked.emit() + return super().mousePressEvent(ev) + +class ClickableButtonLabel(QPushButton): + clicked = Signal() + def __init__(self, size=96) -> None: + super().__init__() + self.setAttribute(Qt.WA_StyledBackground) + self.setObjectName("ButtonItem") + self.iconSize = size + self.setLayout(QHBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.iconLabel = QLabel() + self.iconLabel.setMinimumHeight((self.iconSize)) + self.iconLabel.setMinimumWidth(size) + self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.setMinimumHeight((self.iconSize)) + self.textLabel = QLabel() + self.textLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse) + self.textLabel.setWordWrap(True) + self.textLabel.setStyleSheet("font-size: 10pt;") + self.textLabel.setOpenExternalLinks(True) + self.layout().addSpacing((40/96*self.iconSize)) + self.layout().addWidget(self.iconLabel, stretch=0) + self.layout().addSpacing((20/96*self.iconSize)) + self.layout().addWidget(self.textLabel, stretch=1) + self.layout().addSpacing((40/96*self.iconSize)) + + def setText(self, text: str) -> None: + self.textLabel.setText(text) + + def setButtonText(self, t: str) -> None: + self.button.setText(t) + + def setIcon(self, path: str) -> None: + self.iconLabel.setPixmap(QIcon(getPath(path)).pixmap((self.iconSize), (self.iconSize), Mode=Qt.KeepAspectRatio)) + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + +class MovableFocusSelector(QLabel): + def __init__(self, parent: QWidget = None) -> None: + super().__init__(parent=parent) + self.setObjectName("FocusSelector") + + def move(self, x: int, y: int) -> None: + return super().move(x, y) + + def resize(self, w: int, h: int) -> None: + return super().resize(w+17, h+17) + +class ClickableButtonLabelWithBiggerIcon(QPushButton): + buttonClicked = Signal() + lastClick = 0 + def __init__(self, size=96) -> None: + super().__init__() + self.setAttribute(Qt.WA_StyledBackground) + self.setObjectName("ButtonItem") + self.iconSize = size + self.setCheckable(True) + self.setLayout(QHBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.iconLabel = ClickableLabel() + self.iconLabel.setMinimumHeight((self.iconSize)) + self.iconLabel.setMinimumWidth(size) + self.iconLabel.clicked.connect(self.animateClick) + self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.setMinimumHeight(int(self.iconSize*1.5)) + self.textLabel = ClickableLabel() + self.textLabel.clicked.connect(self.animateClick) + self.textLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse) + self.textLabel.setWordWrap(True) + self.textLabel.setStyleSheet("font-size: 10pt;") + self.textLabel.setOpenExternalLinks(True) + self.layout().addSpacing((20/96*self.iconSize)) + self.layout().addWidget(self.iconLabel, stretch=0) + self.layout().addSpacing((20/96*self.iconSize)) + self.layout().addWidget(self.textLabel, stretch=1) + self.layout().addSpacing((40/96*self.iconSize)) + self.clicked.connect(self.mightClick) + + def mightClick(self): + if time.time() - self.lastClick > 1: + self.lastClick = time.time() + self.buttonClicked.emit() + + def animateClick(self) -> None: + self.mightClick() + return super().animateClick() + + def setText(self, text: str) -> None: + self.textLabel.setText(text) + + def setIcon(self, path: str) -> None: + self.iconLabel.setPixmap(QIcon(getPath(path)).pixmap(QSize((self.iconSize+20), (self.iconSize+20)), mode=QIcon.Normal)) + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + +class ClickableImageWithText(QPushButton): + def __init__(self, size=96) -> None: + super().__init__() + self.setAttribute(Qt.WA_StyledBackground) + self.setObjectName("ButtonItem") + self.iconSize = size + self.setCheckable(True) + self.setLayout(QVBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.iconLabel = ClickableLabel() + self.iconLabel.setMinimumHeight(size) + self.setMinimumWidth((size) * 2) + self.iconLabel.clicked.connect(self.animateClick) + self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.setMinimumHeight((self.iconSize + 50)) + self.textLabel = ClickableLabel() + self.textLabel.clicked.connect(self.animateClick) + self.textLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse) + self.textLabel.setWordWrap(True) + self.textLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.textLabel.setStyleSheet("font-size: 10pt;") + self.textLabel.setOpenExternalLinks(True) + self.layout().addStretch() + self.layout().addWidget(self.iconLabel, stretch=0) + self.layout().addWidget(self.textLabel, stretch=1) + self.layout().addStretch() + + def setText(self, text: str) -> None: + self.textLabel.setText(text) + + def setButtonText(self, t: str) -> None: + self.button.setText(t) + + def setIcon(self, path: str) -> None: + self.iconLabel.setPixmap(QIcon(getPath(path)).pixmap(QSize((self.iconSize+20), (self.iconSize+20)), mode=QIcon.Normal)) + + def get6px(self, i: int) -> int: + return round(i*self.screen().devicePixelRatio()) + +class FirstRunSlide(BasicNavWidget): + def __init__(self, parent=None) -> None: + super().__init__(parent=parent, noNavBar = True) + widget = QWidget() + l = QHBoxLayout() + l.setContentsMargins(0, 10, 0, 10) + widget.setLayout(l) + vl = QVBoxLayout() + vl.setContentsMargins(0, 0, 0, 0) + l.addSpacing(10) + l.addLayout(vl) + vl.addSpacing(0) + + label1 = IconLabel(size=96, frame=False) + label1.setIcon("icon.png") + label1.setText(f""" +