mirror of
https://github.com/Vita3K/Vita3K.git
synced 2026-07-11 01:34:23 +02:00
gui: themes and qss refactor
# Conflicts: # .github/workflows/c-cpp.yml # .github/workflows/codeql-analysis.yml # vita3k/gui-qt/src/controls_dialog.cpp
This commit is contained in:
+3
-1
@@ -42,7 +42,9 @@ sudo apt -y install \
|
|||||||
libpipewire-0.3-dev \
|
libpipewire-0.3-dev \
|
||||||
libwayland-dev \
|
libwayland-dev \
|
||||||
libdecor-0-dev \
|
libdecor-0-dev \
|
||||||
liburing-dev
|
liburing-dev \
|
||||||
|
libgstreamer-plugins-bad1.0-0 \
|
||||||
|
libgstreamer-plugins-good1.0-0
|
||||||
|
|
||||||
if [[ "$APPIMAGE_MODE" == "with-appimage" ]]; then
|
if [[ "$APPIMAGE_MODE" == "with-appimage" ]]; then
|
||||||
curl -sLO "https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-${ARCH}.AppImage"
|
curl -sLO "https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-${ARCH}.AppImage"
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ jobs:
|
|||||||
curl:p
|
curl:p
|
||||||
zlib:p
|
zlib:p
|
||||||
qt6-base:p
|
qt6-base:p
|
||||||
|
qt6-multimedia:p
|
||||||
qt6-svg:p
|
qt6-svg:p
|
||||||
qt6-tools:p
|
qt6-tools:p
|
||||||
|
|
||||||
@@ -146,6 +147,7 @@ jobs:
|
|||||||
if: matrix.platform == 'windows-x64'
|
if: matrix.platform == 'windows-x64'
|
||||||
with:
|
with:
|
||||||
version: ${{ env.QT_VERSION }}
|
version: ${{ env.QT_VERSION }}
|
||||||
|
modules: qtmultimedia
|
||||||
host: windows
|
host: windows
|
||||||
target: desktop
|
target: desktop
|
||||||
arch: win64_msvc2022_64
|
arch: win64_msvc2022_64
|
||||||
@@ -158,6 +160,7 @@ jobs:
|
|||||||
if: matrix.platform != 'windows-arm64' && matrix.platform != 'windows-x64'
|
if: matrix.platform != 'windows-arm64' && matrix.platform != 'windows-x64'
|
||||||
with:
|
with:
|
||||||
version: ${{ env.QT_VERSION }}
|
version: ${{ env.QT_VERSION }}
|
||||||
|
modules: qtmultimedia
|
||||||
cache: true
|
cache: true
|
||||||
install-deps: ${{ matrix.qt_install_deps }}
|
install-deps: ${{ matrix.qt_install_deps }}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ jobs:
|
|||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
version: ${{ env.QT_VERSION }}
|
version: ${{ env.QT_VERSION }}
|
||||||
|
modules: qtmultimedia
|
||||||
cache: true
|
cache: true
|
||||||
install-deps: 'true'
|
install-deps: 'true'
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -4,7 +4,7 @@ add_library(vita3k_qt6 INTERFACE)
|
|||||||
|
|
||||||
set(VITA3K_QT_MIN_VER 6.11.0)
|
set(VITA3K_QT_MIN_VER 6.11.0)
|
||||||
|
|
||||||
set(VITA3K_QT_COMPONENTS Core Gui Widgets Network Concurrent Svg LinguistTools)
|
set(VITA3K_QT_COMPONENTS Core Gui Widgets Network Concurrent Svg Multimedia LinguistTools)
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
list(APPEND VITA3K_QT_COMPONENTS GuiPrivate)
|
list(APPEND VITA3K_QT_COMPONENTS GuiPrivate)
|
||||||
endif()
|
endif()
|
||||||
@@ -39,6 +39,7 @@ target_link_libraries(vita3k_qt6 INTERFACE
|
|||||||
Qt6::Network
|
Qt6::Network
|
||||||
Qt6::Concurrent
|
Qt6::Concurrent
|
||||||
Qt6::Svg
|
Qt6::Svg
|
||||||
|
Qt6::Multimedia
|
||||||
)
|
)
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND TARGET Qt6::GuiPrivate)
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND TARGET Qt6::GuiPrivate)
|
||||||
|
|||||||
+14
-3
@@ -226,6 +226,8 @@ if(NOT ANDROID)
|
|||||||
"${CMAKE_SOURCE_DIR}/i18n/qt/*.ts")
|
"${CMAKE_SOURCE_DIR}/i18n/qt/*.ts")
|
||||||
set(VITA3K_QT_QM_DIR "${CMAKE_BINARY_DIR}/qt_translations")
|
set(VITA3K_QT_QM_DIR "${CMAKE_BINARY_DIR}/qt_translations")
|
||||||
set(VITA3K_QT_QM_FILES "")
|
set(VITA3K_QT_QM_FILES "")
|
||||||
|
set(VITA3K_GUI_CONFIG_THEME_QSS "${CMAKE_CURRENT_SOURCE_DIR}/gui-qt/resources/themes/waves/theme.qss")
|
||||||
|
set(VITA3K_GUI_CONFIG_THEME_BG "${CMAKE_CURRENT_SOURCE_DIR}/gui-qt/resources/themes/waves/shell_background.svg")
|
||||||
|
|
||||||
foreach(ts_file IN LISTS VITA3K_QT_TS_FILES)
|
foreach(ts_file IN LISTS VITA3K_QT_TS_FILES)
|
||||||
get_filename_component(ts_name "${ts_file}" NAME_WE)
|
get_filename_component(ts_name "${ts_file}" NAME_WE)
|
||||||
@@ -285,9 +287,12 @@ if(APPLE)
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET vita3k
|
TARGET vita3k
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:vita3k>/../Resources/gui-configs"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/../data" "$<TARGET_FILE_DIR:vita3k>/../Resources/data"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/../data" "$<TARGET_FILE_DIR:vita3k>/../Resources/data"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${VITA3K_QT_QM_DIR}" "$<TARGET_FILE_DIR:vita3k>/../Resources/translations"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${VITA3K_QT_QM_DIR}" "$<TARGET_FILE_DIR:vita3k>/../Resources/translations"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/shaders-builtin/" "$<TARGET_FILE_DIR:vita3k>/../Resources/shaders-builtin")
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/shaders-builtin/" "$<TARGET_FILE_DIR:vita3k>/../Resources/shaders-builtin"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VITA3K_GUI_CONFIG_THEME_QSS}" "$<TARGET_FILE_DIR:vita3k>/../Resources/gui-configs/Waves.qss"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VITA3K_GUI_CONFIG_THEME_BG}" "$<TARGET_FILE_DIR:vita3k>/../Resources/gui-configs/shell_background.svg")
|
||||||
if(USE_DISCORD_RICH_PRESENCE)
|
if(USE_DISCORD_RICH_PRESENCE)
|
||||||
if(ARCHITECTURE STREQUAL "x86_64")
|
if(ARCHITECTURE STREQUAL "x86_64")
|
||||||
set(discord_dylib "${CMAKE_BINARY_DIR}/external/discord_game_sdk/lib/x86_64/discord_game_sdk.dylib")
|
set(discord_dylib "${CMAKE_BINARY_DIR}/external/discord_game_sdk/lib/x86_64/discord_game_sdk.dylib")
|
||||||
@@ -352,10 +357,13 @@ elseif(LINUX)
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET vita3k
|
TARGET vita3k
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:vita3k>/gui-configs"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/../data" "$<TARGET_FILE_DIR:vita3k>/data"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/../data" "$<TARGET_FILE_DIR:vita3k>/data"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${VITA3K_QT_QM_DIR}" "$<TARGET_FILE_DIR:vita3k>/translations"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${VITA3K_QT_QM_DIR}" "$<TARGET_FILE_DIR:vita3k>/translations"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/shaders-builtin" "$<TARGET_FILE_DIR:vita3k>/shaders-builtin"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/shaders-builtin" "$<TARGET_FILE_DIR:vita3k>/shaders-builtin"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/icons" "$<TARGET_FILE_DIR:vita3k>/icons")
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/icons" "$<TARGET_FILE_DIR:vita3k>/icons"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VITA3K_GUI_CONFIG_THEME_QSS}" "$<TARGET_FILE_DIR:vita3k>/gui-configs/Waves.qss"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VITA3K_GUI_CONFIG_THEME_BG}" "$<TARGET_FILE_DIR:vita3k>/gui-configs/shell_background.svg")
|
||||||
if(USE_DISCORD_RICH_PRESENCE)
|
if(USE_DISCORD_RICH_PRESENCE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET vita3k
|
TARGET vita3k
|
||||||
@@ -370,10 +378,13 @@ elseif(WIN32)
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET vita3k
|
TARGET vita3k
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:vita3k>/gui-configs"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/../data" "$<TARGET_FILE_DIR:vita3k>/data"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/../data" "$<TARGET_FILE_DIR:vita3k>/data"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${VITA3K_QT_QM_DIR}" "$<TARGET_FILE_DIR:vita3k>/translations"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${VITA3K_QT_QM_DIR}" "$<TARGET_FILE_DIR:vita3k>/translations"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/shaders-builtin" "$<TARGET_FILE_DIR:vita3k>/shaders-builtin"
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/shaders-builtin" "$<TARGET_FILE_DIR:vita3k>/shaders-builtin"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/icons" "$<TARGET_FILE_DIR:vita3k>/icons")
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/icons" "$<TARGET_FILE_DIR:vita3k>/icons"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VITA3K_GUI_CONFIG_THEME_QSS}" "$<TARGET_FILE_DIR:vita3k>/gui-configs/Waves.qss"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VITA3K_GUI_CONFIG_THEME_BG}" "$<TARGET_FILE_DIR:vita3k>/gui-configs/shell_background.svg")
|
||||||
if(USE_DISCORD_RICH_PRESENCE)
|
if(USE_DISCORD_RICH_PRESENCE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET vita3k
|
TARGET vita3k
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ add_library(gui-qt STATIC
|
|||||||
src/qt_utils.cpp
|
src/qt_utils.cpp
|
||||||
src/settings_dialog.cpp
|
src/settings_dialog.cpp
|
||||||
src/settings_dialog_tooltips.cpp
|
src/settings_dialog_tooltips.cpp
|
||||||
src/stylesheets.cpp
|
src/theme_manager.cpp
|
||||||
|
src/theme_surface.cpp
|
||||||
|
src/vita_theme.cpp
|
||||||
|
src/vita_themes_dialog.cpp
|
||||||
src/trophy_collection_dialog.cpp
|
src/trophy_collection_dialog.cpp
|
||||||
src/update_manager.cpp
|
src/update_manager.cpp
|
||||||
src/gui_language.cpp
|
src/gui_language.cpp
|
||||||
@@ -66,7 +69,10 @@ add_library(gui-qt STATIC
|
|||||||
include/gui-qt/qt_utils.h
|
include/gui-qt/qt_utils.h
|
||||||
include/gui-qt/settings_dialog.h
|
include/gui-qt/settings_dialog.h
|
||||||
include/gui-qt/settings_dialog_tooltips.h
|
include/gui-qt/settings_dialog_tooltips.h
|
||||||
include/gui-qt/stylesheets.h
|
include/gui-qt/theme_manager.h
|
||||||
|
include/gui-qt/theme_surface.h
|
||||||
|
include/gui-qt/vita_theme.h
|
||||||
|
include/gui-qt/vita_themes_dialog.h
|
||||||
include/gui-qt/trophy_collection_dialog.h
|
include/gui-qt/trophy_collection_dialog.h
|
||||||
include/gui-qt/update_manager.h
|
include/gui-qt/update_manager.h
|
||||||
include/gui-qt/gui_language.h
|
include/gui-qt/gui_language.h
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
bool handle_sdl_event(const SDL_Event &event);
|
bool handle_sdl_event(const SDL_Event &event);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void changeEvent(QEvent *event) override;
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void done(int result) override;
|
void done(int result) override;
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
@@ -216,6 +217,7 @@ private:
|
|||||||
|
|
||||||
void refresh_controller_tabs();
|
void refresh_controller_tabs();
|
||||||
void refresh_controller_page_labels(int index);
|
void refresh_controller_page_labels(int index);
|
||||||
|
void refresh_controller_art();
|
||||||
void set_controller_page_enabled(int index, bool enabled);
|
void set_controller_page_enabled(int index, bool enabled);
|
||||||
void set_controller_page_view(int index, bool show_bindings);
|
void set_controller_page_view(int index, bool show_bindings);
|
||||||
void start_controller_capture(int tab_index, int binding_index);
|
void start_controller_capture(int tab_index, int binding_index);
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ const QString Settings = QStringLiteral("CurrentSettings");
|
|||||||
|
|
||||||
const QString LightStylesheet = QStringLiteral("light");
|
const QString LightStylesheet = QStringLiteral("light");
|
||||||
const QString DarkStylesheet = QStringLiteral("dark");
|
const QString DarkStylesheet = QStringLiteral("dark");
|
||||||
const QString NoStylesheet = QStringLiteral("none");
|
|
||||||
|
|
||||||
const QString main_window = QStringLiteral("MainWindow");
|
const QString main_window = QStringLiteral("MainWindow");
|
||||||
const QString apps_list = QStringLiteral("GameList");
|
const QString apps_list = QStringLiteral("GameList");
|
||||||
@@ -38,6 +37,7 @@ const QString trophy = QStringLiteral("Trophy");
|
|||||||
const QString controls = QStringLiteral("Controls");
|
const QString controls = QStringLiteral("Controls");
|
||||||
const QString settings = QStringLiteral("Settings");
|
const QString settings = QStringLiteral("Settings");
|
||||||
const QString user_mgmt = QStringLiteral("UserManagement");
|
const QString user_mgmt = QStringLiteral("UserManagement");
|
||||||
|
const QString vita_themes = QStringLiteral("VitaThemes");
|
||||||
const QString meta = QStringLiteral("Meta");
|
const QString meta = QStringLiteral("Meta");
|
||||||
|
|
||||||
const GuiSave m_currentStylesheet = GuiSave(meta, QStringLiteral("currentStylesheet"), DarkStylesheet);
|
const GuiSave m_currentStylesheet = GuiSave(meta, QStringLiteral("currentStylesheet"), DarkStylesheet);
|
||||||
@@ -86,6 +86,11 @@ const GuiSave sd_lastTab = GuiSave(settings, QStringLiteral("lastTab"), 0);
|
|||||||
|
|
||||||
const GuiSave um_geometry = GuiSave(user_mgmt, QStringLiteral("geometry"), QByteArray());
|
const GuiSave um_geometry = GuiSave(user_mgmt, QStringLiteral("geometry"), QByteArray());
|
||||||
|
|
||||||
|
const GuiSave vt_geometry = GuiSave(vita_themes, QStringLiteral("geometry"), QByteArray());
|
||||||
|
const GuiSave vt_appliedSelection = GuiSave(vita_themes, QStringLiteral("appliedSelection"), QString());
|
||||||
|
const GuiSave vt_bgmEnabled = GuiSave(vita_themes, QStringLiteral("bgmEnabled"), false);
|
||||||
|
const GuiSave vt_bgmVolume = GuiSave(vita_themes, QStringLiteral("bgmVolume"), 50);
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
|
|
||||||
class GuiSettings : public GuiSettingsBase {
|
class GuiSettings : public GuiSettingsBase {
|
||||||
@@ -93,6 +98,4 @@ class GuiSettings : public GuiSettingsBase {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GuiSettings(const QString &settings_dir, QObject *parent = nullptr);
|
explicit GuiSettings(const QString &settings_dir, QObject *parent = nullptr);
|
||||||
|
|
||||||
QStringList get_stylesheet_entries() const;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,11 +24,13 @@
|
|||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
|
#include <QPointer>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
|
#include <QTimer>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include <deque>
|
class ThemeSurface;
|
||||||
|
|
||||||
class LogWidget : public custom_dock_widget {
|
class LogWidget : public custom_dock_widget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -51,8 +53,6 @@ private:
|
|||||||
void on_log_message(const QString &msg, int level);
|
void on_log_message(const QString &msg, int level);
|
||||||
void drain_pending_messages();
|
void drain_pending_messages();
|
||||||
void append_log_entry(const QString &msg, int level, bool preserve_scroll = true);
|
void append_log_entry(const QString &msg, int level, bool preserve_scroll = true);
|
||||||
QColor color_for_level(int level) const;
|
|
||||||
void trim_entries();
|
|
||||||
void show_search_bar();
|
void show_search_bar();
|
||||||
void hide_search_bar();
|
void hide_search_bar();
|
||||||
void find_text(bool backwards = false);
|
void find_text(bool backwards = false);
|
||||||
@@ -63,11 +63,11 @@ private:
|
|||||||
int effective_buffer_size(int requested_size) const;
|
int effective_buffer_size(int requested_size) const;
|
||||||
|
|
||||||
QPlainTextEdit *m_log;
|
QPlainTextEdit *m_log;
|
||||||
QWidget *m_search_container = nullptr;
|
QPointer<ThemeSurface> m_search_container;
|
||||||
QLineEdit *m_search_edit = nullptr;
|
QLineEdit *m_search_edit = nullptr;
|
||||||
QToolButton *m_search_prev_button = nullptr;
|
QToolButton *m_search_prev_button = nullptr;
|
||||||
QToolButton *m_search_next_button = nullptr;
|
QToolButton *m_search_next_button = nullptr;
|
||||||
QToolButton *m_search_close_button = nullptr;
|
QToolButton *m_search_close_button = nullptr;
|
||||||
QTextCursor m_current_search_cursor;
|
QTextCursor m_current_search_cursor;
|
||||||
std::deque<std::pair<QString, int>> m_entries;
|
QTimer *m_drain_timer = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <emuenv/state.h>
|
#include <emuenv/state.h>
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
#include <QPixmap>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
@@ -43,9 +44,11 @@ class GameCompatibility;
|
|||||||
class QWindow;
|
class QWindow;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
|
class QPaintEvent;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QSlider;
|
class QSlider;
|
||||||
class QToolBar;
|
class QToolBar;
|
||||||
|
class QVariantAnimation;
|
||||||
class TrophyCollectionDialog;
|
class TrophyCollectionDialog;
|
||||||
class CtrlKeyboardFilter;
|
class CtrlKeyboardFilter;
|
||||||
class DebugWidget;
|
class DebugWidget;
|
||||||
@@ -54,6 +57,8 @@ class UserManagementDialog;
|
|||||||
class UpdateManager;
|
class UpdateManager;
|
||||||
class ControlsDialog;
|
class ControlsDialog;
|
||||||
class SettingsDialog;
|
class SettingsDialog;
|
||||||
|
class ThemeManager;
|
||||||
|
class VitaThemesDialog;
|
||||||
namespace app {
|
namespace app {
|
||||||
struct SettingsCommitResult;
|
struct SettingsCommitResult;
|
||||||
}
|
}
|
||||||
@@ -77,6 +82,7 @@ protected:
|
|||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||||
void dropEvent(QDropEvent *event) override;
|
void dropEvent(QDropEvent *event) override;
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_about_action_triggered();
|
void on_about_action_triggered();
|
||||||
@@ -91,6 +97,7 @@ private slots:
|
|||||||
void pump_sdl_events();
|
void pump_sdl_events();
|
||||||
void open_trophy_collection();
|
void open_trophy_collection();
|
||||||
void open_user_management();
|
void open_user_management();
|
||||||
|
void open_vita_themes();
|
||||||
void show_live_area(const std::string &title_id);
|
void show_live_area(const std::string &title_id);
|
||||||
void on_live_area_play();
|
void on_live_area_play();
|
||||||
void on_live_area_closed();
|
void on_live_area_closed();
|
||||||
@@ -155,15 +162,19 @@ private:
|
|||||||
void open_debug_widget(int tab);
|
void open_debug_widget(int tab);
|
||||||
void apply_log_gui_settings();
|
void apply_log_gui_settings();
|
||||||
void repaint_gui();
|
void repaint_gui();
|
||||||
void init_default_style();
|
|
||||||
void init_first_run_stylesheet();
|
void init_first_run_stylesheet();
|
||||||
void apply_stylesheet();
|
void apply_stylesheet();
|
||||||
|
void apply_theme_background_presentation();
|
||||||
|
void clear_theme_background_presentation();
|
||||||
|
void rescale_theme_background_pixmaps();
|
||||||
|
void advance_theme_background_presentation();
|
||||||
|
|
||||||
EmuEnvState &emuenv;
|
EmuEnvState &emuenv;
|
||||||
app::AppSessionController m_app_session;
|
app::AppSessionController m_app_session;
|
||||||
std::unique_ptr<Ui::MainWindow> m_ui;
|
std::unique_ptr<Ui::MainWindow> m_ui;
|
||||||
std::shared_ptr<GuiSettings> m_gui_settings;
|
std::shared_ptr<GuiSettings> m_gui_settings;
|
||||||
std::shared_ptr<PersistentSettings> m_persistent_settings;
|
std::shared_ptr<PersistentSettings> m_persistent_settings;
|
||||||
|
std::unique_ptr<ThemeManager> m_theme_manager;
|
||||||
|
|
||||||
QMainWindow *m_dock_host = nullptr;
|
QMainWindow *m_dock_host = nullptr;
|
||||||
AppsList *m_apps_list_widget = nullptr;
|
AppsList *m_apps_list_widget = nullptr;
|
||||||
@@ -173,25 +184,34 @@ private:
|
|||||||
QWindow *m_game_container = nullptr;
|
QWindow *m_game_container = nullptr;
|
||||||
QTimer *m_sdl_pump_timer = nullptr;
|
QTimer *m_sdl_pump_timer = nullptr;
|
||||||
|
|
||||||
TrophyCollectionDialog *m_trophy_dialog = nullptr;
|
QPointer<TrophyCollectionDialog> m_trophy_dialog;
|
||||||
UserManagementDialog *m_user_mgmt_dialog = nullptr;
|
QPointer<UserManagementDialog> m_user_mgmt_dialog;
|
||||||
CtrlKeyboardFilter *m_kb_filter = nullptr;
|
CtrlKeyboardFilter *m_kb_filter = nullptr;
|
||||||
DebugWidget *m_debug_widget = nullptr;
|
QPointer<DebugWidget> m_debug_widget;
|
||||||
LiveAreaWidget *m_live_area_widget = nullptr;
|
LiveAreaWidget *m_live_area_widget = nullptr;
|
||||||
UpdateManager *m_update_manager = nullptr;
|
UpdateManager *m_update_manager = nullptr;
|
||||||
ControlsDialog *m_controls_dialog = nullptr;
|
QPointer<ControlsDialog> m_controls_dialog;
|
||||||
SettingsDialog *m_settings_dialog = nullptr;
|
QPointer<SettingsDialog> m_settings_dialog;
|
||||||
|
QPointer<VitaThemesDialog> m_vita_themes_dialog;
|
||||||
std::string m_live_area_title_id;
|
std::string m_live_area_title_id;
|
||||||
|
|
||||||
QLineEdit *m_search_bar = nullptr;
|
QLineEdit *m_search_bar = nullptr;
|
||||||
QSlider *m_icon_size_slider = nullptr;
|
QSlider *m_icon_size_slider = nullptr;
|
||||||
QWidget *m_slider_container = nullptr;
|
QWidget *m_slider_container = nullptr;
|
||||||
std::vector<QPointer<QWidget>> m_auxiliary_windows;
|
std::vector<QPointer<QWidget>> m_auxiliary_windows;
|
||||||
|
QTimer *m_theme_background_cycle_timer = nullptr;
|
||||||
|
QVariantAnimation *m_theme_background_fade_animation = nullptr;
|
||||||
|
std::vector<QPixmap> m_theme_background_original_pixmaps;
|
||||||
|
std::vector<QPixmap> m_theme_background_scaled_pixmaps;
|
||||||
|
QSize m_theme_background_scaled_size;
|
||||||
|
int m_theme_background_cycle_interval_ms = 15000;
|
||||||
|
int m_theme_background_current_index = 0;
|
||||||
|
int m_theme_background_next_index = -1;
|
||||||
|
qreal m_theme_background_fade_progress = 0.0;
|
||||||
bool m_is_app_closing = false;
|
bool m_is_app_closing = false;
|
||||||
bool m_fullscreen = false;
|
bool m_fullscreen = false;
|
||||||
bool m_app_selected = false;
|
bool m_app_selected = false;
|
||||||
bool m_admin_privileged = false;
|
bool m_admin_privileged = false;
|
||||||
QString m_default_style;
|
|
||||||
#if USE_DISCORD
|
#if USE_DISCORD
|
||||||
bool m_discord_rich_presence_old = false;
|
bool m_discord_rich_presence_old = false;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -50,8 +50,18 @@ void open_dir(const fs::path &path);
|
|||||||
|
|
||||||
fs::path to_fs_path(const QString &path);
|
fs::path to_fs_path(const QString &path);
|
||||||
QString to_qt_path(const fs::path &path);
|
QString to_qt_path(const fs::path &path);
|
||||||
|
QString sanitize_relative_path_reference(const QString &path);
|
||||||
|
QString resolve_relative_path_in_root(const QString &relative_path, const QString &root_path);
|
||||||
|
|
||||||
QColor get_foreground_color(QWidget *widget = nullptr);
|
QColor foreground_color(QWidget *widget = nullptr);
|
||||||
|
QColor muted_text_color();
|
||||||
|
QColor toolbar_icon_tint();
|
||||||
|
QColor theme_role_color(
|
||||||
|
const QString &role,
|
||||||
|
QPalette::ColorRole color_role = QPalette::WindowText);
|
||||||
|
QString format_help_html(const QString &title, const QString &body);
|
||||||
|
void invalidate_theme_cache();
|
||||||
|
void refresh_theme_state(QWidget *widget);
|
||||||
|
|
||||||
QIcon get_colorized_icon(const QIcon &icon,
|
QIcon get_colorized_icon(const QIcon &icon,
|
||||||
const QColor &source_color,
|
const QColor &source_color,
|
||||||
@@ -59,16 +69,6 @@ QIcon get_colorized_icon(const QIcon &icon,
|
|||||||
|
|
||||||
bool dark_mode_active();
|
bool dark_mode_active();
|
||||||
|
|
||||||
QColor get_label_color(
|
|
||||||
const QString &object_name,
|
|
||||||
const QColor &fallback_light,
|
|
||||||
const QColor &fallback_dark,
|
|
||||||
QPalette::ColorRole color_role = QPalette::WindowText);
|
|
||||||
|
|
||||||
QColor get_label_color(
|
|
||||||
const QColor &fallback_light,
|
|
||||||
const QColor &fallback_dark);
|
|
||||||
|
|
||||||
MessageDialogResult show_message_box(
|
MessageDialogResult show_message_box(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
QMessageBox::Icon icon,
|
QMessageBox::Icon icon,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
struct EmuEnvState;
|
struct EmuEnvState;
|
||||||
class GuiSettings;
|
class GuiSettings;
|
||||||
class SettingsDialogTooltips;
|
class SettingsDialogTooltips;
|
||||||
|
class ThemeManager;
|
||||||
|
|
||||||
enum class SettingsTab : int {
|
enum class SettingsTab : int {
|
||||||
Core = 0,
|
Core = 0,
|
||||||
@@ -54,6 +55,7 @@ class SettingsDialog final : public QDialog {
|
|||||||
public:
|
public:
|
||||||
explicit SettingsDialog(EmuEnvState &emuenv,
|
explicit SettingsDialog(EmuEnvState &emuenv,
|
||||||
std::shared_ptr<GuiSettings> gui_settings,
|
std::shared_ptr<GuiSettings> gui_settings,
|
||||||
|
ThemeManager &theme_manager,
|
||||||
const std::string &app_path = {},
|
const std::string &app_path = {},
|
||||||
int initial_tab = 0);
|
int initial_tab = 0);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
@@ -69,6 +71,7 @@ public:
|
|||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void gui_stylesheet_request();
|
void gui_stylesheet_request();
|
||||||
|
void gui_theme_music_settings_request();
|
||||||
void gui_log_settings_request();
|
void gui_log_settings_request();
|
||||||
void storage_path_changed();
|
void storage_path_changed();
|
||||||
void ui_language_request(const QString &locale_tag);
|
void ui_language_request(const QString &locale_tag);
|
||||||
@@ -78,6 +81,7 @@ private slots:
|
|||||||
void on_save_clicked();
|
void on_save_clicked();
|
||||||
void on_apply_clicked();
|
void on_apply_clicked();
|
||||||
void on_close_clicked();
|
void on_close_clicked();
|
||||||
|
void refresh_themes();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool apply_pending_storage_path();
|
bool apply_pending_storage_path();
|
||||||
@@ -103,7 +107,7 @@ private:
|
|||||||
void mark_dirty();
|
void mark_dirty();
|
||||||
void set_pending_pref_path(const fs::path &pref_path);
|
void set_pending_pref_path(const fs::path &pref_path);
|
||||||
void set_description(QWidget *tab, const QString &title, const QString &text);
|
void set_description(QWidget *tab, const QString &title, const QString &text);
|
||||||
void add_stylesheets();
|
void add_stylesheets(const QString &preferred_name = {});
|
||||||
void apply_stylesheet(bool reset = false);
|
void apply_stylesheet(bool reset = false);
|
||||||
|
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
@@ -114,6 +118,7 @@ private:
|
|||||||
|
|
||||||
EmuEnvState &emuenv;
|
EmuEnvState &emuenv;
|
||||||
std::shared_ptr<GuiSettings> m_gui_settings;
|
std::shared_ptr<GuiSettings> m_gui_settings;
|
||||||
|
ThemeManager &m_theme_manager;
|
||||||
std::unique_ptr<SettingsDialogTooltips> m_tooltips;
|
std::unique_ptr<SettingsDialogTooltips> m_tooltips;
|
||||||
std::string m_app_path;
|
std::string m_app_path;
|
||||||
int m_initial_tab;
|
int m_initial_tab;
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public:
|
|||||||
QString memory_mapping;
|
QString memory_mapping;
|
||||||
QString audio_backend;
|
QString audio_backend;
|
||||||
QString audio_volume;
|
QString audio_volume;
|
||||||
|
QString theme_music_enable;
|
||||||
|
QString theme_music_volume;
|
||||||
QString ngs_enable;
|
QString ngs_enable;
|
||||||
QString front_camera;
|
QString front_camera;
|
||||||
QString back_camera;
|
QString back_camera;
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui-qt/vita_theme.h>
|
||||||
|
|
||||||
|
#include <util/fs.h>
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class GuiSettings;
|
||||||
|
class QAudioOutput;
|
||||||
|
class QMediaPlayer;
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
struct ThemeEntry {
|
||||||
|
QString name;
|
||||||
|
QString display_name;
|
||||||
|
QString qss_source;
|
||||||
|
QString base_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VitaThemeSelection {
|
||||||
|
QString theme_id;
|
||||||
|
QString background_id;
|
||||||
|
int readability = 55;
|
||||||
|
bool normalize_font_colors = true;
|
||||||
|
bool cycle_enabled = false;
|
||||||
|
int cycle_interval_seconds = 15;
|
||||||
|
QStringList cycle_background_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
VitaThemeSelection normalize_vita_theme_selection(
|
||||||
|
const VitaThemeInfo &theme,
|
||||||
|
VitaThemeSelection selection);
|
||||||
|
|
||||||
|
struct ThemeBackgroundPresentation {
|
||||||
|
QStringList image_paths;
|
||||||
|
int interval_ms = 0;
|
||||||
|
bool animated = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppliedThemeState {
|
||||||
|
bool is_vita_theme = false;
|
||||||
|
ThemeBackgroundPresentation background;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
|
||||||
|
class ThemeManager final : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ThemeManager(std::shared_ptr<GuiSettings> gui_settings,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QList<gui::ThemeEntry> available_themes() const;
|
||||||
|
bool apply_theme(const QString &name = {}, bool force_reload = false);
|
||||||
|
void set_pref_path(const fs::path &pref_path);
|
||||||
|
void set_vita_themes_root(const fs::path &themes_root);
|
||||||
|
void refresh_vita_theme_catalog();
|
||||||
|
const std::vector<gui::VitaThemeInfo> &installed_vita_themes(bool force_reload = false) const;
|
||||||
|
void ensure_generated_theme_ready(const fs::path &themes_root);
|
||||||
|
std::optional<gui::VitaThemeSelection> applied_vita_theme_selection() const;
|
||||||
|
bool apply_vita_theme_selection(
|
||||||
|
const gui::VitaThemeSelection &selection,
|
||||||
|
bool force_reload = true);
|
||||||
|
std::optional<gui::AppliedThemeState> current_applied_theme_state() const;
|
||||||
|
void clear_applied_vita_theme();
|
||||||
|
void refresh_vita_theme_bgm();
|
||||||
|
void set_vita_theme_bgm_blocked(bool blocked);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void theme_state_changed();
|
||||||
|
void theme_catalog_changed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void save_applied_selection(const gui::VitaThemeSelection &selection) const;
|
||||||
|
void switch_to_fallback_theme(const QString &reason);
|
||||||
|
QString requested_theme_name() const;
|
||||||
|
const gui::VitaThemeInfo *find_installed_vita_theme(const QString &theme_id, bool force_reload = false) const;
|
||||||
|
std::optional<gui::ThemeEntry> builtin_theme(const QString &name) const;
|
||||||
|
std::optional<gui::ThemeEntry> generated_vita_theme() const;
|
||||||
|
QList<gui::ThemeEntry> custom_themes() const;
|
||||||
|
std::optional<gui::ThemeEntry> resolve_theme(const QString &name) const;
|
||||||
|
std::optional<gui::AppliedThemeState> build_applied_theme_state(const gui::ThemeEntry &theme, const QString &stylesheet) const;
|
||||||
|
bool apply_theme_entry(const gui::ThemeEntry &theme, bool force_reload);
|
||||||
|
bool apply_theme_entry(const gui::ThemeEntry &theme, const QString &stylesheet, bool force_reload);
|
||||||
|
std::optional<QString> load_stylesheet(const gui::ThemeEntry &theme) const;
|
||||||
|
std::optional<QPalette> parse_palette(const QString &stylesheet) const;
|
||||||
|
QString resolve_stylesheet_urls(const QString &stylesheet, const gui::ThemeEntry &theme) const;
|
||||||
|
QString resolve_stylesheet_url(const QString &url, const gui::ThemeEntry &theme) const;
|
||||||
|
fs::path generated_vita_theme_path(const QString &theme_id) const;
|
||||||
|
QString generated_vita_theme_display_name(const QString &theme_id) const;
|
||||||
|
void ensure_fusion_style() const;
|
||||||
|
void stop_vita_theme_bgm();
|
||||||
|
|
||||||
|
std::shared_ptr<GuiSettings> m_gui_settings;
|
||||||
|
QString m_current_theme_name;
|
||||||
|
fs::path m_pref_path;
|
||||||
|
fs::path m_vita_themes_root;
|
||||||
|
QAudioOutput *m_vita_theme_bgm_audio_output = nullptr;
|
||||||
|
QMediaPlayer *m_vita_theme_bgm_player = nullptr;
|
||||||
|
QString m_current_vita_theme_bgm_source_path;
|
||||||
|
std::optional<gui::AppliedThemeState> m_applied_theme_state;
|
||||||
|
mutable std::vector<gui::VitaThemeInfo> m_installed_vita_theme_cache;
|
||||||
|
mutable bool m_vita_theme_cache_loaded = false;
|
||||||
|
bool m_vita_theme_bgm_blocked = false;
|
||||||
|
};
|
||||||
+7
-6
@@ -17,11 +17,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace gui::stylesheets {
|
class ThemeSurface : public QWidget {
|
||||||
|
public:
|
||||||
|
explicit ThemeSurface(QWidget *parent = nullptr);
|
||||||
|
|
||||||
extern const QString light_style_sheet;
|
protected:
|
||||||
extern const QString dark_style_sheet;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
};
|
||||||
} // namespace gui::stylesheets
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <util/fs.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ctime>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
enum class VitaThemeBackgroundSource {
|
||||||
|
Home,
|
||||||
|
Lockscreen,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VitaThemeColor {
|
||||||
|
std::uint8_t red = 0;
|
||||||
|
std::uint8_t green = 0;
|
||||||
|
std::uint8_t blue = 0;
|
||||||
|
std::uint8_t alpha = 255;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VitaThemeBackgroundOption {
|
||||||
|
std::string id;
|
||||||
|
std::string label;
|
||||||
|
fs::path asset_path;
|
||||||
|
VitaThemeBackgroundSource source = VitaThemeBackgroundSource::Home;
|
||||||
|
std::optional<VitaThemeColor> primary_text_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VitaThemePalette {
|
||||||
|
std::optional<VitaThemeColor> information_bar_color;
|
||||||
|
std::optional<VitaThemeColor> information_indicator_color;
|
||||||
|
std::optional<VitaThemeColor> notify_background_color;
|
||||||
|
std::optional<VitaThemeColor> notify_border_color;
|
||||||
|
std::optional<VitaThemeColor> notify_font_color;
|
||||||
|
std::optional<VitaThemeColor> date_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VitaThemeInfo {
|
||||||
|
std::string theme_id;
|
||||||
|
std::string content_id;
|
||||||
|
std::string title;
|
||||||
|
std::string provider;
|
||||||
|
std::string content_version;
|
||||||
|
fs::path installed_path;
|
||||||
|
std::optional<fs::path> bgm_path;
|
||||||
|
fs::path package_thumbnail_path;
|
||||||
|
std::uintmax_t installed_size = 0;
|
||||||
|
std::time_t updated_time = 0;
|
||||||
|
std::vector<VitaThemeBackgroundOption> background_options;
|
||||||
|
VitaThemePalette palette;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<VitaThemeInfo> load_vita_theme(
|
||||||
|
const fs::path &theme_dir,
|
||||||
|
const std::string &preferred_locale = {});
|
||||||
|
std::vector<VitaThemeInfo> enumerate_installed_vita_themes(
|
||||||
|
const fs::path &themes_root,
|
||||||
|
const std::string &preferred_locale = {});
|
||||||
|
|
||||||
|
std::optional<fs::path> synthesize_vita_theme_qss(
|
||||||
|
const VitaThemeInfo &theme,
|
||||||
|
const std::string &background_option_id,
|
||||||
|
int readability_percent,
|
||||||
|
bool normalize_font_colors,
|
||||||
|
const std::vector<fs::path> &background_image_paths,
|
||||||
|
int cycle_interval_seconds,
|
||||||
|
bool cycle_enabled,
|
||||||
|
const fs::path &pref_path,
|
||||||
|
const fs::path &gui_settings_dir);
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui-qt/theme_manager.h>
|
||||||
|
#include <gui-qt/vita_theme.h>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class GuiSettings;
|
||||||
|
class ThemeManager;
|
||||||
|
class QListWidget;
|
||||||
|
class QLabel;
|
||||||
|
class QPushButton;
|
||||||
|
class QWidget;
|
||||||
|
class QHBoxLayout;
|
||||||
|
class QVBoxLayout;
|
||||||
|
class QSlider;
|
||||||
|
class QCheckBox;
|
||||||
|
class QSpinBox;
|
||||||
|
class QCloseEvent;
|
||||||
|
class QResizeEvent;
|
||||||
|
class QEvent;
|
||||||
|
class QButtonGroup;
|
||||||
|
class QRadioButton;
|
||||||
|
class QTextBrowser;
|
||||||
|
class QTreeWidget;
|
||||||
|
class QTreeWidgetItem;
|
||||||
|
|
||||||
|
class VitaThemesDialog final : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VitaThemesDialog(
|
||||||
|
std::shared_ptr<GuiSettings> gui_settings,
|
||||||
|
ThemeManager &theme_manager,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void reload();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct BackgroundRow {
|
||||||
|
QString id;
|
||||||
|
QTreeWidgetItem *item = nullptr;
|
||||||
|
QRadioButton *primary_radio = nullptr;
|
||||||
|
QCheckBox *cycle_checkbox = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void build_ui();
|
||||||
|
void restore_persistent_state();
|
||||||
|
void save_persistent_state() const;
|
||||||
|
void repopulate_theme_list(const QString &preferred_theme_id = {});
|
||||||
|
void apply_theme_selection(int row);
|
||||||
|
void clear_selection_state();
|
||||||
|
gui::VitaThemeSelection selection_from_ui() const;
|
||||||
|
void load_selection_into_ui(
|
||||||
|
gui::VitaThemeSelection selection,
|
||||||
|
std::optional<QString> preview_background_id = std::nullopt,
|
||||||
|
bool follow_primary_preview = false,
|
||||||
|
bool reload_theme_ui = false);
|
||||||
|
void load_theme_into_ui(const gui::VitaThemeInfo *theme);
|
||||||
|
void sync_background_rows();
|
||||||
|
void sync_preview();
|
||||||
|
void sync_metadata();
|
||||||
|
void sync_actions();
|
||||||
|
void sync_cycle_controls();
|
||||||
|
void set_selected_background(const QString &background_id);
|
||||||
|
void set_selected_cycle_backgrounds(const QStringList &background_ids);
|
||||||
|
void update_preview_pixmap();
|
||||||
|
void update_package_thumbnail();
|
||||||
|
void update_readability_label();
|
||||||
|
void apply_selected_theme();
|
||||||
|
void delete_selected_theme();
|
||||||
|
void open_selected_theme_folder() const;
|
||||||
|
void register_description(QWidget *widget, const QString &title, const QString &description);
|
||||||
|
void set_description(const QString &title, const QString &text);
|
||||||
|
void reset_description();
|
||||||
|
QString default_description() const;
|
||||||
|
void set_preview_background(const QString &background_id);
|
||||||
|
QString resolve_preview_background_id(
|
||||||
|
const gui::VitaThemeInfo &theme,
|
||||||
|
const QString &background_id) const;
|
||||||
|
|
||||||
|
const gui::VitaThemeInfo *current_theme() const;
|
||||||
|
const gui::VitaThemeBackgroundOption *current_background() const;
|
||||||
|
const gui::VitaThemeBackgroundOption *current_preview_background() const;
|
||||||
|
|
||||||
|
std::shared_ptr<GuiSettings> m_gui_settings;
|
||||||
|
ThemeManager &m_theme_manager;
|
||||||
|
std::vector<gui::VitaThemeInfo> m_themes;
|
||||||
|
|
||||||
|
QListWidget *m_theme_list = nullptr;
|
||||||
|
QLabel *m_package_thumbnail = nullptr;
|
||||||
|
QLabel *m_theme_title = nullptr;
|
||||||
|
QLabel *m_theme_provider = nullptr;
|
||||||
|
QLabel *m_preview = nullptr;
|
||||||
|
QTreeWidget *m_background_list = nullptr;
|
||||||
|
QCheckBox *m_cycle_checkbox = nullptr;
|
||||||
|
QSpinBox *m_cycle_interval_spinbox = nullptr;
|
||||||
|
QLabel *m_cycle_summary_value = nullptr;
|
||||||
|
QLabel *m_version_value = nullptr;
|
||||||
|
QLabel *m_theme_id_value = nullptr;
|
||||||
|
QLabel *m_size_value = nullptr;
|
||||||
|
QLabel *m_updated_value = nullptr;
|
||||||
|
QLabel *m_background_value = nullptr;
|
||||||
|
QLabel *m_cycle_value = nullptr;
|
||||||
|
QSlider *m_readability_slider = nullptr;
|
||||||
|
QLabel *m_readability_value = nullptr;
|
||||||
|
QCheckBox *m_normalize_fonts_checkbox = nullptr;
|
||||||
|
QPushButton *m_refresh_button = nullptr;
|
||||||
|
QPushButton *m_apply_button = nullptr;
|
||||||
|
QPushButton *m_delete_button = nullptr;
|
||||||
|
QPushButton *m_open_folder_button = nullptr;
|
||||||
|
QTextBrowser *m_help_text = nullptr;
|
||||||
|
QButtonGroup *m_primary_background_group = nullptr;
|
||||||
|
QString m_loaded_theme_id;
|
||||||
|
std::vector<BackgroundRow> m_background_rows;
|
||||||
|
|
||||||
|
QString m_selected_background_id;
|
||||||
|
QString m_preview_background_id;
|
||||||
|
QString m_loaded_preview_background_id;
|
||||||
|
QStringList m_selected_cycle_background_ids;
|
||||||
|
QPixmap m_preview_pixmap;
|
||||||
|
};
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
/*!PALETTE
|
||||||
|
* dark: dark
|
||||||
|
* window: #2d2d30
|
||||||
|
* window-text: #f3f3f3
|
||||||
|
* base: #1e1e1e
|
||||||
|
* alternate-base: #262629
|
||||||
|
* tooltip-base: #2d2d30
|
||||||
|
* tooltip-text: #f3f3f3
|
||||||
|
* text: #f3f3f3
|
||||||
|
* button: #2d2d30
|
||||||
|
* button-text: #f3f3f3
|
||||||
|
* bright-text: #ffffff
|
||||||
|
* link: #429cff
|
||||||
|
* highlight: #21ffffff
|
||||||
|
* inactive-highlight: #21ffffff
|
||||||
|
* disabled-highlight: #14ffffff
|
||||||
|
* highlight-text: #ffffff
|
||||||
|
* inactive-highlight-text: #ffffff
|
||||||
|
* disabled-highlight-text: #c8c8c8
|
||||||
|
* placeholder-text: #a0a0a0
|
||||||
|
* disabled-button-text: #737373
|
||||||
|
* disabled-window-text: #737373
|
||||||
|
* disabled-text: #737373
|
||||||
|
*/
|
||||||
|
QWidget { color: #e0e0e0; background-color: #353535; }
|
||||||
|
QWidget:disabled { color: #777777; }
|
||||||
|
QMainWindow { background-color: #353535; }
|
||||||
|
QMainWindow#main_window { background-color: #353535; }
|
||||||
|
QWidget#mw_frame { background-color: #353535; }
|
||||||
|
QMainWindow#mw_dock_host { background: transparent; }
|
||||||
|
QDialog { background-color: #353535; }
|
||||||
|
QMenuBar { background-color: #353535; color: #e0e0e0; }
|
||||||
|
QMenuBar::item { background: transparent; padding: 4px 8px; }
|
||||||
|
QMenuBar::item:selected { background: #4a4a4a; }
|
||||||
|
QMenu { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; }
|
||||||
|
QMenu::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }
|
||||||
|
QMenu::item:disabled { color: #808080; }
|
||||||
|
QMenu::separator { height: 1px; background: #555555; margin: 4px 8px; }
|
||||||
|
QToolBar#mw_toolbar { background-color: #353535; border: none; border-top: 1px solid #555555; }
|
||||||
|
QToolBar#mw_toolbar::separator { background-color: rgba(100, 100, 100, 235); width: 0.125em; margin-top: 0.250em; margin-bottom: 0.250em; }
|
||||||
|
QToolButton { color: #e0e0e0; background: transparent; border: 1px solid transparent; padding: 3px; }
|
||||||
|
QToolButton:hover { background-color: #4a4a4a; border: 1px solid #666666; }
|
||||||
|
QToolButton:pressed { background-color: rgba(255, 255, 255, 0.12); }
|
||||||
|
QToolButton:disabled { color: #808080; }
|
||||||
|
QToolButton#welcome_firmware_button { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 4px 32px 4px 12px; text-align: left; }
|
||||||
|
QToolButton#welcome_firmware_button:hover { background-color: #4a4a4a; border-color: #666666; }
|
||||||
|
QToolButton#welcome_firmware_button:pressed { background-color: rgba(255, 255, 255, 0.12); }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border-left: 1px solid #555555; border-top-right-radius: 2px; border-bottom-right-radius: 2px; background: #303030; }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button:hover { background: #444444; border-left-color: #666666; }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button:pressed { background: rgba(255, 255, 255, 0.12); }
|
||||||
|
QLabel[themeRole="toolbarIconTint"] { color: #f3f3f3; }
|
||||||
|
QLabel[themeRole="mutedText"] { color: #a8a8a8; }
|
||||||
|
QPushButton { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 4px 16px; min-height: 1.2em; }
|
||||||
|
QPushButton:hover { background-color: #4a4a4a; border-color: #666666; }
|
||||||
|
QPushButton:pressed { background-color: rgba(255, 255, 255, 0.12); }
|
||||||
|
QPushButton:disabled { color: #808080; background-color: #303030; border-color: #444444; }
|
||||||
|
QPushButton:default { border-color: rgba(255, 255, 255, 0.24); }
|
||||||
|
QDialog#vita_themes_dialog { color: #e0e0e0; }
|
||||||
|
QDialog#vita_themes_dialog QLabel,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox,
|
||||||
|
QDialog#vita_themes_dialog QRadioButton { color: #e0e0e0; background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QLabel[themeRole="mutedText"] { color: #a8a8a8; }
|
||||||
|
QDialog#vita_themes_dialog QWidget[themeRole="backgroundCell"] { background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox[themeRole="backgroundCellControl"],
|
||||||
|
QDialog#vita_themes_dialog QRadioButton[themeRole="backgroundCellControl"] { spacing: 0px; padding: 0px; margin: 0px; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:!active { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:hover:!selected { background-color: #3a3a3a; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:!active { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:hover:!selected { background-color: #3a3a3a; }
|
||||||
|
QDialog#vita_themes_dialog QTextBrowser#vitaThemesHelpText {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QScrollArea { background: transparent; border: none; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #666666;
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:hover { border-color: #888888; background-color: #323232; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate {
|
||||||
|
background-color: rgba(255, 255, 255, 0.78);
|
||||||
|
border-color: rgba(255, 255, 255, 0.82);
|
||||||
|
image: url(":/icons/check_dark.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:disabled { border-color: #4a4a4a; background-color: #252525; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked:disabled,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate:disabled {
|
||||||
|
background-color: rgba(255, 255, 255, 0.38);
|
||||||
|
border-color: rgba(255, 255, 255, 0.42);
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #666666;
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:hover { border-color: #888888; background-color: #323232; }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked {
|
||||||
|
background-color: rgba(255, 255, 255, 0.78);
|
||||||
|
border-color: rgba(255, 255, 255, 0.82);
|
||||||
|
image: url(":/icons/radio_dot_dark.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:disabled { border-color: #4a4a4a; background-color: #252525; }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked:disabled {
|
||||||
|
background-color: rgba(255, 255, 255, 0.38);
|
||||||
|
border-color: rgba(255, 255, 255, 0.42);
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"] { background: transparent; border: none; border-radius: 10px; padding: 6px 12px; }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:hover { background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:checked { background-color: rgba(255, 255, 255, 0.14); }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:pressed { background-color: rgba(255, 255, 255, 0.20); }
|
||||||
|
QDialog#controls_dialog QPushButton { background-color: #303030; color: #e0e0e0; border: none; border-radius: 6px; padding: 6px 12px; outline: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:hover { background-color: #3a3a3a; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:pressed { background-color: #464646; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:disabled { color: #808080; background-color: #2c2c2c; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"] { background-color: #2f2f2f; border-radius: 6px; padding: 6px 12px; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:hover { background-color: #393939; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:pressed { background-color: #454545; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"] { background-color: rgba(255, 255, 255, 0.18); color: #ffffff; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:hover { background-color: rgba(255, 255, 255, 0.24); }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:pressed { background-color: rgba(255, 255, 255, 0.30); }
|
||||||
|
QStatusBar#emulator_status_bar { background-color: #353535; color: #e0e0e0; }
|
||||||
|
QStatusBar#emulator_status_bar::item { border: none; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button { border: 1px solid transparent; padding: 2px 8px; font-weight: bold; font-size: 11px; background: transparent; border-radius: 6px; color: #e0e0e0; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:hover { border: 1px solid #666666; background: rgba(255, 255, 255, 0.04); }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:pressed { border: 1px solid #888888; background: rgba(255, 255, 255, 0.07); }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_vulkan"] { color: #ff4444; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_opengl"] { color: #4488ff; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="high"] { color: #ff8800; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="standard"] { color: #aaaaaa; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="neutral"] { color: #cccccc; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="audio"] { color: #88dd44; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="muted"] { color: #777777; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="update"] { color: #6de089; }
|
||||||
|
QLineEdit { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 2px 4px; selection-background-color: rgba(255, 255, 255, 0.18); selection-color: #ffffff; }
|
||||||
|
QLineEdit:focus { border-color: rgba(255, 255, 255, 0.24); }
|
||||||
|
QLineEdit:disabled { color: #777777; background-color: #303030; border-color: #444444; }
|
||||||
|
QComboBox { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 2px 28px 2px 6px; }
|
||||||
|
QComboBox:hover { border-color: #666666; }
|
||||||
|
QComboBox:disabled { color: #777777; background-color: #303030; border-color: #444444; }
|
||||||
|
QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }
|
||||||
|
QComboBox::down-arrow { image: url("chevron_down_light.svg"); width: 10px; height: 6px; }
|
||||||
|
QComboBox::down-arrow:on { top: 1px; }
|
||||||
|
QComboBox QAbstractItemView { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; selection-background-color: rgba(255, 255, 255, 0.12); selection-color: #ffffff; }
|
||||||
|
QSpinBox, QDoubleSpinBox { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 2px 24px 2px 6px; selection-background-color: rgba(255, 255, 255, 0.18); selection-color: #ffffff; }
|
||||||
|
QSpinBox:disabled, QDoubleSpinBox:disabled { color: #777777; background-color: #303030; border-color: #444444; }
|
||||||
|
QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { image: url("chevron_up_light.svg"); width: 10px; height: 6px; }
|
||||||
|
QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { image: url("chevron_down_light.svg"); width: 10px; height: 6px; }
|
||||||
|
QCheckBox { color: #e0e0e0; spacing: 6px; }
|
||||||
|
QCheckBox:disabled { color: #777777; }
|
||||||
|
QRadioButton { color: #e0e0e0; spacing: 6px; }
|
||||||
|
QRadioButton:disabled { color: #777777; }
|
||||||
|
QGroupBox { color: #e0e0e0; border: 1px solid #555555; border-radius: 4px; margin-top: 0.5em; padding-top: 0.6em; }
|
||||||
|
QGroupBox:disabled { color: #7f7f7f; border-color: #4a4a4a; }
|
||||||
|
QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: #e0e0e0; }
|
||||||
|
QLabel { color: #e0e0e0; background: transparent; }
|
||||||
|
QLabel:disabled { color: #777777; }
|
||||||
|
QTableWidget, QTableView { alternate-background-color: #3a3a3a; background-color: #2a2a2a; border: none; color: #e0e0e0; gridline-color: #444444; }
|
||||||
|
QTableWidget:disabled, QTableView:disabled { color: #777777; background-color: #303030; }
|
||||||
|
QTableCornerButton::section { background-color: #353535; border: 1px solid #555555; }
|
||||||
|
QTableView::item { border-left: 0.063em solid #2a2a2a; border-right: 0.063em solid #2a2a2a; padding-left: 0.313em; }
|
||||||
|
QTableView::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }
|
||||||
|
QTableWidget#apps_list_table::item:selected, QTableView#apps_list_table::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #f0f0f0; border-left: 0.063em solid rgba(255, 255, 255, 0); border-right: 0.063em solid rgba(255, 255, 255, 0); }
|
||||||
|
QTableView#apps_list_table QHeaderView::section, QTableWidget#apps_list_table QHeaderView::section { padding-top: 0; padding-bottom: 0; }
|
||||||
|
QTableView#trophy_app_table::item:selected, QTableView#trophy_table::item:selected { background-color: rgba(255, 255, 255, 0.13); color: #ffffff; border-left: 0.063em solid rgba(255, 255, 255, 0); border-right: 0.063em solid rgba(255, 255, 255, 0); }
|
||||||
|
QHeaderView::section { background-color: #353535; color: #ffffff; padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #2a2a2a; }
|
||||||
|
QHeaderView::section:hover { background: #4a4a4a; }
|
||||||
|
QDockWidget { background: transparent; color: #ffffff; }
|
||||||
|
QDockWidget#apps_list[floating="true"],
|
||||||
|
QDockWidget#logger[floating="true"] { background-color: #353535; }
|
||||||
|
QDockWidget::title { background: #353535; border: none; padding-top: 0.2em; padding-left: 0.2em; }
|
||||||
|
QDockWidget::close-button, QDockWidget::float-button { background-color: #353535; border: none; }
|
||||||
|
QDockWidget::close-button:hover, QDockWidget::float-button:hover { background-color: #4a4a4a; }
|
||||||
|
QWidget#apps_list_frame { background-color: #353535; }
|
||||||
|
QWidget#log_surface { background-color: #353535; }
|
||||||
|
QWidget#apps_list_viewport { background-color: #2a2a2a; }
|
||||||
|
QWidget#log_frame_viewport { background-color: #2a2a2a; }
|
||||||
|
QPlainTextEdit#log_frame { background-color: #2a2a2a; color: #ffffff; }
|
||||||
|
QWidget#log_search_container { background-color: #2a2a2a; border: 1px solid #555555; border-radius: 4px; }
|
||||||
|
QWidget#toolbar_icon_size_container { background-color: #353535; }
|
||||||
|
QSlider#toolbar_icon_size_slider { background: transparent; }
|
||||||
|
QSlider#toolbar_icon_size_slider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::sub-page:horizontal { background: rgba(255, 255, 255, 0.82); height: 4px; border-radius: 2px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::add-page:horizontal { background: #555555; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal { background: #f3f3f3; width: 14px; margin: -5px 0; border-radius: 7px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal:hover { background: #ffffff; }
|
||||||
|
QLabel[themeRole="logFatal"] { color: #ff66ff; }
|
||||||
|
QLabel[themeRole="logError"] { color: #ff8080; }
|
||||||
|
QLabel[themeRole="logWarning"] { color: #ffd27f; }
|
||||||
|
QLabel[themeRole="logNotice"] { color: #ffffff; }
|
||||||
|
QLabel[themeRole="logDebug"] { color: #8bb9ff; }
|
||||||
|
QLabel[themeRole="logTrace"] { color: #ffffff; }
|
||||||
|
QLabel[themeRole="sectionHeader"] { font-weight: 600; margin-top: 8px; }
|
||||||
|
QLabel[severity="success"] { border-radius: 10px; padding: 4px 10px; background-color: #255a2e; color: #b9f5bf; }
|
||||||
|
QLabel[severity="error"] { border-radius: 10px; padding: 4px 10px; background-color: #6d2a25; color: #ffb1aa; }
|
||||||
|
QTabWidget::pane { border: 1px solid #555555; background-color: #353535; }
|
||||||
|
QTabBar::tab { background-color: #2d2d2d; color: #e0e0e0; padding: 6px 14px; border: 1px solid #555555; border-bottom: none; border-top-left-radius: 3px; border-top-right-radius: 3px; }
|
||||||
|
QTabBar::tab:selected { background-color: #353535; }
|
||||||
|
QTabBar::tab:!selected { margin-top: 2px; }
|
||||||
|
QTabBar::tab:hover:!selected { background-color: #3a3a3a; }
|
||||||
|
QListWidget { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; }
|
||||||
|
QListWidget:disabled { color: #777777; background-color: #303030; border-color: #444444; }
|
||||||
|
QListWidget::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }
|
||||||
|
QListWidget::item:hover:!selected { background-color: #3a3a3a; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory { background-color: #2a2a2a; border: none; border-radius: 10px; padding: 6px; outline: none; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item { background: transparent; border: none; border-radius: 10px; padding: 8px 12px; margin: 0px; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:active,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:!active { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:hover:!selected { background-color: #3a3a3a; }
|
||||||
|
QDialog[class="settings-window"] QTextBrowser#helpText { background-color: #2a2a2a; color: #e0e0e0; border: none; border-radius: 10px; padding: 3px 4px; }
|
||||||
|
QScrollBar:vertical { background: #2a2a2a; width: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:vertical { background: #5a5a5a; min-height: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:vertical:hover { background: #6a6a6a; }
|
||||||
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }
|
||||||
|
QScrollBar:horizontal { background: #2a2a2a; height: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:horizontal { background: #5a5a5a; min-width: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:horizontal:hover { background: #6a6a6a; }
|
||||||
|
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }
|
||||||
|
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; }
|
||||||
|
QAbstractScrollArea::corner { background: #2a2a2a; }
|
||||||
|
QSlider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::sub-page:horizontal { background: rgba(255, 255, 255, 0.82); height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::add-page:horizontal { background: #555555; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:horizontal { background: #c0c0c0; width: 14px; margin: -5px 0; border-radius: 7px; }
|
||||||
|
QSlider::handle:horizontal:hover { background: #ffffff; }
|
||||||
|
QSlider::groove:vertical { background: transparent; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::sub-page:vertical { background: rgba(255, 255, 255, 0.82); width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::add-page:vertical { background: #555555; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:vertical { background: #c0c0c0; height: 14px; margin: 0 -5px; border-radius: 7px; }
|
||||||
|
QSlider::handle:vertical:hover { background: #ffffff; }
|
||||||
|
QProgressBar { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; text-align: center; border-radius: 2px; }
|
||||||
|
QProgressBar::chunk { background-color: #6a6a6a; border-radius: 2px; }
|
||||||
|
QSplitter::handle { background-color: #555555; }
|
||||||
|
QToolTip { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; padding: 2px; }
|
||||||
|
QPlainTextEdit, QTextEdit, QTextBrowser { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; }
|
||||||
|
QPlainTextEdit:disabled, QTextEdit:disabled, QTextBrowser:disabled { color: #777777; background-color: #303030; border-color: #444444; }
|
||||||
|
QDialogButtonBox QPushButton { min-width: 80px; }
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
/*!PALETTE
|
||||||
|
* dark: light
|
||||||
|
* window: #f5f5f5
|
||||||
|
* window-text: #202020
|
||||||
|
* base: #ffffff
|
||||||
|
* alternate-base: #f2f2f2
|
||||||
|
* tooltip-base: #f7f7f7
|
||||||
|
* tooltip-text: #202020
|
||||||
|
* text: #202020
|
||||||
|
* button: #ffffff
|
||||||
|
* button-text: #202020
|
||||||
|
* bright-text: #ffffff
|
||||||
|
* link: #148aff
|
||||||
|
* highlight: #148aff
|
||||||
|
* inactive-highlight: #148aff
|
||||||
|
* disabled-highlight: #40148aff
|
||||||
|
* highlight-text: #ffffff
|
||||||
|
* inactive-highlight-text: #ffffff
|
||||||
|
* disabled-highlight-text: #d0d0d0
|
||||||
|
* placeholder-text: #8a8a8a
|
||||||
|
* disabled-button-text: #8a8a8a
|
||||||
|
* disabled-window-text: #8a8a8a
|
||||||
|
* disabled-text: #8a8a8a
|
||||||
|
*/
|
||||||
|
QWidget { color: #202020; background-color: #f5f5f5; }
|
||||||
|
QWidget:disabled { color: #8a8a8a; }
|
||||||
|
QMainWindow { background-color: #f5f5f5; }
|
||||||
|
QMainWindow#main_window { background-color: #f5f5f5; }
|
||||||
|
QWidget#mw_frame { background-color: #f5f5f5; }
|
||||||
|
QMainWindow#mw_dock_host { background: transparent; }
|
||||||
|
QDialog { background-color: #f5f5f5; }
|
||||||
|
QMenuBar { background-color: #f0f0f0; color: #202020; }
|
||||||
|
QMenuBar::item { background: transparent; padding: 4px 8px; }
|
||||||
|
QMenuBar::item:selected { background: #e1e1e1; }
|
||||||
|
QMenu { background-color: #f7f7f7; color: #202020; border: 1px solid #c6c6c6; }
|
||||||
|
QMenu::item:selected { background-color: #d6e8ff; color: #202020; }
|
||||||
|
QMenu::item:disabled { color: #808080; }
|
||||||
|
QMenu::separator { height: 1px; background: #d0d0d0; margin: 4px 8px; }
|
||||||
|
QToolBar#mw_toolbar { background-color: #f0f0f0; border: none; border-top: 1px solid #cfcfcf; }
|
||||||
|
QToolBar#mw_toolbar::separator { background-color: rgba(207, 207, 207, 235); width: 0.125em; margin-top: 0.250em; margin-bottom: 0.250em; }
|
||||||
|
QToolButton { color: #202020; background: transparent; border: 1px solid transparent; padding: 3px; }
|
||||||
|
QToolButton:hover { background-color: #e8e8e8; border: 1px solid #bdbdbd; }
|
||||||
|
QToolButton:pressed { background-color: #d8d8d8; color: #202020; }
|
||||||
|
QToolButton:disabled { color: #808080; }
|
||||||
|
QToolButton#welcome_firmware_button { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 4px 32px 4px 12px; text-align: left; }
|
||||||
|
QToolButton#welcome_firmware_button:hover { background-color: #f3f3f3; border-color: #9f9f9f; }
|
||||||
|
QToolButton#welcome_firmware_button:pressed { background-color: #e3e3e3; }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border-left: 1px solid #b8b8b8; border-top-right-radius: 2px; border-bottom-right-radius: 2px; background: #f3f3f3; }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button:hover { background: #e8e8e8; border-left-color: #9f9f9f; }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button:pressed { background: #dcdcdc; }
|
||||||
|
QLabel[themeRole="toolbarIconTint"] { color: #5b5b5b; }
|
||||||
|
QLabel[themeRole="mutedText"] { color: #5b5b5b; }
|
||||||
|
QPushButton { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 4px 16px; min-height: 1.2em; }
|
||||||
|
QPushButton:hover { background-color: #f3f3f3; border-color: #9f9f9f; }
|
||||||
|
QPushButton:pressed { background-color: #e3e3e3; }
|
||||||
|
QPushButton:disabled { color: #8a8a8a; background-color: #efefef; border-color: #d4d4d4; }
|
||||||
|
QPushButton:default { border-color: #148aff; }
|
||||||
|
QDialog#vita_themes_dialog { color: #202020; }
|
||||||
|
QDialog#vita_themes_dialog QLabel,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox,
|
||||||
|
QDialog#vita_themes_dialog QRadioButton { color: #202020; background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QLabel[themeRole="mutedText"] { color: #5b5b5b; }
|
||||||
|
QDialog#vita_themes_dialog QWidget[themeRole="backgroundCell"] { background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox[themeRole="backgroundCellControl"],
|
||||||
|
QDialog#vita_themes_dialog QRadioButton[themeRole="backgroundCellControl"] { spacing: 0px; padding: 0px; margin: 0px; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #202020;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:!active { background-color: #148aff; color: #ffffff; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:hover:!selected { background-color: #f2f2f2; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #202020;
|
||||||
|
border: 1px solid #c8c8c8;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:!active { background-color: #148aff; color: #ffffff; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:hover:!selected { background-color: #f2f2f2; }
|
||||||
|
QDialog#vita_themes_dialog QTextBrowser#vitaThemesHelpText {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #202020;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QScrollArea { background: transparent; border: none; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #b8b8b8;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:hover { border-color: #148aff; background-color: #f2f8ff; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate {
|
||||||
|
background-color: #148aff;
|
||||||
|
border-color: #148aff;
|
||||||
|
image: url(":/icons/check_light.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:disabled { border-color: #d0d0d0; background-color: #f0f0f0; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked:disabled,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate:disabled {
|
||||||
|
background-color: rgba(20, 138, 255, 0.25);
|
||||||
|
border-color: rgba(20, 138, 255, 0.35);
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #b8b8b8;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:hover { border-color: #148aff; background-color: #f2f8ff; }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked {
|
||||||
|
background-color: #148aff;
|
||||||
|
border-color: #148aff;
|
||||||
|
image: url(":/icons/radio_dot_light.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:disabled { border-color: #d0d0d0; background-color: #f0f0f0; }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked:disabled {
|
||||||
|
background-color: rgba(20, 138, 255, 0.25);
|
||||||
|
border-color: rgba(20, 138, 255, 0.35);
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"] { background: transparent; border: none; border-radius: 10px; padding: 6px 12px; }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:hover { background-color: rgba(20, 138, 255, 0.08); }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:checked { background-color: rgba(20, 138, 255, 0.14); }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:pressed { background-color: rgba(20, 138, 255, 0.18); }
|
||||||
|
QDialog#controls_dialog QPushButton { background-color: #ececec; color: #202020; border: none; border-radius: 6px; padding: 6px 12px; outline: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:hover { background-color: #e3e3e3; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:pressed { background-color: #d8d8d8; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:disabled { color: #8a8a8a; background-color: #e4e4e4; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"] { background-color: #e6e6e6; border-radius: 6px; padding: 6px 12px; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:hover { background-color: #dddddd; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:pressed { background-color: #d2d2d2; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"] { background-color: #148aff; color: #ffffff; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:hover { background-color: #2a97ff; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:pressed { background-color: #0f74d6; }
|
||||||
|
QStatusBar#emulator_status_bar { background-color: #f0f0f0; color: #202020; }
|
||||||
|
QStatusBar#emulator_status_bar::item { border: none; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button { border: 1px solid transparent; padding: 2px 8px; font-weight: bold; font-size: 11px; background: transparent; border-radius: 6px; color: #202020; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:hover { border: 1px solid #a0a0a0; background: rgba(0, 0, 0, 0.03); }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:pressed { border: 1px solid #8a8a8a; background: rgba(0, 0, 0, 0.06); }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_vulkan"] { color: #ff4444; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_opengl"] { color: #4488ff; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="high"] { color: #ff8800; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="standard"] { color: #aaaaaa; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="neutral"] { color: #5f5f5f; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="audio"] { color: #4b9b2f; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="muted"] { color: #777777; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="update"] { color: #1f8f4e; }
|
||||||
|
QLineEdit { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 2px 4px; selection-background-color: #148aff; selection-color: #ffffff; }
|
||||||
|
QLineEdit:focus { border-color: #148aff; }
|
||||||
|
QLineEdit:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }
|
||||||
|
QComboBox { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 2px 28px 2px 6px; }
|
||||||
|
QComboBox:hover { border-color: #9f9f9f; }
|
||||||
|
QComboBox:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }
|
||||||
|
QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }
|
||||||
|
QComboBox::down-arrow { image: url("chevron_down_dark.svg"); width: 10px; height: 6px; }
|
||||||
|
QComboBox::down-arrow:on { top: 1px; }
|
||||||
|
QComboBox QAbstractItemView { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; selection-background-color: #148aff; selection-color: #ffffff; }
|
||||||
|
QSpinBox, QDoubleSpinBox { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 2px 24px 2px 6px; selection-background-color: #148aff; selection-color: #ffffff; }
|
||||||
|
QSpinBox:disabled, QDoubleSpinBox:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }
|
||||||
|
QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { image: url("chevron_up_dark.svg"); width: 10px; height: 6px; }
|
||||||
|
QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { image: url("chevron_down_dark.svg"); width: 10px; height: 6px; }
|
||||||
|
QCheckBox { color: #202020; spacing: 6px; }
|
||||||
|
QCheckBox:disabled { color: #8a8a8a; }
|
||||||
|
QRadioButton { color: #202020; spacing: 6px; }
|
||||||
|
QRadioButton:disabled { color: #8a8a8a; }
|
||||||
|
QGroupBox { color: #202020; border: 1px solid #c8c8c8; border-radius: 4px; margin-top: 0.5em; padding-top: 0.6em; }
|
||||||
|
QGroupBox:disabled { color: #9a9a9a; border-color: #d8d8d8; }
|
||||||
|
QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: #202020; }
|
||||||
|
QLabel { color: #202020; background: transparent; }
|
||||||
|
QLabel:disabled { color: #8a8a8a; }
|
||||||
|
QTableWidget, QTableView { alternate-background-color: #f2f2f2; background-color: #ffffff; border: none; color: #202020; gridline-color: #e0e0e0; }
|
||||||
|
QTableWidget:disabled, QTableView:disabled { color: #8a8a8a; background-color: #f0f0f0; }
|
||||||
|
QTableCornerButton::section { background-color: #ececec; border: 1px solid #d4d4d4; }
|
||||||
|
QTableView::item { border-left: 0.063em solid #ffffff; border-right: 0.063em solid #ffffff; padding-left: 0.313em; }
|
||||||
|
QTableView::item:selected { background-color: #148aff; color: #ffffff; }
|
||||||
|
QTableWidget#apps_list_table::item:selected, QTableView#apps_list_table::item:selected { background-color: rgba(84, 140, 188, 48); color: #202020; border-left: 0.063em solid rgba(84, 140, 188, 0); border-right: 0.063em solid rgba(84, 140, 188, 0); }
|
||||||
|
QTableView#apps_list_table QHeaderView::section, QTableWidget#apps_list_table QHeaderView::section { padding-top: 0; padding-bottom: 0; }
|
||||||
|
QTableView#trophy_app_table::item:selected, QTableView#trophy_table::item:selected { background-color: rgba(84, 140, 188, 48); color: #202020; border-left: 0.063em solid rgba(84, 140, 188, 0); border-right: 0.063em solid rgba(84, 140, 188, 0); }
|
||||||
|
QHeaderView::section { background-color: #ececec; color: #202020; padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }
|
||||||
|
QHeaderView::section:hover { background: #e0e0e0; }
|
||||||
|
QDockWidget { background: transparent; color: #202020; }
|
||||||
|
QDockWidget#apps_list[floating="true"],
|
||||||
|
QDockWidget#logger[floating="true"] { background-color: #f5f5f5; }
|
||||||
|
QDockWidget::title { background: #ececec; border: none; padding-top: 0.2em; padding-left: 0.2em; }
|
||||||
|
QDockWidget::close-button, QDockWidget::float-button { background-color: #ececec; border: none; }
|
||||||
|
QDockWidget::close-button:hover, QDockWidget::float-button:hover { background-color: #e0e0e0; }
|
||||||
|
QWidget#apps_list_frame { background-color: #f5f5f5; }
|
||||||
|
QWidget#log_surface { background-color: #f5f5f5; }
|
||||||
|
QWidget#apps_list_viewport { background-color: #ffffff; }
|
||||||
|
QWidget#log_frame_viewport { background-color: #ffffff; }
|
||||||
|
QPlainTextEdit#log_frame { background-color: #ffffff; color: #202020; }
|
||||||
|
QWidget#log_search_container { background-color: #ffffff; border: 1px solid #c8c8c8; border-radius: 4px; }
|
||||||
|
QWidget#toolbar_icon_size_container { background-color: #f0f0f0; }
|
||||||
|
QSlider#toolbar_icon_size_slider { background: transparent; }
|
||||||
|
QSlider#toolbar_icon_size_slider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::sub-page:horizontal { background: #7f7f7f; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::add-page:horizontal { background: #c8c8c8; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal { background: #5b5b5b; width: 14px; margin: -5px 0; border-radius: 7px; }
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal:hover { background: #404040; }
|
||||||
|
QLabel[themeRole="logFatal"] { color: #ff00ff; }
|
||||||
|
QLabel[themeRole="logError"] { color: #C02F1D; }
|
||||||
|
QLabel[themeRole="logWarning"] { color: #BA8745; }
|
||||||
|
QLabel[themeRole="logNotice"] { color: #000000; }
|
||||||
|
QLabel[themeRole="logDebug"] { color: #2467c8; }
|
||||||
|
QLabel[themeRole="logTrace"] { color: #808080; }
|
||||||
|
QLabel[themeRole="sectionHeader"] { font-weight: 600; margin-top: 8px; }
|
||||||
|
QLabel[severity="success"] { border-radius: 10px; padding: 4px 10px; background-color: #d9f2dd; color: #1f7a1f; }
|
||||||
|
QLabel[severity="error"] { border-radius: 10px; padding: 4px 10px; background-color: #f7dcd9; color: #a32a1c; }
|
||||||
|
QTabWidget::pane { border: 1px solid #c8c8c8; background-color: #f5f5f5; }
|
||||||
|
QTabBar::tab { background-color: #e9e9e9; color: #202020; padding: 6px 14px; border: 1px solid #c8c8c8; border-bottom: none; border-top-left-radius: 3px; border-top-right-radius: 3px; }
|
||||||
|
QTabBar::tab:selected { background-color: #f5f5f5; }
|
||||||
|
QTabBar::tab:!selected { margin-top: 2px; }
|
||||||
|
QTabBar::tab:hover:!selected { background-color: #dfdfdf; }
|
||||||
|
QListWidget { background-color: #ffffff; color: #202020; border: 1px solid #c8c8c8; }
|
||||||
|
QListWidget:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d6d6d6; }
|
||||||
|
QListWidget::item:selected { background-color: #148aff; color: #ffffff; }
|
||||||
|
QListWidget::item:hover:!selected { background-color: #f2f2f2; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory { background-color: #ffffff; border: none; border-radius: 10px; padding: 6px; outline: none; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item { background: transparent; border: none; border-radius: 10px; padding: 8px 12px; margin: 0px; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:active,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:!active { background-color: #148aff; color: #ffffff; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:hover:!selected { background-color: #f2f2f2; }
|
||||||
|
QDialog[class="settings-window"] QTextBrowser#helpText { background-color: #ffffff; color: #202020; border: none; border-radius: 10px; padding: 3px 4px; }
|
||||||
|
QScrollBar:vertical { background: #f0f0f0; width: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:vertical { background: #b0b0b0; min-height: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:vertical:hover { background: #989898; }
|
||||||
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }
|
||||||
|
QScrollBar:horizontal { background: #f0f0f0; height: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:horizontal { background: #b0b0b0; min-width: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:horizontal:hover { background: #989898; }
|
||||||
|
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }
|
||||||
|
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; }
|
||||||
|
QAbstractScrollArea::corner { background: #f0f0f0; }
|
||||||
|
QSlider::groove:horizontal { background: #c8c8c8; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:horizontal { background: #7f7f7f; width: 14px; margin: -5px 0; border-radius: 7px; }
|
||||||
|
QSlider::handle:horizontal:hover { background: #666666; }
|
||||||
|
QSlider::groove:vertical { background: #c8c8c8; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:vertical { background: #7f7f7f; height: 14px; margin: 0 -5px; border-radius: 7px; }
|
||||||
|
QSlider::handle:vertical:hover { background: #666666; }
|
||||||
|
QProgressBar { background-color: #ffffff; color: #202020; border: 1px solid #c8c8c8; text-align: center; border-radius: 2px; }
|
||||||
|
QProgressBar::chunk { background-color: #148aff; border-radius: 2px; }
|
||||||
|
QSplitter::handle { background-color: #d4d4d4; }
|
||||||
|
QToolTip { background-color: #f7f7f7; color: #202020; border: 1px solid #c8c8c8; padding: 2px; }
|
||||||
|
QPlainTextEdit, QTextEdit, QTextBrowser { background-color: #ffffff; color: #202020; border: 1px solid #c8c8c8; }
|
||||||
|
QPlainTextEdit:disabled, QTextEdit:disabled, QTextBrowser:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }
|
||||||
|
QDialogButtonBox QPushButton { min-width: 80px; }
|
||||||
@@ -0,0 +1,664 @@
|
|||||||
|
/*!VITA3K_THEME
|
||||||
|
background-images: {{generated_background_images}}
|
||||||
|
background-interval: {{generated_background_interval_seconds}}
|
||||||
|
background-animated: {{generated_background_animated}}
|
||||||
|
*/
|
||||||
|
/*!PALETTE
|
||||||
|
* dark: {{palette_dark}}
|
||||||
|
* window: {{window_hex}}
|
||||||
|
* window-text: {{text_hex}}
|
||||||
|
* base: {{base_hex}}
|
||||||
|
* alternate-base: {{alternate_base_hex}}
|
||||||
|
* tooltip-base: {{base_hex}}
|
||||||
|
* tooltip-text: {{text_hex}}
|
||||||
|
* text: {{text_hex}}
|
||||||
|
* button: {{button_hex}}
|
||||||
|
* button-text: {{button_text_hex}}
|
||||||
|
* bright-text: {{button_bright_text_hex}}
|
||||||
|
* link: {{accent_hex}}
|
||||||
|
* highlight: {{highlight_argb}}
|
||||||
|
* inactive-highlight: {{inactive_highlight_argb}}
|
||||||
|
* disabled-highlight: {{disabled_highlight_argb}}
|
||||||
|
* highlight-text: {{highlight_text_hex}}
|
||||||
|
* inactive-highlight-text: {{highlight_text_hex}}
|
||||||
|
* disabled-highlight-text: {{overlay_disabled_text_hex}}
|
||||||
|
* placeholder-text: {{muted_text_hex}}
|
||||||
|
* disabled-button-text: {{disabled_text_hex}}
|
||||||
|
* disabled-window-text: {{disabled_text_hex}}
|
||||||
|
* disabled-text: {{disabled_text_hex}}
|
||||||
|
*/
|
||||||
|
QWidget { color: {{text_hex}}; background: transparent; }
|
||||||
|
QWidget:disabled { color: {{disabled_text_hex}}; }
|
||||||
|
QMainWindow { background-color: {{window_hex}}; }
|
||||||
|
QMainWindow#main_window {
|
||||||
|
background-color: {{window_hex}};
|
||||||
|
}
|
||||||
|
QWidget#mw_frame,
|
||||||
|
QMainWindow#mw_dock_host {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
QDialog { background-color: {{dialog_bg}}; }
|
||||||
|
QMenuBar {
|
||||||
|
background-color: {{menu_bar_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border-bottom: 1px solid {{menu_bar_border}};
|
||||||
|
}
|
||||||
|
QMenuBar::item { background: transparent; padding: 4px 8px; }
|
||||||
|
QMenuBar::item:selected { background: {{menu_bar_item_hover_bg}}; }
|
||||||
|
QMenu {
|
||||||
|
background-color: {{menu_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{menu_border}};
|
||||||
|
}
|
||||||
|
QMenu::item:selected { background-color: {{menu_selected_bg}}; color: {{selected_text_hex}}; }
|
||||||
|
QMenu::item:disabled { color: {{disabled_text_hex}}; }
|
||||||
|
QMenu::separator { height: 1px; background: {{menu_separator_bg}}; margin: 4px 8px; }
|
||||||
|
QToolBar#mw_toolbar {
|
||||||
|
background-color: {{toolbar_bg}};
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid {{toolbar_border_top}};
|
||||||
|
border-bottom: 1px solid {{toolbar_border_bottom}};
|
||||||
|
}
|
||||||
|
QToolBar#mw_toolbar::separator {
|
||||||
|
background-color: {{toolbar_separator_bg}};
|
||||||
|
width: 0.125em;
|
||||||
|
margin-top: 0.250em;
|
||||||
|
margin-bottom: 0.250em;
|
||||||
|
}
|
||||||
|
QToolBar#mw_toolbar > QWidget { background: transparent; }
|
||||||
|
QToolButton {
|
||||||
|
color: {{text_hex}};
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
QToolButton:hover { background-color: {{tool_button_hover_bg}}; border: 1px solid {{tool_button_hover_border}}; }
|
||||||
|
QToolButton:pressed { background-color: {{tool_button_pressed_bg}}; }
|
||||||
|
QToolButton:disabled { color: {{disabled_text_hex}}; }
|
||||||
|
QToolButton#welcome_firmware_button {
|
||||||
|
background-color: {{welcome_button_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{welcome_button_border}};
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 4px 32px 4px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
QToolButton#welcome_firmware_button:hover {
|
||||||
|
background-color: {{welcome_button_hover_bg}};
|
||||||
|
border-color: {{welcome_button_hover_border}};
|
||||||
|
}
|
||||||
|
QToolButton#welcome_firmware_button:pressed { background-color: {{welcome_button_pressed_bg}}; }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button {
|
||||||
|
subcontrol-origin: padding;
|
||||||
|
subcontrol-position: top right;
|
||||||
|
width: 24px;
|
||||||
|
border-left: 1px solid {{welcome_menu_button_border}};
|
||||||
|
background: {{welcome_menu_button_bg}};
|
||||||
|
}
|
||||||
|
QToolButton#welcome_firmware_button::menu-button:hover {
|
||||||
|
background: {{welcome_menu_button_hover_bg}};
|
||||||
|
border-left-color: {{welcome_menu_button_hover_border}};
|
||||||
|
}
|
||||||
|
QToolButton#welcome_firmware_button::menu-button:pressed { background: {{welcome_menu_button_pressed_bg}}; }
|
||||||
|
QLabel[themeRole="toolbarIconTint"] { color: {{toolbar_icon_tint_hex}}; }
|
||||||
|
QLabel[themeRole="mutedText"] { color: {{muted_text_hex}}; }
|
||||||
|
QPushButton {
|
||||||
|
background-color: {{push_button_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{push_button_border}};
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 4px 16px;
|
||||||
|
min-height: 1.2em;
|
||||||
|
}
|
||||||
|
QPushButton:hover { background-color: {{push_button_hover_bg}}; border-color: {{push_button_hover_border}}; }
|
||||||
|
QPushButton:pressed { background-color: {{push_button_pressed_bg}}; color: {{push_button_pressed_text_hex}}; }
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: {{disabled_text_hex}};
|
||||||
|
background-color: {{push_button_disabled_bg}};
|
||||||
|
border-color: {{push_button_disabled_border}};
|
||||||
|
}
|
||||||
|
QPushButton:default { border-color: {{push_button_default_border}}; }
|
||||||
|
QDialog#vita_themes_dialog { color: {{dialog_text_hex}}; }
|
||||||
|
QDialog#vita_themes_dialog QLabel,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox,
|
||||||
|
QDialog#vita_themes_dialog QRadioButton { color: {{dialog_text_hex}}; background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QLabel[themeRole="mutedText"] { color: {{dialog_muted_text_hex}}; }
|
||||||
|
QDialog#vita_themes_dialog QWidget[themeRole="backgroundCell"] { background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox[themeRole="backgroundCellControl"],
|
||||||
|
QDialog#vita_themes_dialog QRadioButton[themeRole="backgroundCellControl"] { spacing: 0px; padding: 0px; margin: 0px; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList {
|
||||||
|
background-color: {{vita_list_bg}};
|
||||||
|
color: {{dialog_text_hex}};
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:!active { background-color: {{vita_list_selected_bg}}; color: {{dialog_selected_text_hex}}; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:hover:!selected { background-color: {{vita_list_hover_bg}}; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList {
|
||||||
|
background-color: {{background_list_bg}};
|
||||||
|
color: {{dialog_text_hex}};
|
||||||
|
border: 1px solid {{background_list_border}};
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:!active { background-color: {{background_list_selected_bg}}; color: {{dialog_selected_text_hex}}; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:hover:!selected { background-color: {{background_list_hover_bg}}; }
|
||||||
|
QDialog#vita_themes_dialog QTextBrowser#vitaThemesHelpText {
|
||||||
|
background-color: {{vita_help_bg}};
|
||||||
|
color: {{dialog_text_hex}};
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QScrollArea { background: transparent; border: none; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid {{indicator_border}};
|
||||||
|
background-color: {{indicator_bg}};
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:hover { border-color: {{indicator_hover_border}}; background-color: {{indicator_hover_bg}}; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate {
|
||||||
|
background-color: {{indicator_checked_bg}};
|
||||||
|
border-color: {{indicator_checked_border}};
|
||||||
|
image: url(":/icons/check_{{check_icon_suffix}}.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:disabled { border-color: {{indicator_disabled_border}}; background-color: {{indicator_disabled_bg}}; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked:disabled,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate:disabled {
|
||||||
|
background-color: {{indicator_checked_disabled_bg}};
|
||||||
|
border-color: {{indicator_checked_disabled_border}};
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid {{radio_indicator_border}};
|
||||||
|
background-color: {{radio_indicator_bg}};
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:hover { border-color: {{radio_indicator_hover_border}}; background-color: {{radio_indicator_hover_bg}}; }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked {
|
||||||
|
background-color: {{radio_checked_bg}};
|
||||||
|
border-color: {{radio_checked_border}};
|
||||||
|
image: url(":/icons/radio_dot_{{check_icon_suffix}}.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:disabled { border-color: {{indicator_disabled_border}}; background-color: {{indicator_disabled_bg}}; }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked:disabled {
|
||||||
|
background-color: {{radio_checked_disabled_bg}};
|
||||||
|
border-color: {{radio_checked_disabled_border}};
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"] {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:hover { background-color: {{controls_mode_hover_bg}}; }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:checked { background-color: {{controls_mode_checked_bg}}; color: {{dialog_mode_checked_text_hex}}; }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:pressed { background-color: {{controls_mode_pressed_bg}}; color: {{dialog_mode_checked_text_hex}}; }
|
||||||
|
QDialog#controls_dialog QPushButton {
|
||||||
|
background-color: {{controls_button_bg}};
|
||||||
|
color: {{dialog_text_hex}};
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QPushButton:hover { background-color: {{controls_button_hover_bg}}; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:pressed { background-color: {{controls_button_pressed_bg}}; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:disabled { color: {{dialog_disabled_text_hex}}; background-color: {{controls_button_disabled_bg}}; border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"] {
|
||||||
|
background-color: {{controls_bind_bg}};
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:hover { background-color: {{controls_bind_hover_bg}}; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:pressed { background-color: {{controls_bind_pressed_bg}}; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"] {
|
||||||
|
background-color: {{controls_capture_bg}};
|
||||||
|
color: {{dialog_selected_text_hex}};
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:hover { background-color: {{controls_capture_hover_bg}}; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:pressed { background-color: {{controls_capture_pressed_bg}}; }
|
||||||
|
QStatusBar#emulator_status_bar {
|
||||||
|
background-color: {{status_bar_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border-top: 1px solid {{status_bar_border}};
|
||||||
|
}
|
||||||
|
QStatusBar#emulator_status_bar::item { border: none; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 11px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: {{text_hex}};
|
||||||
|
}
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:hover { border: 1px solid {{status_button_hover_border}}; background: {{status_button_hover_bg}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:pressed { border: 1px solid {{status_button_pressed_border}}; background: {{status_button_pressed_bg}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_vulkan"] { color: {{renderer_vulkan_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_opengl"] { color: {{renderer_opengl_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="high"] { color: {{status_high_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="standard"] { color: {{status_standard_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="neutral"] { color: {{status_neutral_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="audio"] { color: {{status_audio_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="muted"] { color: {{status_muted_hex}}; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="update"] { color: {{status_update_hex}}; }
|
||||||
|
QLineEdit {
|
||||||
|
background-color: {{line_edit_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{line_edit_border}};
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
selection-background-color: {{line_edit_selection_bg}};
|
||||||
|
selection-color: {{selected_text_hex}};
|
||||||
|
}
|
||||||
|
QLineEdit:focus { border-color: {{line_edit_focus_border}}; }
|
||||||
|
QLineEdit:disabled { color: {{disabled_text_hex}}; background-color: {{line_edit_disabled_bg}}; border-color: {{line_edit_disabled_border}}; }
|
||||||
|
QLineEdit#mw_searchbar {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid {{main_search_border}};
|
||||||
|
border-radius: 10px;
|
||||||
|
color: {{text_hex}};
|
||||||
|
padding: 5px 12px;
|
||||||
|
selection-background-color: {{main_search_selection_bg}};
|
||||||
|
selection-color: {{selected_text_hex}};
|
||||||
|
}
|
||||||
|
QLineEdit#mw_searchbar:focus {
|
||||||
|
background: {{main_search_focus_bg}};
|
||||||
|
border-color: {{main_search_focus_border}};
|
||||||
|
}
|
||||||
|
QComboBox {
|
||||||
|
background-color: {{combo_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{combo_border}};
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 28px 2px 6px;
|
||||||
|
}
|
||||||
|
QComboBox:hover { border-color: {{combo_hover_border}}; }
|
||||||
|
QComboBox:disabled { color: {{disabled_text_hex}}; background-color: {{combo_disabled_bg}}; border-color: {{combo_disabled_border}}; }
|
||||||
|
QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }
|
||||||
|
QComboBox::down-arrow { image: url(":/icons/chevron_down_{{icon_suffix}}.svg"); width: 10px; height: 6px; }
|
||||||
|
QComboBox::down-arrow:on { top: 1px; }
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
background-color: {{combo_popup_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{combo_popup_border}};
|
||||||
|
selection-background-color: {{combo_popup_selection_bg}};
|
||||||
|
selection-color: {{selected_text_hex}};
|
||||||
|
}
|
||||||
|
QSpinBox, QDoubleSpinBox {
|
||||||
|
background-color: {{spin_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{spin_border}};
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 24px 2px 6px;
|
||||||
|
selection-background-color: {{spin_selection_bg}};
|
||||||
|
selection-color: {{selected_text_hex}};
|
||||||
|
}
|
||||||
|
QSpinBox:disabled, QDoubleSpinBox:disabled { color: {{disabled_text_hex}}; background-color: {{spin_disabled_bg}}; border-color: {{spin_disabled_border}}; }
|
||||||
|
QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { image: url(":/icons/chevron_up_{{icon_suffix}}.svg"); width: 10px; height: 6px; }
|
||||||
|
QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { image: url(":/icons/chevron_down_{{icon_suffix}}.svg"); width: 10px; height: 6px; }
|
||||||
|
QCheckBox { color: {{text_hex}}; spacing: 6px; }
|
||||||
|
QCheckBox:disabled { color: {{disabled_text_hex}}; }
|
||||||
|
QRadioButton { color: {{text_hex}}; spacing: 6px; }
|
||||||
|
QRadioButton:disabled { color: {{disabled_text_hex}}; }
|
||||||
|
QDialog#controls_dialog,
|
||||||
|
QDialog[class="settings-window"] { color: {{dialog_text_hex}}; }
|
||||||
|
QDialog#controls_dialog QLabel,
|
||||||
|
QDialog#controls_dialog QCheckBox,
|
||||||
|
QDialog#controls_dialog QRadioButton,
|
||||||
|
QDialog#controls_dialog QGroupBox,
|
||||||
|
QDialog[class="settings-window"] QLabel,
|
||||||
|
QDialog[class="settings-window"] QCheckBox,
|
||||||
|
QDialog[class="settings-window"] QRadioButton,
|
||||||
|
QDialog[class="settings-window"] QGroupBox { color: {{dialog_text_hex}}; background: transparent; }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"] { color: {{dialog_text_hex}}; }
|
||||||
|
QCheckBox::indicator,
|
||||||
|
QGroupBox::indicator,
|
||||||
|
QListView::indicator,
|
||||||
|
QListWidget::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid {{indicator_border}};
|
||||||
|
background-color: {{indicator_bg}};
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:hover,
|
||||||
|
QGroupBox::indicator:hover,
|
||||||
|
QListView::indicator:hover,
|
||||||
|
QListWidget::indicator:hover { border-color: {{indicator_hover_border}}; background-color: {{indicator_hover_bg}}; }
|
||||||
|
QCheckBox::indicator:checked,
|
||||||
|
QCheckBox::indicator:indeterminate,
|
||||||
|
QGroupBox::indicator:checked,
|
||||||
|
QGroupBox::indicator:indeterminate,
|
||||||
|
QListView::indicator:checked,
|
||||||
|
QListView::indicator:indeterminate,
|
||||||
|
QListWidget::indicator:checked,
|
||||||
|
QListWidget::indicator:indeterminate {
|
||||||
|
background-color: {{indicator_checked_bg}};
|
||||||
|
border-color: {{indicator_checked_border}};
|
||||||
|
image: url(":/icons/check_{{check_icon_suffix}}.svg");
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:disabled,
|
||||||
|
QGroupBox::indicator:disabled,
|
||||||
|
QListView::indicator:disabled,
|
||||||
|
QListWidget::indicator:disabled { border-color: {{indicator_disabled_border}}; background-color: {{indicator_disabled_bg}}; }
|
||||||
|
QCheckBox::indicator:checked:disabled,
|
||||||
|
QCheckBox::indicator:indeterminate:disabled,
|
||||||
|
QGroupBox::indicator:checked:disabled,
|
||||||
|
QGroupBox::indicator:indeterminate:disabled,
|
||||||
|
QListView::indicator:checked:disabled,
|
||||||
|
QListView::indicator:indeterminate:disabled,
|
||||||
|
QListWidget::indicator:checked:disabled,
|
||||||
|
QListWidget::indicator:indeterminate:disabled {
|
||||||
|
background-color: {{indicator_checked_disabled_bg}};
|
||||||
|
border-color: {{indicator_checked_disabled_border}};
|
||||||
|
}
|
||||||
|
QRadioButton::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid {{radio_indicator_border}};
|
||||||
|
background-color: {{radio_indicator_bg}};
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:hover { border-color: {{radio_indicator_hover_border}}; background-color: {{radio_indicator_hover_bg}}; }
|
||||||
|
QRadioButton::indicator:checked {
|
||||||
|
background-color: {{radio_checked_bg}};
|
||||||
|
border-color: {{radio_checked_border}};
|
||||||
|
image: url(":/icons/radio_dot_{{check_icon_suffix}}.svg");
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:disabled { border-color: {{indicator_disabled_border}}; background-color: {{indicator_disabled_bg}}; }
|
||||||
|
QRadioButton::indicator:checked:disabled {
|
||||||
|
background-color: {{radio_checked_disabled_bg}};
|
||||||
|
border-color: {{radio_checked_disabled_border}};
|
||||||
|
}
|
||||||
|
QGroupBox {
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{group_border}};
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
padding-top: 0.6em;
|
||||||
|
}
|
||||||
|
QGroupBox:disabled { color: {{disabled_text_hex}}; border-color: {{group_disabled_border}}; }
|
||||||
|
QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: {{text_hex}}; }
|
||||||
|
QLabel { color: {{text_hex}}; background: transparent; }
|
||||||
|
QLabel:disabled { color: {{disabled_text_hex}}; }
|
||||||
|
QDockWidget#apps_list,
|
||||||
|
QDockWidget#logger {
|
||||||
|
background: transparent;
|
||||||
|
color: {{selected_text_hex}};
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list::title,
|
||||||
|
QDockWidget#logger::title {
|
||||||
|
background: {{dock_title_bg}};
|
||||||
|
border: none;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list::close-button,
|
||||||
|
QDockWidget#apps_list::float-button,
|
||||||
|
QDockWidget#logger::close-button,
|
||||||
|
QDockWidget#logger::float-button {
|
||||||
|
background-color: {{dock_title_bg}};
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list::close-button:hover,
|
||||||
|
QDockWidget#apps_list::float-button:hover,
|
||||||
|
QDockWidget#logger::close-button:hover,
|
||||||
|
QDockWidget#logger::float-button:hover {
|
||||||
|
background-color: {{dock_button_hover_bg}};
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list[floating="true"],
|
||||||
|
QDockWidget#logger[floating="true"] {
|
||||||
|
background-color: {{window_hex}};
|
||||||
|
}
|
||||||
|
QWidget#apps_list_frame,
|
||||||
|
QWidget#log_surface {
|
||||||
|
background-color: {{apps_surface_bg}};
|
||||||
|
}
|
||||||
|
QTableWidget#apps_list_table,
|
||||||
|
QTableView#apps_list_table,
|
||||||
|
QWidget#apps_list_viewport,
|
||||||
|
QPlainTextEdit#log_frame,
|
||||||
|
QWidget#log_frame_viewport { background: transparent; }
|
||||||
|
QTableWidget, QTableView {
|
||||||
|
alternate-background-color: {{alt_row_bg}};
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: {{text_hex}};
|
||||||
|
gridline-color: {{table_gridline}};
|
||||||
|
}
|
||||||
|
QTableWidget:disabled, QTableView:disabled { color: {{disabled_text_hex}}; background-color: {{table_disabled_bg}}; }
|
||||||
|
QTableCornerButton::section { background-color: {{table_corner_bg}}; border: 1px solid {{table_corner_border}}; }
|
||||||
|
QTableView::item { border-left: 0.063em solid transparent; border-right: 0.063em solid transparent; padding-left: 0.313em; }
|
||||||
|
QTableView::item:selected { background-color: {{table_selected_bg}}; color: {{selected_text_hex}}; }
|
||||||
|
QTableWidget#apps_list_table::item:selected, QTableView#apps_list_table::item:selected {
|
||||||
|
background-color: {{table_selected_bg}};
|
||||||
|
color: {{selected_text_hex}};
|
||||||
|
border-left: 0.063em solid transparent;
|
||||||
|
border-right: 0.063em solid transparent;
|
||||||
|
}
|
||||||
|
QTableView#apps_list_table QHeaderView,
|
||||||
|
QTableWidget#apps_list_table QHeaderView {
|
||||||
|
background-color: {{header_bg}};
|
||||||
|
}
|
||||||
|
QTableView#apps_list_table QHeaderView::section,
|
||||||
|
QTableWidget#apps_list_table QHeaderView::section {
|
||||||
|
background-color: {{header_bg}};
|
||||||
|
color: {{selected_text_hex}};
|
||||||
|
border: 0.063em solid {{header_border}};
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
QTableView#trophy_app_table::item:selected, QTableView#trophy_table::item:selected {
|
||||||
|
background-color: {{table_selected_bg}};
|
||||||
|
color: {{selected_text_hex}};
|
||||||
|
border-left: 0.063em solid transparent;
|
||||||
|
border-right: 0.063em solid transparent;
|
||||||
|
}
|
||||||
|
QHeaderView::section {
|
||||||
|
background-color: {{header_bg}};
|
||||||
|
color: {{selected_text_hex}};
|
||||||
|
padding-left: .5em;
|
||||||
|
padding-right: .5em;
|
||||||
|
padding-top: .4em;
|
||||||
|
padding-bottom: -.1em;
|
||||||
|
border: 0.063em solid {{header_border}};
|
||||||
|
}
|
||||||
|
QHeaderView::section:hover { background: {{header_hover_bg}}; }
|
||||||
|
QPlainTextEdit#log_frame {
|
||||||
|
color: {{selected_text_hex}};
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QWidget#log_search_container {
|
||||||
|
background-color: {{log_search_bg}};
|
||||||
|
border: 1px solid {{log_search_border}};
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QWidget#toolbar_icon_size_container {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::groove:horizontal {
|
||||||
|
background: {{toolbar_slider_groove_bg}};
|
||||||
|
border: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::sub-page:horizontal {
|
||||||
|
background: {{toolbar_slider_subpage_bg}};
|
||||||
|
border: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::add-page:horizontal {
|
||||||
|
background: {{toolbar_slider_addpage_bg}};
|
||||||
|
border: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal {
|
||||||
|
background: {{toolbar_slider_handle_bg}};
|
||||||
|
border: none;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
margin: -4px 0;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal:hover { background: {{toolbar_slider_handle_hover_bg}}; }
|
||||||
|
QLabel[themeRole="logFatal"] { color: {{log_fatal_hex}}; }
|
||||||
|
QLabel[themeRole="logError"] { color: {{log_error_hex}}; }
|
||||||
|
QLabel[themeRole="logWarning"] { color: {{log_warning_hex}}; }
|
||||||
|
QLabel[themeRole="logNotice"] { color: {{selected_text_hex}}; }
|
||||||
|
QLabel[themeRole="logDebug"] { color: {{log_debug_hex}}; }
|
||||||
|
QLabel[themeRole="logTrace"] { color: {{muted_text_hex}}; }
|
||||||
|
QLabel[themeRole="sectionHeader"] { font-weight: 600; margin-top: 8px; }
|
||||||
|
QLabel[severity="success"] { border-radius: 10px; padding: 4px 10px; background-color: {{severity_success_bg}}; color: {{severity_success_text_hex}}; }
|
||||||
|
QLabel[severity="error"] { border-radius: 10px; padding: 4px 10px; background-color: {{severity_error_bg}}; color: {{severity_error_text_hex}}; }
|
||||||
|
QTabWidget::pane { border: 1px solid {{tab_pane_border}}; background-color: {{tab_pane_bg}}; }
|
||||||
|
QTabBar::tab {
|
||||||
|
background-color: {{tab_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
padding: 6px 14px;
|
||||||
|
border: 1px solid {{tab_border}};
|
||||||
|
border-bottom: none;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
}
|
||||||
|
QTabBar::tab:selected { background-color: {{tab_selected_bg}}; }
|
||||||
|
QTabBar::tab:!selected { margin-top: 2px; }
|
||||||
|
QTabBar::tab:hover:!selected { background-color: {{tab_hover_bg}}; }
|
||||||
|
QListWidget {
|
||||||
|
background-color: transparent;
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{list_border}};
|
||||||
|
}
|
||||||
|
QListWidget:disabled { color: {{disabled_text_hex}}; background-color: {{list_disabled_bg}}; border-color: {{list_disabled_border}}; }
|
||||||
|
QListWidget::item:selected { background-color: {{list_selected_bg}}; color: {{selected_text_hex}}; }
|
||||||
|
QListWidget::item:hover:!selected { background-color: {{list_hover_bg}}; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory {
|
||||||
|
background-color: {{settings_category_bg}};
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:active,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:!active {
|
||||||
|
background-color: {{settings_category_selected_bg}};
|
||||||
|
color: {{dialog_selected_text_hex}};
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:hover:!selected { background-color: {{settings_category_hover_bg}}; }
|
||||||
|
QDialog[class="settings-window"] QTextBrowser#helpText {
|
||||||
|
background-color: {{settings_help_bg}};
|
||||||
|
color: {{dialog_text_hex}};
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#modules_list {
|
||||||
|
background-color: {{modules_list_bg}};
|
||||||
|
color: {{dialog_text_hex}};
|
||||||
|
border: 1px solid {{modules_list_border}};
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#modules_list::item {
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#modules_list::item:hover { background-color: {{modules_list_hover_bg}}; }
|
||||||
|
QDialog[class="settings-window"] QListWidget#modules_list::item:selected { background: transparent; color: {{dialog_text_hex}}; }
|
||||||
|
QScrollBar:vertical { background: {{scrollbar_bg}}; width: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:vertical { background: {{scrollbar_handle_bg}}; min-height: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:vertical:hover { background: {{scrollbar_handle_hover_bg}}; }
|
||||||
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }
|
||||||
|
QScrollBar:horizontal { background: {{scrollbar_bg}}; height: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:horizontal { background: {{scrollbar_handle_bg}}; min-width: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:horizontal:hover { background: {{scrollbar_handle_hover_bg}}; }
|
||||||
|
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }
|
||||||
|
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; }
|
||||||
|
QAbstractScrollArea::corner { background: {{scrollbar_bg}}; }
|
||||||
|
QSlider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::sub-page:horizontal { background: {{slider_subpage_bg}}; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::add-page:horizontal { background: {{slider_addpage_bg}}; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:horizontal { background: {{slider_handle_bg}}; width: 14px; margin: -5px 0; border-radius: 7px; }
|
||||||
|
QSlider::handle:horizontal:hover { background: {{slider_handle_hover_bg}}; }
|
||||||
|
QSlider::groove:vertical { background: transparent; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::sub-page:vertical { background: {{slider_subpage_bg}}; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::add-page:vertical { background: {{slider_addpage_bg}}; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:vertical { background: {{slider_handle_bg}}; height: 14px; margin: 0 -5px; border-radius: 7px; }
|
||||||
|
QSlider::handle:vertical:hover { background: {{slider_handle_hover_bg}}; }
|
||||||
|
QProgressBar {
|
||||||
|
background-color: {{progress_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{progress_border}};
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
QProgressBar::chunk { background-color: {{progress_chunk_bg}}; border-radius: 2px; }
|
||||||
|
QSplitter::handle { background-color: {{splitter_handle_bg}}; }
|
||||||
|
QToolTip {
|
||||||
|
background-color: {{tooltip_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{tooltip_border}};
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
QPlainTextEdit, QTextEdit, QTextBrowser {
|
||||||
|
background-color: {{text_edit_bg}};
|
||||||
|
color: {{text_hex}};
|
||||||
|
border: 1px solid {{text_edit_border}};
|
||||||
|
}
|
||||||
|
QPlainTextEdit:disabled, QTextEdit:disabled, QTextBrowser:disabled {
|
||||||
|
color: {{disabled_text_hex}};
|
||||||
|
background-color: {{text_edit_disabled_bg}};
|
||||||
|
border-color: {{text_edit_disabled_border}};
|
||||||
|
}
|
||||||
|
QDialogButtonBox QPushButton { min-width: 80px; }
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="sky" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#101722"/>
|
||||||
|
<stop offset="45%" stop-color="#1f3953"/>
|
||||||
|
<stop offset="100%" stop-color="#355e7f"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="glow" cx="72%" cy="24%" r="52%">
|
||||||
|
<stop offset="0%" stop-color="#f6b67b" stop-opacity="0.82"/>
|
||||||
|
<stop offset="38%" stop-color="#db8f5e" stop-opacity="0.45"/>
|
||||||
|
<stop offset="100%" stop-color="#db8f5e" stop-opacity="0"/>
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient id="ridge" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#0f2438" stop-opacity="0"/>
|
||||||
|
<stop offset="100%" stop-color="#07111d" stop-opacity="0.86"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="1600" height="900" fill="url(#sky)"/>
|
||||||
|
<rect width="1600" height="900" fill="url(#glow)"/>
|
||||||
|
<path d="M0 545 C170 500 305 470 510 520 C720 572 860 567 1055 510 C1248 455 1438 446 1600 498 L1600 900 L0 900 Z" fill="#0b1624" fill-opacity="0.74"/>
|
||||||
|
<path d="M0 618 C220 555 390 552 612 620 C812 680 1010 694 1252 618 C1393 574 1512 553 1600 560 L1600 900 L0 900 Z" fill="#13283d" fill-opacity="0.66"/>
|
||||||
|
<path d="M0 690 C155 654 319 661 500 712 C702 770 933 795 1130 736 C1337 674 1494 652 1600 672 L1600 900 L0 900 Z" fill="url(#ridge)"/>
|
||||||
|
<circle cx="1230" cy="190" r="108" fill="#ffd8b1" fill-opacity="0.22"/>
|
||||||
|
<circle cx="1298" cy="160" r="28" fill="#ffffff" fill-opacity="0.16"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,539 @@
|
|||||||
|
/*!PALETTE
|
||||||
|
* dark: dark
|
||||||
|
* window: #102030
|
||||||
|
* window-text: #f2f3f6
|
||||||
|
* base: #112334
|
||||||
|
* alternate-base: #173047
|
||||||
|
* tooltip-base: #102030
|
||||||
|
* tooltip-text: #f2f3f6
|
||||||
|
* text: #f2f3f6
|
||||||
|
* button: #173047
|
||||||
|
* button-text: #f2f3f6
|
||||||
|
* bright-text: #ffffff
|
||||||
|
* link: #8cd4ff
|
||||||
|
* highlight: #56b2ff
|
||||||
|
* inactive-highlight: #56b2ff
|
||||||
|
* disabled-highlight: #3356b2ff
|
||||||
|
* highlight-text: #061018
|
||||||
|
* inactive-highlight-text: #061018
|
||||||
|
* disabled-highlight-text: #ced7de
|
||||||
|
* placeholder-text: #a9bbc8
|
||||||
|
* disabled-button-text: #788791
|
||||||
|
* disabled-window-text: #788791
|
||||||
|
* disabled-text: #788791
|
||||||
|
*/
|
||||||
|
QWidget { color: #edf3f8; background-color: #102030; }
|
||||||
|
QWidget:disabled { color: #788791; }
|
||||||
|
QMainWindow#main_window {
|
||||||
|
border-image: url("shell_background.svg") 0 0 0 0 stretch stretch;
|
||||||
|
background-color: #102030;
|
||||||
|
}
|
||||||
|
QWidget#mw_frame,
|
||||||
|
QMainWindow#mw_dock_host {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
QMenuBar {
|
||||||
|
background-color: rgba(11, 20, 31, 0.42);
|
||||||
|
color: #eef4f8;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.10);
|
||||||
|
}
|
||||||
|
QMenuBar::item { background: transparent; padding: 4px 8px; }
|
||||||
|
QMenuBar::item:selected { background: rgba(255, 255, 255, 0.11); }
|
||||||
|
QMenu {
|
||||||
|
background-color: rgba(10, 19, 30, 0.95);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
}
|
||||||
|
QMenu::item:selected { background-color: rgba(86, 178, 255, 0.28); color: #ffffff; }
|
||||||
|
QMenu::item:disabled { color: #7f8d97; }
|
||||||
|
QMenu::separator { height: 1px; background: rgba(255, 255, 255, 0.12); margin: 4px 8px; }
|
||||||
|
QToolBar#mw_toolbar {
|
||||||
|
background-color: rgba(8, 17, 27, 0.45);
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.10);
|
||||||
|
}
|
||||||
|
QToolBar#mw_toolbar::separator {
|
||||||
|
background-color: rgba(255, 255, 255, 0.16);
|
||||||
|
width: 0.125em;
|
||||||
|
margin-top: 0.250em;
|
||||||
|
margin-bottom: 0.250em;
|
||||||
|
}
|
||||||
|
QToolBar#mw_toolbar > QWidget { background: transparent; }
|
||||||
|
QToolButton {
|
||||||
|
color: #eef4f8;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
QToolButton:hover { background-color: rgba(255, 255, 255, 0.10); border: 1px solid rgba(255, 255, 255, 0.12); }
|
||||||
|
QToolButton:pressed { background-color: rgba(86, 178, 255, 0.18); }
|
||||||
|
QToolButton:disabled { color: #7f8d97; }
|
||||||
|
QToolButton#welcome_firmware_button {
|
||||||
|
background-color: rgba(9, 18, 29, 0.68);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 4px 32px 4px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
QToolButton#welcome_firmware_button:hover { background-color: rgba(255, 255, 255, 0.10); }
|
||||||
|
QToolButton#welcome_firmware_button:pressed { background-color: rgba(86, 178, 255, 0.18); }
|
||||||
|
QToolButton#welcome_firmware_button::menu-button {
|
||||||
|
subcontrol-origin: padding;
|
||||||
|
subcontrol-position: top right;
|
||||||
|
width: 24px;
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
QLabel[themeRole="toolbarIconTint"] { color: #f2f3f6; }
|
||||||
|
QLabel[themeRole="mutedText"] { color: #9eb2c1; }
|
||||||
|
QPushButton {
|
||||||
|
background-color: rgba(10, 20, 31, 0.66);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 4px 16px;
|
||||||
|
min-height: 1.2em;
|
||||||
|
}
|
||||||
|
QPushButton:hover { background-color: rgba(255, 255, 255, 0.10); border-color: rgba(255, 255, 255, 0.18); }
|
||||||
|
QPushButton:pressed { background-color: rgba(86, 178, 255, 0.20); }
|
||||||
|
QPushButton:disabled { color: #7f8d97; background-color: rgba(8, 15, 24, 0.56); border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QPushButton:default { border-color: rgba(140, 212, 255, 0.60); }
|
||||||
|
QDialog#vita_themes_dialog { color: #eef4f8; }
|
||||||
|
QDialog#vita_themes_dialog QLabel,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox,
|
||||||
|
QDialog#vita_themes_dialog QRadioButton { color: #eef4f8; background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QLabel[themeRole="mutedText"] { color: #9eb2c1; }
|
||||||
|
QDialog#vita_themes_dialog QWidget[themeRole="backgroundCell"] { background: transparent; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox[themeRole="backgroundCellControl"],
|
||||||
|
QDialog#vita_themes_dialog QRadioButton[themeRole="backgroundCellControl"] { spacing: 0px; padding: 0px; margin: 0px; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList {
|
||||||
|
background-color: rgba(10, 19, 30, 0.96);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:selected:!active { background-color: rgba(86, 178, 255, 0.28); color: #ffffff; }
|
||||||
|
QDialog#vita_themes_dialog QListWidget#vitaThemesList::item:hover:!selected { background-color: rgba(255, 255, 255, 0.06); }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList {
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:active,
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:selected:!active { background-color: rgba(86, 178, 255, 0.28); color: #ffffff; }
|
||||||
|
QDialog#vita_themes_dialog QTreeWidget#vitaThemesBackgroundList::item:hover:!selected { background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialog#vita_themes_dialog QTextBrowser#vitaThemesHelpText {
|
||||||
|
background-color: rgba(10, 19, 30, 0.96);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QScrollArea { background: transparent; border: none; }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.20);
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:hover { border-color: rgba(140, 212, 255, 0.54); background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate {
|
||||||
|
background-color: rgba(140, 212, 255, 0.86);
|
||||||
|
border-color: rgba(140, 212, 255, 0.92);
|
||||||
|
image: url(":/icons/check_dark.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:disabled { border-color: rgba(255, 255, 255, 0.10); background-color: rgba(8, 15, 24, 0.60); }
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:checked:disabled,
|
||||||
|
QDialog#vita_themes_dialog QCheckBox::indicator:indeterminate:disabled {
|
||||||
|
background-color: rgba(140, 212, 255, 0.32);
|
||||||
|
border-color: rgba(140, 212, 255, 0.40);
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.20);
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:hover { border-color: rgba(140, 212, 255, 0.54); background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked {
|
||||||
|
background-color: rgba(140, 212, 255, 0.86);
|
||||||
|
border-color: rgba(140, 212, 255, 0.92);
|
||||||
|
image: url(":/icons/radio_dot_dark.svg");
|
||||||
|
}
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:disabled { border-color: rgba(255, 255, 255, 0.10); background-color: rgba(8, 15, 24, 0.60); }
|
||||||
|
QDialog#vita_themes_dialog QRadioButton::indicator:checked:disabled {
|
||||||
|
background-color: rgba(140, 212, 255, 0.32);
|
||||||
|
border-color: rgba(140, 212, 255, 0.40);
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"] {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:hover { background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:checked { background-color: rgba(255, 255, 255, 0.14); }
|
||||||
|
QDialog#controls_dialog QToolButton[modeButton="true"]:pressed { background-color: rgba(255, 255, 255, 0.20); }
|
||||||
|
QDialog#controls_dialog QPushButton {
|
||||||
|
background-color: rgba(9, 18, 29, 0.66);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QPushButton:hover { background-color: rgba(255, 255, 255, 0.10); border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:pressed { background-color: rgba(86, 178, 255, 0.20); border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton:disabled { color: #7f8d97; background-color: rgba(8, 15, 24, 0.56); border: none; }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"] {
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:hover { background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"]:pressed { background-color: rgba(86, 178, 255, 0.18); }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"] {
|
||||||
|
background-color: rgba(140, 212, 255, 0.22);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:hover { background-color: rgba(140, 212, 255, 0.28); }
|
||||||
|
QDialog#controls_dialog QPushButton[bindButton="true"][captureActive="true"]:pressed { background-color: rgba(140, 212, 255, 0.34); }
|
||||||
|
QStatusBar#emulator_status_bar {
|
||||||
|
background-color: rgba(8, 17, 27, 0.52);
|
||||||
|
color: #eef4f8;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.10);
|
||||||
|
}
|
||||||
|
QStatusBar#emulator_status_bar::item { border: none; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 11px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #eef4f8;
|
||||||
|
}
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:hover { border: 1px solid rgba(255, 255, 255, 0.14); background: rgba(255, 255, 255, 0.06); }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button:pressed { border: 1px solid rgba(255, 255, 255, 0.20); background: rgba(86, 178, 255, 0.18); }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_vulkan"] { color: #ff8f7a; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="renderer_opengl"] { color: #7fb2ff; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="high"] { color: #ffd278; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="standard"] { color: #b8c5cf; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="neutral"] { color: #d6e0e7; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="audio"] { color: #8ef0a2; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="muted"] { color: #7f8d97; }
|
||||||
|
QStatusBar#emulator_status_bar QPushButton#status_button[accent="update"] { color: #a3f0b5; }
|
||||||
|
QLineEdit {
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
selection-background-color: rgba(140, 212, 255, 0.50);
|
||||||
|
selection-color: #061018;
|
||||||
|
}
|
||||||
|
QLineEdit:focus { border-color: rgba(140, 212, 255, 0.65); }
|
||||||
|
QLineEdit:disabled { color: #788791; background-color: rgba(8, 15, 24, 0.60); border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QLineEdit#mw_searchbar {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #eef4f8;
|
||||||
|
padding: 5px 12px;
|
||||||
|
selection-background-color: rgba(140, 212, 255, 0.42);
|
||||||
|
selection-color: #061018;
|
||||||
|
}
|
||||||
|
QLineEdit#mw_searchbar:focus {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-color: rgba(140, 212, 255, 0.42);
|
||||||
|
}
|
||||||
|
QComboBox {
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 28px 2px 6px;
|
||||||
|
}
|
||||||
|
QComboBox:hover { border-color: rgba(255, 255, 255, 0.20); }
|
||||||
|
QComboBox:disabled { color: #788791; background-color: rgba(8, 15, 24, 0.60); border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }
|
||||||
|
QComboBox::down-arrow { image: url(":/icons/chevron_down_light.svg"); width: 10px; height: 6px; }
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
background-color: rgba(10, 19, 30, 0.96);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
selection-background-color: rgba(86, 178, 255, 0.28);
|
||||||
|
selection-color: #ffffff;
|
||||||
|
}
|
||||||
|
QSpinBox, QDoubleSpinBox {
|
||||||
|
background-color: rgba(9, 18, 29, 0.72);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 24px 2px 6px;
|
||||||
|
selection-background-color: rgba(140, 212, 255, 0.50);
|
||||||
|
selection-color: #061018;
|
||||||
|
}
|
||||||
|
QSpinBox:disabled, QDoubleSpinBox:disabled { color: #788791; background-color: rgba(8, 15, 24, 0.60); border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 18px; border: none; background: transparent; }
|
||||||
|
QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { image: url(":/icons/chevron_up_light.svg"); width: 10px; height: 6px; }
|
||||||
|
QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { image: url(":/icons/chevron_down_light.svg"); width: 10px; height: 6px; }
|
||||||
|
QCheckBox { color: #eef4f8; spacing: 6px; }
|
||||||
|
QCheckBox:disabled { color: #788791; }
|
||||||
|
QRadioButton { color: #eef4f8; spacing: 6px; }
|
||||||
|
QRadioButton:disabled { color: #788791; }
|
||||||
|
QGroupBox {
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
padding-top: 0.6em;
|
||||||
|
}
|
||||||
|
QGroupBox:disabled { color: #7f8d97; border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: #eef4f8; }
|
||||||
|
QLabel { color: #eef4f8; background: transparent; }
|
||||||
|
QLabel:disabled { color: #788791; }
|
||||||
|
QDockWidget#apps_list,
|
||||||
|
QDockWidget#logger {
|
||||||
|
background: transparent;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list::title,
|
||||||
|
QDockWidget#logger::title {
|
||||||
|
background: rgba(8, 17, 27, 0.62);
|
||||||
|
border: none;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list::close-button,
|
||||||
|
QDockWidget#apps_list::float-button,
|
||||||
|
QDockWidget#logger::close-button,
|
||||||
|
QDockWidget#logger::float-button {
|
||||||
|
background-color: rgba(8, 17, 27, 0.62);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list::close-button:hover,
|
||||||
|
QDockWidget#apps_list::float-button:hover,
|
||||||
|
QDockWidget#logger::close-button:hover,
|
||||||
|
QDockWidget#logger::float-button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
QDockWidget#apps_list[floating="true"],
|
||||||
|
QDockWidget#logger[floating="true"] {
|
||||||
|
border-image: url("shell_background.svg") 0 0 0 0 stretch stretch;
|
||||||
|
background-color: #102030;
|
||||||
|
}
|
||||||
|
QWidget#apps_list_frame,
|
||||||
|
QWidget#log_surface {
|
||||||
|
background-color: rgba(8, 17, 27, 0.38);
|
||||||
|
}
|
||||||
|
QTableWidget#apps_list_table,
|
||||||
|
QWidget#apps_list_viewport,
|
||||||
|
QPlainTextEdit#log_frame,
|
||||||
|
QWidget#log_frame_viewport {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
QTableWidget, QTableView {
|
||||||
|
alternate-background-color: rgba(255, 255, 255, 0.04);
|
||||||
|
background-color: rgba(9, 18, 29, 0.54);
|
||||||
|
border: none;
|
||||||
|
color: #eef4f8;
|
||||||
|
gridline-color: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
QTableWidget:disabled, QTableView:disabled { color: #788791; background-color: rgba(8, 15, 24, 0.56); }
|
||||||
|
QTableCornerButton::section { background-color: rgba(12, 25, 38, 0.88); border: 1px solid rgba(255, 255, 255, 0.10); }
|
||||||
|
QTableView::item { border-left: 0.063em solid transparent; border-right: 0.063em solid transparent; padding-left: 0.313em; }
|
||||||
|
QTableView::item:selected { background-color: rgba(86, 178, 255, 0.20); color: #ffffff; }
|
||||||
|
QTableWidget#apps_list_table::item:selected, QTableView#apps_list_table::item:selected {
|
||||||
|
background-color: rgba(86, 178, 255, 0.22);
|
||||||
|
color: #f7fbfe;
|
||||||
|
border-left: 0.063em solid transparent;
|
||||||
|
border-right: 0.063em solid transparent;
|
||||||
|
}
|
||||||
|
QTableView#apps_list_table QHeaderView::section, QTableWidget#apps_list_table QHeaderView::section { padding-top: 0; padding-bottom: 0; }
|
||||||
|
QHeaderView::section {
|
||||||
|
background-color: rgba(12, 25, 38, 0.84);
|
||||||
|
color: #ffffff;
|
||||||
|
padding-left: .5em;
|
||||||
|
padding-right: .5em;
|
||||||
|
padding-top: .4em;
|
||||||
|
padding-bottom: -.1em;
|
||||||
|
border: 0.063em solid rgba(255, 255, 255, 0.06);
|
||||||
|
}
|
||||||
|
QHeaderView::section:hover { background: rgba(255, 255, 255, 0.10); }
|
||||||
|
QPlainTextEdit#log_frame {
|
||||||
|
color: #f7fbfe;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QWidget#log_search_container {
|
||||||
|
background-color: rgba(8, 17, 27, 0.82);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QWidget#toolbar_icon_size_container {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::groove:horizontal {
|
||||||
|
background: rgba(255, 255, 255, 0.18);
|
||||||
|
border: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::sub-page:horizontal {
|
||||||
|
background: rgba(242, 243, 246, 0.92);
|
||||||
|
border: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::add-page:horizontal {
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
border: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal {
|
||||||
|
background: #f2f3f6;
|
||||||
|
border: none;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
margin: -4px 0;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
QSlider#toolbar_icon_size_slider::handle:horizontal:hover { background: #ffffff; }
|
||||||
|
QLabel[themeRole="logFatal"] { color: #ff9ff3; }
|
||||||
|
QLabel[themeRole="logError"] { color: #ff9a7f; }
|
||||||
|
QLabel[themeRole="logWarning"] { color: #ffd37f; }
|
||||||
|
QLabel[themeRole="logNotice"] { color: #f7fbfe; }
|
||||||
|
QLabel[themeRole="logDebug"] { color: #8cd4ff; }
|
||||||
|
QLabel[themeRole="logTrace"] { color: #bdd0de; }
|
||||||
|
QLabel[themeRole="sectionHeader"] { font-weight: 600; margin-top: 8px; }
|
||||||
|
QLabel[severity="success"] { border-radius: 10px; padding: 4px 10px; background-color: rgba(71, 125, 92, 0.58); color: #d3ffe0; }
|
||||||
|
QLabel[severity="error"] { border-radius: 10px; padding: 4px 10px; background-color: rgba(134, 72, 60, 0.62); color: #ffd5cd; }
|
||||||
|
QTabWidget::pane { border: 1px solid rgba(255, 255, 255, 0.12); background-color: rgba(8, 17, 27, 0.44); }
|
||||||
|
QTabBar::tab {
|
||||||
|
background-color: rgba(9, 18, 29, 0.62);
|
||||||
|
color: #eef4f8;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
border-bottom: none;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
}
|
||||||
|
QTabBar::tab:selected { background-color: rgba(255, 255, 255, 0.10); }
|
||||||
|
QTabBar::tab:!selected { margin-top: 2px; }
|
||||||
|
QTabBar::tab:hover:!selected { background-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QListWidget {
|
||||||
|
background-color: rgba(9, 18, 29, 0.62);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
QListWidget:disabled { color: #788791; background-color: rgba(8, 15, 24, 0.56); border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QListWidget::item:selected { background-color: rgba(86, 178, 255, 0.26); color: #ffffff; }
|
||||||
|
QListWidget::item:hover:!selected { background-color: rgba(255, 255, 255, 0.06); }
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory {
|
||||||
|
background-color: rgba(9, 18, 29, 0.62);
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:active,
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:selected:!active {
|
||||||
|
background-color: rgba(86, 178, 255, 0.26);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QDialog[class="settings-window"] QListWidget#settingsCategory::item:hover:!selected { background-color: rgba(255, 255, 255, 0.06); }
|
||||||
|
QDialog[class="settings-window"] QTextBrowser#helpText {
|
||||||
|
background-color: rgba(9, 18, 29, 0.62);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
QScrollBar:vertical { background: rgba(9, 18, 29, 0.52); width: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:vertical { background: rgba(255, 255, 255, 0.20); min-height: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:vertical:hover { background: rgba(255, 255, 255, 0.28); }
|
||||||
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }
|
||||||
|
QScrollBar:horizontal { background: rgba(9, 18, 29, 0.52); height: 14px; margin: 0; }
|
||||||
|
QScrollBar::handle:horizontal { background: rgba(255, 255, 255, 0.20); min-width: 20px; border-radius: 3px; margin: 2px; }
|
||||||
|
QScrollBar::handle:horizontal:hover { background: rgba(255, 255, 255, 0.28); }
|
||||||
|
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }
|
||||||
|
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; }
|
||||||
|
QAbstractScrollArea::corner { background: rgba(9, 18, 29, 0.52); }
|
||||||
|
QSlider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::sub-page:horizontal { background: rgba(242, 243, 246, 0.88); height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::add-page:horizontal { background: rgba(255, 255, 255, 0.20); height: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:horizontal { background: #ced7de; width: 14px; margin: -5px 0; border-radius: 7px; }
|
||||||
|
QSlider::handle:horizontal:hover { background: #ffffff; }
|
||||||
|
QSlider::groove:vertical { background: transparent; width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::sub-page:vertical { background: rgba(242, 243, 246, 0.88); width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::add-page:vertical { background: rgba(255, 255, 255, 0.20); width: 4px; border-radius: 2px; }
|
||||||
|
QSlider::handle:vertical { background: #ced7de; height: 14px; margin: 0 -5px; border-radius: 7px; }
|
||||||
|
QSlider::handle:vertical:hover { background: #ffffff; }
|
||||||
|
QProgressBar {
|
||||||
|
background-color: rgba(9, 18, 29, 0.70);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
QProgressBar::chunk { background-color: #8cd4ff; border-radius: 2px; }
|
||||||
|
QSplitter::handle { background-color: rgba(255, 255, 255, 0.14); }
|
||||||
|
QToolTip {
|
||||||
|
background-color: rgba(10, 19, 30, 0.96);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
QPlainTextEdit, QTextEdit, QTextBrowser {
|
||||||
|
background-color: rgba(9, 18, 29, 0.62);
|
||||||
|
color: #eef4f8;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
QPlainTextEdit:disabled, QTextEdit:disabled, QTextBrowser:disabled { color: #788791; background-color: rgba(8, 15, 24, 0.56); border-color: rgba(255, 255, 255, 0.08); }
|
||||||
|
QDialogButtonBox QPushButton { min-width: 80px; }
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <gui-qt/apps_list.h>
|
#include <gui-qt/apps_list.h>
|
||||||
#include <gui-qt/apps_list_table.h>
|
#include <gui-qt/apps_list_table.h>
|
||||||
#include <gui-qt/gui_settings.h>
|
#include <gui-qt/gui_settings.h>
|
||||||
|
#include <gui-qt/theme_surface.h>
|
||||||
|
|
||||||
#include <app/functions.h>
|
#include <app/functions.h>
|
||||||
#include <emuenv/state.h>
|
#include <emuenv/state.h>
|
||||||
@@ -33,15 +34,17 @@
|
|||||||
AppsList::AppsList(EmuEnvState &emuenv, QWidget *parent)
|
AppsList::AppsList(EmuEnvState &emuenv, QWidget *parent)
|
||||||
: custom_dock_widget(tr("App Library"), parent)
|
: custom_dock_widget(tr("App Library"), parent)
|
||||||
, m_emuenv(emuenv) {
|
, m_emuenv(emuenv) {
|
||||||
setObjectName(QStringLiteral("AppsList"));
|
setObjectName(QStringLiteral("apps_list"));
|
||||||
setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
||||||
|
|
||||||
auto *central = new QWidget(this);
|
auto *central = new ThemeSurface(this);
|
||||||
|
central->setObjectName(QStringLiteral("apps_list_frame"));
|
||||||
auto *vbox = new QVBoxLayout(central);
|
auto *vbox = new QVBoxLayout(central);
|
||||||
vbox->setContentsMargins(0, 4, 0, 0);
|
vbox->setContentsMargins(0, 4, 0, 0);
|
||||||
vbox->setSpacing(4);
|
vbox->setSpacing(4);
|
||||||
|
|
||||||
m_table = new AppsListTable(central);
|
m_table = new AppsListTable(central);
|
||||||
|
m_table->viewport()->setObjectName(QStringLiteral("apps_list_viewport"));
|
||||||
vbox->addWidget(m_table);
|
vbox->addWidget(m_table);
|
||||||
|
|
||||||
setWidget(central);
|
setWidget(central);
|
||||||
|
|||||||
@@ -72,10 +72,7 @@ QLabel *make_muted_label(const QString &text, QWidget *parent, Qt::Alignment ali
|
|||||||
auto *label = new QLabel(text, parent);
|
auto *label = new QLabel(text, parent);
|
||||||
label->setAlignment(alignment);
|
label->setAlignment(alignment);
|
||||||
label->setWordWrap(true);
|
label->setWordWrap(true);
|
||||||
const QColor color = gui::utils::get_label_color(QColor(0x5b, 0x5b, 0x5b), QColor(0xa8, 0xa8, 0xa8));
|
label->setProperty("themeRole", QStringLiteral("mutedText"));
|
||||||
QPalette palette = label->palette();
|
|
||||||
palette.setColor(QPalette::WindowText, color);
|
|
||||||
label->setPalette(palette);
|
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +106,7 @@ QFrame *make_result_row(const ArchiveInstallResult &result, QWidget *parent) {
|
|||||||
status->setAlignment(Qt::AlignCenter);
|
status->setAlignment(Qt::AlignCenter);
|
||||||
status->setMinimumWidth(84);
|
status->setMinimumWidth(84);
|
||||||
status->setMargin(6);
|
status->setMargin(6);
|
||||||
const QColor status_bg = result.success
|
status->setProperty("severity", result.success ? QStringLiteral("success") : QStringLiteral("error"));
|
||||||
? gui::utils::get_label_color(QColor(0xd9, 0xf2, 0xdd), QColor(0x25, 0x5a, 0x2e))
|
|
||||||
: gui::utils::get_label_color(QColor(0xf7, 0xdc, 0xd9), QColor(0x6d, 0x2a, 0x25));
|
|
||||||
const QColor status_fg = result.success
|
|
||||||
? gui::utils::get_label_color(QColor(0x1f, 0x7a, 0x1f), QColor(0xb9, 0xf5, 0xbf))
|
|
||||||
: gui::utils::get_label_color(QColor(0xa3, 0x2a, 0x1c), QColor(0xff, 0xb1, 0xaa));
|
|
||||||
status->setStyleSheet(QStringLiteral("border-radius: 10px; padding: 4px 10px; background-color: %1; color: %2;").arg(status_bg.name(), status_fg.name()));
|
|
||||||
layout->addWidget(status, 0, Qt::AlignTop);
|
layout->addWidget(status, 0, Qt::AlignTop);
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
#include <SDL3/SDL_gamepad.h>
|
#include <SDL3/SDL_gamepad.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -67,22 +66,6 @@ constexpr int STACK_PAGE_MIN_WIDTH = 980;
|
|||||||
constexpr int CAPTURE_TIMEOUT_MS = 5'000;
|
constexpr int CAPTURE_TIMEOUT_MS = 5'000;
|
||||||
constexpr int16_t AXIS_DEADZONE = 16000;
|
constexpr int16_t AXIS_DEADZONE = 16000;
|
||||||
|
|
||||||
QColor mix_colors(const QColor &a, const QColor &b, float amount_b) {
|
|
||||||
const float amount_a = 1.0f - amount_b;
|
|
||||||
return QColor(
|
|
||||||
std::lround(a.red() * amount_a + b.red() * amount_b),
|
|
||||||
std::lround(a.green() * amount_a + b.green() * amount_b),
|
|
||||||
std::lround(a.blue() * amount_a + b.blue() * amount_b),
|
|
||||||
std::lround(a.alpha() * amount_a + b.alpha() * amount_b));
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon theme_icon(QWidget *widget, const QString &theme_name, QStyle::StandardPixmap fallback) {
|
|
||||||
QIcon icon = QIcon::fromTheme(theme_name);
|
|
||||||
if (icon.isNull() && widget)
|
|
||||||
icon = widget->style()->standardIcon(fallback);
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPixmap render_vita_svg(const QColor &color) {
|
QPixmap render_vita_svg(const QColor &color) {
|
||||||
QSvgRenderer renderer(QStringLiteral(":/icons/PSV_Layout.svg"));
|
QSvgRenderer renderer(QStringLiteral(":/icons/PSV_Layout.svg"));
|
||||||
QPixmap px(renderer.defaultSize() * 4);
|
QPixmap px(renderer.defaultSize() * 4);
|
||||||
@@ -112,51 +95,22 @@ QLabel *make_wrapped_label(const QString &text, QWidget *parent = nullptr) {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton *make_header_tool_button(QWidget *parent, const QString &text, const QString &theme_name,
|
QToolButton *make_header_tool_button(QWidget *parent, const QString &text) {
|
||||||
QStyle::StandardPixmap fallback) {
|
|
||||||
Q_UNUSED(theme_name);
|
|
||||||
Q_UNUSED(fallback);
|
|
||||||
auto *button = new QToolButton(parent);
|
auto *button = new QToolButton(parent);
|
||||||
button->setText(text);
|
button->setText(text);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
button->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||||
button->setAutoRaise(true);
|
button->setAutoRaise(true);
|
||||||
|
button->setProperty("modeButton", true);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
void refresh_widget_style(QWidget *widget) {
|
|
||||||
if (!widget)
|
|
||||||
return;
|
|
||||||
widget->style()->unpolish(widget);
|
|
||||||
widget->style()->polish(widget);
|
|
||||||
widget->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void style_mode_button(QToolButton *button) {
|
|
||||||
if (!button)
|
|
||||||
return;
|
|
||||||
const QPalette pal = button->palette();
|
|
||||||
const QColor base = pal.color(QPalette::Base);
|
|
||||||
const QColor hover = mix_colors(base, pal.color(QPalette::Highlight), 0.10f);
|
|
||||||
const QColor checked = mix_colors(base, pal.color(QPalette::Highlight), 0.18f);
|
|
||||||
button->setStyleSheet(QStringLiteral(
|
|
||||||
"QToolButton {"
|
|
||||||
" background: transparent;"
|
|
||||||
" border: none;"
|
|
||||||
" border-radius: 10px;"
|
|
||||||
" padding: 6px 12px;"
|
|
||||||
" }"
|
|
||||||
"QToolButton:hover { background-color: %1; }"
|
|
||||||
"QToolButton:checked { background-color: %2; }")
|
|
||||||
.arg(hover.name(QColor::HexArgb), checked.name(QColor::HexArgb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void style_binding_button(QPushButton *button, bool capturing = false) {
|
void style_binding_button(QPushButton *button, bool capturing = false) {
|
||||||
if (!button)
|
if (!button)
|
||||||
return;
|
return;
|
||||||
button->setProperty("bindButton", true);
|
button->setProperty("bindButton", true);
|
||||||
button->setProperty("captureActive", capturing);
|
button->setProperty("captureActive", capturing);
|
||||||
refresh_widget_style(button);
|
gui::utils::refresh_theme_state(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton *make_bind_button() {
|
QPushButton *make_bind_button() {
|
||||||
@@ -375,6 +329,7 @@ ControlsDialog::ControlsDialog(EmuEnvState &emuenv,
|
|||||||
, m_gui_settings(std::move(gui_settings))
|
, m_gui_settings(std::move(gui_settings))
|
||||||
, m_ui(std::make_unique<Ui::ControlsDialog>()) {
|
, m_ui(std::make_unique<Ui::ControlsDialog>()) {
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
setObjectName(QStringLiteral("controls_dialog"));
|
||||||
setWindowTitle(tr("Controls"));
|
setWindowTitle(tr("Controls"));
|
||||||
setWindowModality(Qt::NonModal);
|
setWindowModality(Qt::NonModal);
|
||||||
setWindowFlag(Qt::Window, true);
|
setWindowFlag(Qt::Window, true);
|
||||||
@@ -425,6 +380,13 @@ ControlsDialog::~ControlsDialog() {
|
|||||||
cancel_controller_capture();
|
cancel_controller_capture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlsDialog::changeEvent(QEvent *event) {
|
||||||
|
if (event->type() == QEvent::PaletteChange || event->type() == QEvent::StyleChange)
|
||||||
|
refresh_controller_art();
|
||||||
|
|
||||||
|
QDialog::changeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
void ControlsDialog::closeEvent(QCloseEvent *event) {
|
void ControlsDialog::closeEvent(QCloseEvent *event) {
|
||||||
if (m_gui_settings) {
|
if (m_gui_settings) {
|
||||||
m_gui_settings->set_value(gui::cd_geometry, saveGeometry(), false);
|
m_gui_settings->set_value(gui::cd_geometry, saveGeometry(), false);
|
||||||
@@ -542,12 +504,8 @@ void ControlsDialog::build_keyboard_page() {
|
|||||||
header_layout->addWidget(m_keyboard_page.lbl_summary, 1);
|
header_layout->addWidget(m_keyboard_page.lbl_summary, 1);
|
||||||
|
|
||||||
auto *view_layout = new QHBoxLayout();
|
auto *view_layout = new QHBoxLayout();
|
||||||
m_keyboard_page.btn_primary = make_header_tool_button(header_group, tr("Primary"),
|
m_keyboard_page.btn_primary = make_header_tool_button(header_group, tr("Primary"));
|
||||||
QStringLiteral("input-keyboard"), QStyle::SP_ArrowRight);
|
m_keyboard_page.btn_alt = make_header_tool_button(header_group, tr("Alternate"));
|
||||||
m_keyboard_page.btn_alt = make_header_tool_button(header_group, tr("Alternate"),
|
|
||||||
QStringLiteral("input-keyboard-shortcut"), QStyle::SP_FileDialogDetailedView);
|
|
||||||
style_mode_button(m_keyboard_page.btn_primary);
|
|
||||||
style_mode_button(m_keyboard_page.btn_alt);
|
|
||||||
view_layout->addWidget(m_keyboard_page.btn_primary);
|
view_layout->addWidget(m_keyboard_page.btn_primary);
|
||||||
view_layout->addWidget(m_keyboard_page.btn_alt);
|
view_layout->addWidget(m_keyboard_page.btn_alt);
|
||||||
header_layout->addLayout(view_layout);
|
header_layout->addLayout(view_layout);
|
||||||
@@ -561,7 +519,7 @@ void ControlsDialog::build_keyboard_page() {
|
|||||||
m_keyboard_page.stack = new QStackedWidget(m_keyboard_page.page);
|
m_keyboard_page.stack = new QStackedWidget(m_keyboard_page.page);
|
||||||
root_layout->addWidget(m_keyboard_page.stack, 1);
|
root_layout->addWidget(m_keyboard_page.stack, 1);
|
||||||
|
|
||||||
const QPixmap vita_pixmap = render_vita_svg(gui::utils::get_foreground_color(this));
|
const QPixmap vita_pixmap = render_vita_svg(gui::utils::foreground_color(this));
|
||||||
|
|
||||||
{
|
{
|
||||||
QWidget *content_widget = nullptr;
|
QWidget *content_widget = nullptr;
|
||||||
@@ -705,10 +663,8 @@ void ControlsDialog::build_controller_page(int index) {
|
|||||||
header_layout->addWidget(t.lbl_summary, 1);
|
header_layout->addWidget(t.lbl_summary, 1);
|
||||||
|
|
||||||
auto *view_layout = new QHBoxLayout();
|
auto *view_layout = new QHBoxLayout();
|
||||||
t.btn_bindings = make_header_tool_button(header_group, tr("Bindings"), QStringLiteral("controller-digital-line"), QStyle::SP_ArrowRight);
|
t.btn_bindings = make_header_tool_button(header_group, tr("Bindings"));
|
||||||
t.btn_settings = make_header_tool_button(header_group, tr("Settings"), QStringLiteral("settings-3-line"), QStyle::SP_FileDialogDetailedView);
|
t.btn_settings = make_header_tool_button(header_group, tr("Settings"));
|
||||||
style_mode_button(t.btn_bindings);
|
|
||||||
style_mode_button(t.btn_settings);
|
|
||||||
view_layout->addWidget(t.btn_bindings);
|
view_layout->addWidget(t.btn_bindings);
|
||||||
view_layout->addWidget(t.btn_settings);
|
view_layout->addWidget(t.btn_settings);
|
||||||
header_layout->addLayout(view_layout);
|
header_layout->addLayout(view_layout);
|
||||||
@@ -750,7 +706,7 @@ void ControlsDialog::build_controller_page(int index) {
|
|||||||
top_grid->addWidget(make_button_group(tr("R1"), t.btn_r1), 0, 3);
|
top_grid->addWidget(make_button_group(tr("R1"), t.btn_r1), 0, 3);
|
||||||
center_column->addLayout(top_grid);
|
center_column->addLayout(top_grid);
|
||||||
|
|
||||||
t.l_controller_image = make_vita_image_label(this, render_vita_svg(gui::utils::get_foreground_color(this)));
|
t.l_controller_image = make_vita_image_label(this, render_vita_svg(gui::utils::foreground_color(this)));
|
||||||
center_column->addWidget(make_centered_widget(t.l_controller_image));
|
center_column->addWidget(make_centered_widget(t.l_controller_image));
|
||||||
center_column->addStretch();
|
center_column->addStretch();
|
||||||
|
|
||||||
@@ -1190,6 +1146,18 @@ void ControlsDialog::refresh_controller_page_labels(int index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlsDialog::refresh_controller_art() {
|
||||||
|
const QPixmap vita_pixmap = render_vita_svg(gui::utils::foreground_color(this));
|
||||||
|
|
||||||
|
if (m_keyboard_page.l_controller_image)
|
||||||
|
m_keyboard_page.l_controller_image->setPixmap(vita_pixmap);
|
||||||
|
|
||||||
|
for (auto &tab : m_ctrl_tabs) {
|
||||||
|
if (tab.l_controller_image)
|
||||||
|
tab.l_controller_image->setPixmap(vita_pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ControlsDialog::set_controller_page_enabled(int index, bool enabled) {
|
void ControlsDialog::set_controller_page_enabled(int index, bool enabled) {
|
||||||
auto &tab = m_ctrl_tabs[index];
|
auto &tab = m_ctrl_tabs[index];
|
||||||
for (auto &binding : tab.bindings)
|
for (auto &binding : tab.bindings)
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ void DebugWidget::on_thread_double_clicked(QTreeWidgetItem *item, int /*column*/
|
|||||||
layout->addLayout(form);
|
layout->addLayout(form);
|
||||||
|
|
||||||
auto *reg_label = new QLabel(tr("Registers"), dlg);
|
auto *reg_label = new QLabel(tr("Registers"), dlg);
|
||||||
reg_label->setStyleSheet(QStringLiteral("font-weight: bold; margin-top: 8px;"));
|
reg_label->setProperty("themeRole", QStringLiteral("sectionHeader"));
|
||||||
layout->addWidget(reg_label);
|
layout->addWidget(reg_label);
|
||||||
|
|
||||||
auto *reg_tree = new QTreeWidget(dlg);
|
auto *reg_tree = new QTreeWidget(dlg);
|
||||||
@@ -432,7 +432,7 @@ void DebugWidget::on_thread_double_clicked(QTreeWidgetItem *item, int /*column*/
|
|||||||
layout->addWidget(reg_tree);
|
layout->addWidget(reg_tree);
|
||||||
|
|
||||||
auto *stack_label = new QLabel(tr("Stack"), dlg);
|
auto *stack_label = new QLabel(tr("Stack"), dlg);
|
||||||
stack_label->setStyleSheet(QStringLiteral("font-weight: bold; margin-top: 8px;"));
|
stack_label->setProperty("themeRole", QStringLiteral("sectionHeader"));
|
||||||
layout->addWidget(stack_label);
|
layout->addWidget(stack_label);
|
||||||
|
|
||||||
auto *stack_tree = new QTreeWidget(dlg);
|
auto *stack_tree = new QTreeWidget(dlg);
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
#include <gui-qt/gui_settings.h>
|
#include <gui-qt/gui_settings.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
|
||||||
|
|
||||||
GuiSettings::GuiSettings(const QString &settings_dir, QObject *parent)
|
GuiSettings::GuiSettings(const QString &settings_dir, QObject *parent)
|
||||||
: GuiSettingsBase(parent) {
|
: GuiSettingsBase(parent) {
|
||||||
m_settings_dir = QDir(settings_dir);
|
m_settings_dir = QDir(settings_dir);
|
||||||
@@ -30,20 +28,3 @@ GuiSettings::GuiSettings(const QString &settings_dir, QObject *parent)
|
|||||||
m_settings_dir.filePath(gui::Settings + QStringLiteral(".ini")),
|
m_settings_dir.filePath(gui::Settings + QStringLiteral(".ini")),
|
||||||
QSettings::IniFormat, parent);
|
QSettings::IniFormat, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList GuiSettings::get_stylesheet_entries() const {
|
|
||||||
const QStringList name_filter = QStringList(QStringLiteral("*.qss"));
|
|
||||||
QStringList res;
|
|
||||||
|
|
||||||
{
|
|
||||||
QDirIterator it(m_settings_dir.absolutePath(), name_filter, QDir::Files);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next();
|
|
||||||
res.append(it.fileInfo().completeBaseName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.removeDuplicates();
|
|
||||||
res.sort();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,11 +17,13 @@
|
|||||||
|
|
||||||
#include <gui-qt/log_widget.h>
|
#include <gui-qt/log_widget.h>
|
||||||
#include <gui-qt/qt_utils.h>
|
#include <gui-qt/qt_utils.h>
|
||||||
|
#include <gui-qt/theme_surface.h>
|
||||||
|
|
||||||
#include <util/log.h>
|
#include <util/log.h>
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
@@ -34,8 +36,11 @@
|
|||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QTextBlockUserData>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
|
#include <QTimer>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
@@ -47,7 +52,9 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int SAFE_UNBOUNDED_LOG_BUFFER_SIZE = 50000;
|
constexpr int SAFE_UNBOUNDED_LOG_BUFFER_SIZE = 50000;
|
||||||
constexpr size_t MAX_LOG_DRAIN_BATCH = 256;
|
constexpr int LOG_DRAIN_INTERVAL_MS = 10;
|
||||||
|
constexpr qint64 LOG_DRAIN_TIME_BUDGET_MS = 4;
|
||||||
|
constexpr size_t MAX_LOG_DRAIN_BATCH = 64;
|
||||||
constexpr size_t MIN_PENDING_LOG_LINES = 512;
|
constexpr size_t MIN_PENDING_LOG_LINES = 512;
|
||||||
constexpr size_t MAX_PENDING_LOG_LINES = 8192;
|
constexpr size_t MAX_PENDING_LOG_LINES = 8192;
|
||||||
|
|
||||||
@@ -57,6 +64,84 @@ struct PendingLogMessage {
|
|||||||
size_t repeat_count = 1;
|
size_t repeat_count = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LogBlockData final : public QTextBlockUserData {
|
||||||
|
public:
|
||||||
|
explicit LogBlockData(int level_value)
|
||||||
|
: level(level_value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int level = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderedLogMessage {
|
||||||
|
QString msg;
|
||||||
|
int level = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
QColor log_level_color(int level) {
|
||||||
|
using L = spdlog::level::level_enum;
|
||||||
|
|
||||||
|
switch (static_cast<L>(level)) {
|
||||||
|
case L::critical:
|
||||||
|
return gui::utils::theme_role_color(QStringLiteral("logFatal"));
|
||||||
|
case L::err:
|
||||||
|
return gui::utils::theme_role_color(QStringLiteral("logError"));
|
||||||
|
case L::warn:
|
||||||
|
return gui::utils::theme_role_color(QStringLiteral("logWarning"));
|
||||||
|
case L::info:
|
||||||
|
return gui::utils::theme_role_color(QStringLiteral("logNotice"));
|
||||||
|
case L::debug:
|
||||||
|
return gui::utils::theme_role_color(QStringLiteral("logDebug"));
|
||||||
|
case L::trace:
|
||||||
|
default:
|
||||||
|
return gui::utils::theme_role_color(QStringLiteral("logTrace"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_block_color(const QTextBlock &block, int level) {
|
||||||
|
if (!block.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTextCursor cursor(block);
|
||||||
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
|
QTextCharFormat format;
|
||||||
|
format.setForeground(log_level_color(level));
|
||||||
|
cursor.mergeCharFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_log_entries(QPlainTextEdit *log, const std::vector<RenderedLogMessage> &entries, bool preserve_scroll = true) {
|
||||||
|
if (!log || entries.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QScrollBar *bar = log->verticalScrollBar();
|
||||||
|
const bool at_bottom = !preserve_scroll || bar->value() == bar->maximum();
|
||||||
|
|
||||||
|
QTextCursor cursor(log->document());
|
||||||
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
|
||||||
|
for (const RenderedLogMessage &entry : entries) {
|
||||||
|
const QString normalized = entry.msg;
|
||||||
|
const QStringList lines = normalized.split(QLatin1Char('\n'), Qt::KeepEmptyParts);
|
||||||
|
|
||||||
|
QTextCharFormat format;
|
||||||
|
format.setForeground(log_level_color(entry.level));
|
||||||
|
|
||||||
|
for (const QString &line : lines) {
|
||||||
|
cursor.insertText(line, format);
|
||||||
|
if (QTextBlock block = cursor.block(); block.isValid())
|
||||||
|
block.setUserData(new LogBlockData(entry.level));
|
||||||
|
cursor.insertBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.endEditBlock();
|
||||||
|
|
||||||
|
if (at_bottom)
|
||||||
|
bar->setValue(bar->maximum());
|
||||||
|
}
|
||||||
|
|
||||||
QFont make_log_font(const QString &family = {}) {
|
QFont make_log_font(const QString &family = {}) {
|
||||||
QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||||
font.setPointSize(9);
|
font.setPointSize(9);
|
||||||
@@ -83,7 +168,10 @@ static struct {
|
|||||||
|
|
||||||
LogWidget::LogWidget(QWidget *parent)
|
LogWidget::LogWidget(QWidget *parent)
|
||||||
: custom_dock_widget(tr("Log"), parent) {
|
: custom_dock_widget(tr("Log"), parent) {
|
||||||
auto *container = new QWidget(this);
|
setObjectName(QStringLiteral("logger"));
|
||||||
|
|
||||||
|
auto *container = new ThemeSurface(this);
|
||||||
|
container->setObjectName(QStringLiteral("log_surface"));
|
||||||
auto *layout = new QVBoxLayout(container);
|
auto *layout = new QVBoxLayout(container);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
layout->setSpacing(0);
|
layout->setSpacing(0);
|
||||||
@@ -93,10 +181,11 @@ LogWidget::LogWidget(QWidget *parent)
|
|||||||
m_log->setFont(make_log_font());
|
m_log->setFont(make_log_font());
|
||||||
m_log->document()->setMaximumBlockCount(effective_buffer_size(1000));
|
m_log->document()->setMaximumBlockCount(effective_buffer_size(1000));
|
||||||
m_log->setObjectName(QStringLiteral("log_frame"));
|
m_log->setObjectName(QStringLiteral("log_frame"));
|
||||||
|
m_log->viewport()->setObjectName(QStringLiteral("log_frame_viewport"));
|
||||||
layout->addWidget(m_log);
|
layout->addWidget(m_log);
|
||||||
setWidget(container);
|
setWidget(container);
|
||||||
|
|
||||||
m_search_container = new QWidget(m_log);
|
m_search_container = new ThemeSurface(m_log);
|
||||||
m_search_container->setObjectName(QStringLiteral("log_search_container"));
|
m_search_container->setObjectName(QStringLiteral("log_search_container"));
|
||||||
auto *search_layout = new QHBoxLayout(m_search_container);
|
auto *search_layout = new QHBoxLayout(m_search_container);
|
||||||
search_layout->setContentsMargins(6, 6, 6, 6);
|
search_layout->setContentsMargins(6, 6, 6, 6);
|
||||||
@@ -147,6 +236,10 @@ LogWidget::LogWidget(QWidget *parent)
|
|||||||
connect(m_search_next_button, &QToolButton::clicked, this, [this]() { find_text(false); });
|
connect(m_search_next_button, &QToolButton::clicked, this, [this]() { find_text(false); });
|
||||||
connect(m_search_close_button, &QToolButton::clicked, this, &LogWidget::hide_search_bar);
|
connect(m_search_close_button, &QToolButton::clicked, this, &LogWidget::hide_search_bar);
|
||||||
|
|
||||||
|
m_drain_timer = new QTimer(this);
|
||||||
|
m_drain_timer->setSingleShot(true);
|
||||||
|
connect(m_drain_timer, &QTimer::timeout, this, &LogWidget::drain_pending_messages);
|
||||||
|
|
||||||
update_search_ui_text();
|
update_search_ui_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,15 +302,15 @@ void LogWidget::attach() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LogWidget::on_log_message(const QString &msg, int level) {
|
void LogWidget::on_log_message(const QString &msg, int level) {
|
||||||
m_entries.emplace_back(msg, level);
|
|
||||||
trim_entries();
|
|
||||||
append_log_entry(msg, level);
|
append_log_entry(msg, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogWidget::drain_pending_messages() {
|
void LogWidget::drain_pending_messages() {
|
||||||
|
if (m_drain_timer && m_drain_timer->isActive())
|
||||||
|
m_drain_timer->stop();
|
||||||
|
|
||||||
std::deque<PendingLogMessage> batch;
|
std::deque<PendingLogMessage> batch;
|
||||||
size_t dropped_lines = 0;
|
size_t dropped_lines = 0;
|
||||||
bool should_schedule_next = false;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> lock(s_log_state.mutex);
|
const std::lock_guard<std::mutex> lock(s_log_state.mutex);
|
||||||
@@ -228,95 +321,76 @@ void LogWidget::drain_pending_messages() {
|
|||||||
batch.push_back(std::move(s_log_state.backlog.front()));
|
batch.push_back(std::move(s_log_state.backlog.front()));
|
||||||
s_log_state.backlog.pop_front();
|
s_log_state.backlog.pop_front();
|
||||||
}
|
}
|
||||||
|
s_log_state.drain_posted = false;
|
||||||
should_schedule_next = !s_log_state.backlog.empty();
|
|
||||||
s_log_state.drain_posted = should_schedule_next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<RenderedLogMessage> rendered_entries;
|
||||||
|
rendered_entries.reserve(batch.size() + (dropped_lines > 0 ? 1 : 0));
|
||||||
|
|
||||||
if (dropped_lines > 0) {
|
if (dropped_lines > 0) {
|
||||||
on_log_message(
|
rendered_entries.push_back({ QStringLiteral("[Qt log] Dropped %1 queued log lines before they could be rendered.")
|
||||||
QStringLiteral("[Qt log] Dropped %1 queued log lines before they could be rendered.")
|
.arg(static_cast<qulonglong>(dropped_lines)),
|
||||||
.arg(static_cast<qulonglong>(dropped_lines)),
|
static_cast<int>(spdlog::level::warn) });
|
||||||
static_cast<int>(spdlog::level::warn));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!batch.empty()) {
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
size_t consumed = 0;
|
||||||
|
for (; consumed < batch.size(); ++consumed) {
|
||||||
|
if (consumed > 0 && timer.elapsed() >= LOG_DRAIN_TIME_BUDGET_MS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
QString qmsg = QString::fromStdString(batch[consumed].msg).trimmed();
|
||||||
|
if (qmsg.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
qmsg.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
|
||||||
|
qmsg.replace(QLatin1Char('\r'), QLatin1Char('\n'));
|
||||||
|
|
||||||
|
if (batch[consumed].repeat_count > 1)
|
||||||
|
qmsg += QStringLiteral(" [x%1]").arg(static_cast<qulonglong>(batch[consumed].repeat_count));
|
||||||
|
|
||||||
|
rendered_entries.push_back({ std::move(qmsg), batch[consumed].level });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consumed < batch.size()) {
|
||||||
|
const std::lock_guard<std::mutex> lock(s_log_state.mutex);
|
||||||
|
for (auto it = batch.rbegin(); it != batch.rbegin() + static_cast<std::ptrdiff_t>(batch.size() - consumed); ++it)
|
||||||
|
s_log_state.backlog.push_front(std::move(*it));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rendered_entries.empty()) {
|
||||||
m_log->setUpdatesEnabled(false);
|
m_log->setUpdatesEnabled(false);
|
||||||
for (const auto &entry : batch) {
|
append_log_entries(m_log, rendered_entries);
|
||||||
QString qmsg = QString::fromStdString(entry.msg).trimmed();
|
|
||||||
if (qmsg.isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (entry.repeat_count > 1) {
|
|
||||||
qmsg += QStringLiteral(" [x%1]").arg(static_cast<qulonglong>(entry.repeat_count));
|
|
||||||
}
|
|
||||||
|
|
||||||
on_log_message(qmsg, entry.level);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log->setUpdatesEnabled(true);
|
m_log->setUpdatesEnabled(true);
|
||||||
m_log->viewport()->update();
|
m_log->viewport()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool should_schedule_next = false;
|
||||||
|
{
|
||||||
|
const std::lock_guard<std::mutex> lock(s_log_state.mutex);
|
||||||
|
should_schedule_next = !s_log_state.backlog.empty();
|
||||||
|
s_log_state.drain_posted = should_schedule_next;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_search_container && m_search_container->isVisible() && m_search_edit && !m_search_edit->text().isEmpty())
|
if (m_search_container && m_search_container->isVisible() && m_search_edit && !m_search_edit->text().isEmpty())
|
||||||
update_search_highlights();
|
update_search_highlights();
|
||||||
|
|
||||||
if (should_schedule_next)
|
if (should_schedule_next && m_drain_timer) {
|
||||||
QMetaObject::invokeMethod(this, &LogWidget::drain_pending_messages, Qt::QueuedConnection);
|
m_drain_timer->start(LOG_DRAIN_INTERVAL_MS);
|
||||||
}
|
|
||||||
|
|
||||||
void LogWidget::append_log_entry(const QString &msg, int level, bool preserve_scroll) {
|
|
||||||
QScrollBar *bar = m_log->verticalScrollBar();
|
|
||||||
const bool at_bottom = !preserve_scroll || bar->value() == bar->maximum();
|
|
||||||
|
|
||||||
QTextCursor cursor = m_log->textCursor();
|
|
||||||
cursor.movePosition(QTextCursor::End);
|
|
||||||
|
|
||||||
QTextCharFormat fmt;
|
|
||||||
fmt.setForeground(color_for_level(level));
|
|
||||||
|
|
||||||
cursor.insertText(msg + "\n", fmt);
|
|
||||||
|
|
||||||
if (at_bottom)
|
|
||||||
bar->setValue(bar->maximum());
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor LogWidget::color_for_level(int level) const {
|
|
||||||
using L = spdlog::level::level_enum;
|
|
||||||
|
|
||||||
switch (static_cast<L>(level)) {
|
|
||||||
case L::critical:
|
|
||||||
return gui::utils::get_label_color(
|
|
||||||
QStringLiteral("log_level_fatal"),
|
|
||||||
QColor(QStringLiteral("#ff00ff")),
|
|
||||||
QColor(QStringLiteral("#ff66ff")));
|
|
||||||
case L::err:
|
|
||||||
return gui::utils::get_label_color(
|
|
||||||
QStringLiteral("log_level_error"),
|
|
||||||
QColor(QStringLiteral("#C02F1D")),
|
|
||||||
QColor(QStringLiteral("#ff8080")));
|
|
||||||
case L::warn:
|
|
||||||
return gui::utils::get_label_color(
|
|
||||||
QStringLiteral("log_level_warning"),
|
|
||||||
QColor(QStringLiteral("#BA8745")),
|
|
||||||
QColor(QStringLiteral("#ffd27f")));
|
|
||||||
case L::info:
|
|
||||||
return gui::utils::get_label_color(
|
|
||||||
QStringLiteral("log_level_notice"),
|
|
||||||
QColor(Qt::black),
|
|
||||||
QColor(Qt::white));
|
|
||||||
default:
|
|
||||||
return gui::utils::get_label_color(
|
|
||||||
QStringLiteral("log_level_trace"),
|
|
||||||
QColor(QStringLiteral("#808080")),
|
|
||||||
QColor(Qt::white));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogWidget::trim_entries() {
|
void LogWidget::append_log_entry(const QString &msg, int level, bool preserve_scroll) {
|
||||||
const int max_blocks = m_log->document()->maximumBlockCount();
|
QString normalized = msg.trimmed();
|
||||||
while (max_blocks > 0 && static_cast<int>(m_entries.size()) > max_blocks)
|
if (normalized.isEmpty())
|
||||||
m_entries.pop_front();
|
return;
|
||||||
|
|
||||||
|
normalized.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
|
||||||
|
normalized.replace(QLatin1Char('\r'), QLatin1Char('\n'));
|
||||||
|
|
||||||
|
append_log_entries(m_log, { RenderedLogMessage{ std::move(normalized), level } }, preserve_scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogWidget::set_log_buffer_size(int size) {
|
void LogWidget::set_log_buffer_size(int size) {
|
||||||
@@ -329,7 +403,6 @@ void LogWidget::set_log_buffer_size(int size) {
|
|||||||
s_log_state.backlog.pop_front();
|
s_log_state.backlog.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trim_entries();
|
|
||||||
if (m_search_edit && !m_search_edit->text().isEmpty())
|
if (m_search_edit && !m_search_edit->text().isEmpty())
|
||||||
update_search_highlights();
|
update_search_highlights();
|
||||||
}
|
}
|
||||||
@@ -354,14 +427,23 @@ QString LogWidget::log_font_family() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LogWidget::repaint_text_colors() {
|
void LogWidget::repaint_text_colors() {
|
||||||
|
if (!m_log)
|
||||||
|
return;
|
||||||
|
|
||||||
const QScrollBar *bar = m_log->verticalScrollBar();
|
const QScrollBar *bar = m_log->verticalScrollBar();
|
||||||
const int old_value = bar->value();
|
const int old_value = bar->value();
|
||||||
const bool at_bottom = old_value == bar->maximum();
|
const bool at_bottom = old_value == bar->maximum();
|
||||||
|
|
||||||
m_log->clear();
|
m_log->setUpdatesEnabled(false);
|
||||||
|
for (QTextBlock block = m_log->document()->begin(); block.isValid(); block = block.next()) {
|
||||||
|
const auto *data = static_cast<const LogBlockData *>(block.userData());
|
||||||
|
if (!data)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (const auto &[msg, level] : m_entries)
|
apply_block_color(block, data->level);
|
||||||
append_log_entry(msg, level, false);
|
}
|
||||||
|
m_log->setUpdatesEnabled(true);
|
||||||
|
m_log->viewport()->update();
|
||||||
|
|
||||||
if (!at_bottom)
|
if (!at_bottom)
|
||||||
m_log->verticalScrollBar()->setValue(old_value);
|
m_log->verticalScrollBar()->setValue(old_value);
|
||||||
@@ -521,7 +603,7 @@ void LogWidget::update_search_ui_icons() {
|
|||||||
if (!m_search_prev_button || !m_search_next_button || !m_search_close_button)
|
if (!m_search_prev_button || !m_search_next_button || !m_search_close_button)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QColor foreground = gui::utils::get_foreground_color(m_search_container);
|
const QColor foreground = gui::utils::foreground_color(m_search_container);
|
||||||
const QString theme_suffix = foreground.lightnessF() > 0.5f
|
const QString theme_suffix = foreground.lightnessF() > 0.5f
|
||||||
? QStringLiteral("light")
|
? QStringLiteral("light")
|
||||||
: QStringLiteral("dark");
|
: QStringLiteral("dark");
|
||||||
|
|||||||
+214
-126
@@ -39,10 +39,12 @@
|
|||||||
#include <gui-qt/qt_utils.h>
|
#include <gui-qt/qt_utils.h>
|
||||||
#include <gui-qt/settings_dialog.h>
|
#include <gui-qt/settings_dialog.h>
|
||||||
#include <gui-qt/settings_dialog_tooltips.h>
|
#include <gui-qt/settings_dialog_tooltips.h>
|
||||||
#include <gui-qt/stylesheets.h>
|
#include <gui-qt/theme_manager.h>
|
||||||
|
#include <gui-qt/theme_surface.h>
|
||||||
#include <gui-qt/trophy_collection_dialog.h>
|
#include <gui-qt/trophy_collection_dialog.h>
|
||||||
#include <gui-qt/update_manager.h>
|
#include <gui-qt/update_manager.h>
|
||||||
#include <gui-qt/user_management_dialog.h>
|
#include <gui-qt/user_management_dialog.h>
|
||||||
|
#include <gui-qt/vita_themes_dialog.h>
|
||||||
#include <gui-qt/welcome_dialog.h>
|
#include <gui-qt/welcome_dialog.h>
|
||||||
|
|
||||||
#include <app/functions.h>
|
#include <app/functions.h>
|
||||||
@@ -90,7 +92,7 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <QPalette>
|
#include <QPainter>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QSlider>
|
#include <QSlider>
|
||||||
@@ -99,6 +101,7 @@
|
|||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
|
#include <QVariantAnimation>
|
||||||
#include <QWidgetAction>
|
#include <QWidgetAction>
|
||||||
#include <QtResource>
|
#include <QtResource>
|
||||||
|
|
||||||
@@ -127,35 +130,6 @@ void copy_config_for_edit(Config &dst, const Config &src) {
|
|||||||
dst.current_config = src.current_config;
|
dst.current_config = src.current_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPalette make_dark_palette() {
|
|
||||||
QPalette palette;
|
|
||||||
const QColor selection_highlight(255, 255, 255, 33);
|
|
||||||
const QColor disabled_selection_highlight(255, 255, 255, 20);
|
|
||||||
|
|
||||||
palette.setColor(QPalette::Window, QColor(45, 45, 48));
|
|
||||||
palette.setColor(QPalette::WindowText, QColor(243, 243, 243));
|
|
||||||
palette.setColor(QPalette::Base, QColor(30, 30, 30));
|
|
||||||
palette.setColor(QPalette::AlternateBase, QColor(38, 38, 41));
|
|
||||||
palette.setColor(QPalette::ToolTipBase, QColor(45, 45, 48));
|
|
||||||
palette.setColor(QPalette::ToolTipText, QColor(243, 243, 243));
|
|
||||||
palette.setColor(QPalette::Text, QColor(243, 243, 243));
|
|
||||||
palette.setColor(QPalette::Button, QColor(45, 45, 48));
|
|
||||||
palette.setColor(QPalette::ButtonText, QColor(243, 243, 243));
|
|
||||||
palette.setColor(QPalette::BrightText, Qt::white);
|
|
||||||
palette.setColor(QPalette::Link, QColor(66, 156, 255));
|
|
||||||
palette.setColor(QPalette::Highlight, selection_highlight);
|
|
||||||
palette.setColor(QPalette::Inactive, QPalette::Highlight, selection_highlight);
|
|
||||||
palette.setColor(QPalette::Disabled, QPalette::Highlight, disabled_selection_highlight);
|
|
||||||
palette.setColor(QPalette::HighlightedText, Qt::white);
|
|
||||||
palette.setColor(QPalette::Inactive, QPalette::HighlightedText, Qt::white);
|
|
||||||
palette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(200, 200, 200));
|
|
||||||
palette.setColor(QPalette::PlaceholderText, QColor(160, 160, 160));
|
|
||||||
palette.setColor(QPalette::Disabled, QPalette::Text, QColor(115, 115, 115));
|
|
||||||
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(115, 115, 115));
|
|
||||||
palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(115, 115, 115));
|
|
||||||
return palette;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_status_button_accent(QPushButton *button, const QString &accent) {
|
void update_status_button_accent(QPushButton *button, const QString &accent) {
|
||||||
if (!button)
|
if (!button)
|
||||||
return;
|
return;
|
||||||
@@ -165,11 +139,8 @@ void update_status_button_accent(QPushButton *button, const QString &accent) {
|
|||||||
|
|
||||||
if (button->property("accent").toString() != accent) {
|
if (button->property("accent").toString() != accent) {
|
||||||
button->setProperty("accent", accent);
|
button->setProperty("accent", accent);
|
||||||
button->style()->unpolish(button);
|
gui::utils::refresh_theme_state(button);
|
||||||
button->style()->polish(button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button->update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -212,9 +183,6 @@ void MainWindow::register_auxiliary_window(QWidget *window) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_auxiliary_windows.push_back(window);
|
m_auxiliary_windows.push_back(window);
|
||||||
connect(window, &QObject::destroyed, this, [this](QObject *) {
|
|
||||||
prune_auxiliary_windows();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::prune_auxiliary_windows() {
|
void MainWindow::prune_auxiliary_windows() {
|
||||||
@@ -287,6 +255,39 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
|||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::paintEvent(QPaintEvent *event) {
|
||||||
|
QMainWindow::paintEvent(event);
|
||||||
|
|
||||||
|
if (m_theme_background_scaled_pixmaps.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_theme_background_scaled_size != size())
|
||||||
|
rescale_theme_background_pixmaps();
|
||||||
|
if (m_theme_background_scaled_pixmaps.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int current_index = std::clamp(m_theme_background_current_index, 0,
|
||||||
|
static_cast<int>(m_theme_background_scaled_pixmaps.size()) - 1);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||||
|
|
||||||
|
const auto draw_index = [this, &painter](const int index, const qreal opacity) {
|
||||||
|
if (index < 0 || index >= static_cast<int>(m_theme_background_scaled_pixmaps.size()) || opacity <= 0.0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
painter.setOpacity(opacity);
|
||||||
|
painter.drawPixmap(rect(), m_theme_background_scaled_pixmaps[static_cast<std::size_t>(index)]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_theme_background_next_index >= 0 && m_theme_background_next_index < static_cast<int>(m_theme_background_scaled_pixmaps.size())) {
|
||||||
|
draw_index(current_index, 1.0 - m_theme_background_fade_progress);
|
||||||
|
draw_index(m_theme_background_next_index, m_theme_background_fade_progress);
|
||||||
|
} else {
|
||||||
|
draw_index(current_index, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::save_window_state() {
|
void MainWindow::save_window_state() {
|
||||||
if (!m_gui_settings)
|
if (!m_gui_settings)
|
||||||
return;
|
return;
|
||||||
@@ -374,6 +375,7 @@ void MainWindow::restore_window_state() {
|
|||||||
void MainWindow::initialize() {
|
void MainWindow::initialize() {
|
||||||
Q_INIT_RESOURCE(resources);
|
Q_INIT_RESOURCE(resources);
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
setObjectName(QStringLiteral("main_window"));
|
||||||
|
|
||||||
this->resize(1280, 720);
|
this->resize(1280, 720);
|
||||||
this->setWindowIcon(QIcon(":/Vita3K.png"));
|
this->setWindowIcon(QIcon(":/Vita3K.png"));
|
||||||
@@ -386,11 +388,19 @@ void MainWindow::initialize() {
|
|||||||
|
|
||||||
init_current_user();
|
init_current_user();
|
||||||
|
|
||||||
m_dock_host = new QMainWindow(this);
|
auto *shell_frame = new ThemeSurface(this);
|
||||||
|
shell_frame->setObjectName(QStringLiteral("mw_frame"));
|
||||||
|
auto *shell_layout = new QVBoxLayout(shell_frame);
|
||||||
|
shell_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
shell_layout->setSpacing(0);
|
||||||
|
setCentralWidget(shell_frame);
|
||||||
|
|
||||||
|
m_dock_host = new QMainWindow(shell_frame);
|
||||||
|
m_dock_host->setObjectName(QStringLiteral("mw_dock_host"));
|
||||||
m_dock_host->setWindowFlags(Qt::Widget);
|
m_dock_host->setWindowFlags(Qt::Widget);
|
||||||
m_dock_host->setContextMenuPolicy(Qt::PreventContextMenu);
|
m_dock_host->setContextMenuPolicy(Qt::PreventContextMenu);
|
||||||
m_dock_host->setDockNestingEnabled(true);
|
m_dock_host->setDockNestingEnabled(true);
|
||||||
setCentralWidget(m_dock_host);
|
shell_layout->addWidget(m_dock_host);
|
||||||
|
|
||||||
m_apps_list_widget = new AppsList(emuenv, m_dock_host);
|
m_apps_list_widget = new AppsList(emuenv, m_dock_host);
|
||||||
m_apps_list_widget->refresh();
|
m_apps_list_widget->refresh();
|
||||||
@@ -423,6 +433,9 @@ void MainWindow::initialize() {
|
|||||||
connect(m_ui->trophy_collection_action, &QAction::triggered,
|
connect(m_ui->trophy_collection_action, &QAction::triggered,
|
||||||
this, &MainWindow::open_trophy_collection);
|
this, &MainWindow::open_trophy_collection);
|
||||||
|
|
||||||
|
connect(m_ui->vita_themes_action, &QAction::triggered,
|
||||||
|
this, &MainWindow::open_vita_themes);
|
||||||
|
|
||||||
connect(m_ui->user_management_action, &QAction::triggered,
|
connect(m_ui->user_management_action, &QAction::triggered,
|
||||||
this, &MainWindow::open_user_management);
|
this, &MainWindow::open_user_management);
|
||||||
|
|
||||||
@@ -500,7 +513,35 @@ void MainWindow::initialize() {
|
|||||||
|
|
||||||
apply_log_gui_settings();
|
apply_log_gui_settings();
|
||||||
|
|
||||||
init_default_style();
|
m_theme_manager = std::make_unique<ThemeManager>(m_gui_settings);
|
||||||
|
m_theme_manager->set_pref_path(emuenv.pref_path);
|
||||||
|
m_theme_manager->set_vita_themes_root(emuenv.pref_path / "ux0/theme");
|
||||||
|
connect(m_theme_manager.get(), &ThemeManager::theme_state_changed, this, &MainWindow::repaint_gui);
|
||||||
|
connect(m_theme_manager.get(), &ThemeManager::theme_state_changed, this, &MainWindow::apply_theme_background_presentation);
|
||||||
|
|
||||||
|
m_theme_background_cycle_timer = new QTimer(this);
|
||||||
|
connect(m_theme_background_cycle_timer, &QTimer::timeout, this, &MainWindow::advance_theme_background_presentation);
|
||||||
|
|
||||||
|
m_theme_background_fade_animation = new QVariantAnimation(this);
|
||||||
|
m_theme_background_fade_animation->setDuration(900);
|
||||||
|
m_theme_background_fade_animation->setStartValue(0.0);
|
||||||
|
m_theme_background_fade_animation->setEndValue(1.0);
|
||||||
|
m_theme_background_fade_animation->setEasingCurve(QEasingCurve::InOutSine);
|
||||||
|
connect(m_theme_background_fade_animation, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) {
|
||||||
|
m_theme_background_fade_progress = value.toReal();
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
connect(m_theme_background_fade_animation, &QVariantAnimation::finished, this, [this] {
|
||||||
|
if (m_theme_background_next_index >= 0)
|
||||||
|
m_theme_background_current_index = m_theme_background_next_index;
|
||||||
|
|
||||||
|
m_theme_background_next_index = -1;
|
||||||
|
m_theme_background_fade_progress = 0.0;
|
||||||
|
if (m_theme_background_cycle_timer && m_theme_background_scaled_pixmaps.size() > 1)
|
||||||
|
m_theme_background_cycle_timer->start(m_theme_background_cycle_interval_ms);
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
init_first_run_stylesheet();
|
init_first_run_stylesheet();
|
||||||
apply_stylesheet();
|
apply_stylesheet();
|
||||||
|
|
||||||
@@ -580,6 +621,10 @@ void MainWindow::on_install_license_triggered() {
|
|||||||
|
|
||||||
void MainWindow::on_install_finished() {
|
void MainWindow::on_install_finished() {
|
||||||
m_apps_list_widget->refresh(true);
|
m_apps_list_widget->refresh(true);
|
||||||
|
if (m_theme_manager)
|
||||||
|
m_theme_manager->refresh_vita_theme_catalog();
|
||||||
|
if (m_vita_themes_dialog)
|
||||||
|
m_vita_themes_dialog->reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::prompt_startup_warnings() {
|
bool MainWindow::prompt_startup_warnings() {
|
||||||
@@ -747,9 +792,6 @@ void MainWindow::on_controls_action_triggered() {
|
|||||||
if (!m_controls_dialog) {
|
if (!m_controls_dialog) {
|
||||||
m_controls_dialog = new ControlsDialog(emuenv, m_gui_settings);
|
m_controls_dialog = new ControlsDialog(emuenv, m_gui_settings);
|
||||||
register_auxiliary_window(m_controls_dialog);
|
register_auxiliary_window(m_controls_dialog);
|
||||||
connect(m_controls_dialog, &QObject::destroyed, this, [this] {
|
|
||||||
m_controls_dialog = nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
present_tool_window(m_controls_dialog);
|
present_tool_window(m_controls_dialog);
|
||||||
}
|
}
|
||||||
@@ -761,13 +803,18 @@ void MainWindow::on_check_for_updates_action_triggered() {
|
|||||||
|
|
||||||
void MainWindow::open_settings(int tab_index) {
|
void MainWindow::open_settings(int tab_index) {
|
||||||
if (!m_settings_dialog) {
|
if (!m_settings_dialog) {
|
||||||
m_settings_dialog = new SettingsDialog(emuenv, m_gui_settings, {}, tab_index);
|
m_settings_dialog = new SettingsDialog(emuenv, m_gui_settings, *m_theme_manager, {}, tab_index);
|
||||||
register_auxiliary_window(m_settings_dialog);
|
register_auxiliary_window(m_settings_dialog);
|
||||||
connect(m_settings_dialog, &SettingsDialog::gui_stylesheet_request, this, &MainWindow::apply_stylesheet);
|
connect(m_settings_dialog, &SettingsDialog::gui_stylesheet_request, this, &MainWindow::apply_stylesheet);
|
||||||
|
connect(m_settings_dialog, &SettingsDialog::gui_theme_music_settings_request, this, [this] {
|
||||||
|
if (m_theme_manager)
|
||||||
|
m_theme_manager->refresh_vita_theme_bgm();
|
||||||
|
});
|
||||||
connect(m_settings_dialog, &SettingsDialog::gui_log_settings_request, this, &MainWindow::apply_log_gui_settings);
|
connect(m_settings_dialog, &SettingsDialog::gui_log_settings_request, this, &MainWindow::apply_log_gui_settings);
|
||||||
connect(m_settings_dialog, &SettingsDialog::storage_path_changed, this, [this] {
|
connect(m_settings_dialog, &SettingsDialog::storage_path_changed, this, [this] {
|
||||||
if (m_apps_list_widget)
|
if (m_apps_list_widget)
|
||||||
m_apps_list_widget->refresh(true);
|
m_apps_list_widget->refresh(true);
|
||||||
|
apply_stylesheet();
|
||||||
});
|
});
|
||||||
connect(m_settings_dialog, &SettingsDialog::ui_language_request, this, &MainWindow::apply_ui_language);
|
connect(m_settings_dialog, &SettingsDialog::ui_language_request, this, &MainWindow::apply_ui_language);
|
||||||
connect(m_settings_dialog, &SettingsDialog::restart_game_requested, this, &MainWindow::restart_running_app);
|
connect(m_settings_dialog, &SettingsDialog::restart_game_requested, this, &MainWindow::restart_running_app);
|
||||||
@@ -776,9 +823,6 @@ void MainWindow::open_settings(int tab_index) {
|
|||||||
refresh_status_bar();
|
refresh_status_bar();
|
||||||
apply_log_gui_settings();
|
apply_log_gui_settings();
|
||||||
});
|
});
|
||||||
connect(m_settings_dialog, &QObject::destroyed, this, [this] {
|
|
||||||
m_settings_dialog = nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_settings_dialog->set_storage_path_locked(m_game_window != nullptr);
|
m_settings_dialog->set_storage_path_locked(m_game_window != nullptr);
|
||||||
@@ -904,6 +948,8 @@ std::optional<AppLaunchRequest> MainWindow::boot_game_once(const AppLaunchReques
|
|||||||
tr("Could not find app '%1' in apps list.").arg(QString::fromStdString(launch_request.app_path)));
|
tr("Could not find app '%1' in apps list.").arg(QString::fromStdString(launch_request.app_path)));
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
if (m_theme_manager)
|
||||||
|
m_theme_manager->set_vita_theme_bgm_blocked(true);
|
||||||
|
|
||||||
bool launch_fullscreen = emuenv.cfg.boot_apps_full_screen;
|
bool launch_fullscreen = emuenv.cfg.boot_apps_full_screen;
|
||||||
if (m_game_container) {
|
if (m_game_container) {
|
||||||
@@ -941,6 +987,8 @@ std::optional<AppLaunchRequest> MainWindow::boot_game_once(const AppLaunchReques
|
|||||||
const auto abort_boot = [&](const QString &msg) {
|
const auto abort_boot = [&](const QString &msg) {
|
||||||
QMessageBox::critical(this, tr("Error"), msg);
|
QMessageBox::critical(this, tr("Error"), msg);
|
||||||
m_app_session.stop(app::AppSessionStopReason::LaunchFailure);
|
m_app_session.stop(app::AppSessionStopReason::LaunchFailure);
|
||||||
|
if (m_theme_manager)
|
||||||
|
m_theme_manager->set_vita_theme_bgm_blocked(false);
|
||||||
|
|
||||||
if (m_game_window) {
|
if (m_game_window) {
|
||||||
delete m_game_window;
|
delete m_game_window;
|
||||||
@@ -1156,6 +1204,8 @@ void MainWindow::on_game_closed() {
|
|||||||
|
|
||||||
if (m_settings_dialog)
|
if (m_settings_dialog)
|
||||||
m_settings_dialog->set_storage_path_locked(false);
|
m_settings_dialog->set_storage_path_locked(false);
|
||||||
|
if (m_theme_manager && !m_is_app_closing)
|
||||||
|
m_theme_manager->set_vita_theme_bgm_blocked(false);
|
||||||
refresh_emulation_actions();
|
refresh_emulation_actions();
|
||||||
|
|
||||||
if (!m_is_app_closing && m_apps_list_widget)
|
if (!m_is_app_closing && m_apps_list_widget)
|
||||||
@@ -1451,13 +1501,19 @@ void MainWindow::open_trophy_collection() {
|
|||||||
if (!m_trophy_dialog) {
|
if (!m_trophy_dialog) {
|
||||||
m_trophy_dialog = new TrophyCollectionDialog(emuenv, m_gui_settings);
|
m_trophy_dialog = new TrophyCollectionDialog(emuenv, m_gui_settings);
|
||||||
register_auxiliary_window(m_trophy_dialog);
|
register_auxiliary_window(m_trophy_dialog);
|
||||||
connect(m_trophy_dialog, &QObject::destroyed, this, [this] {
|
|
||||||
m_trophy_dialog = nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
present_tool_window(m_trophy_dialog);
|
present_tool_window(m_trophy_dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::open_vita_themes() {
|
||||||
|
if (!m_vita_themes_dialog) {
|
||||||
|
m_vita_themes_dialog = new VitaThemesDialog(m_gui_settings, *m_theme_manager, this);
|
||||||
|
register_auxiliary_window(m_vita_themes_dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
present_tool_window(m_vita_themes_dialog);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::open_user_management() {
|
void MainWindow::open_user_management() {
|
||||||
if (m_game_window) {
|
if (m_game_window) {
|
||||||
return;
|
return;
|
||||||
@@ -1472,9 +1528,6 @@ void MainWindow::open_user_management() {
|
|||||||
if (m_trophy_dialog)
|
if (m_trophy_dialog)
|
||||||
m_trophy_dialog->reload();
|
m_trophy_dialog->reload();
|
||||||
});
|
});
|
||||||
connect(m_user_mgmt_dialog, &QObject::destroyed, this, [this] {
|
|
||||||
m_user_mgmt_dialog = nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
present_tool_window(m_user_mgmt_dialog);
|
present_tool_window(m_user_mgmt_dialog);
|
||||||
}
|
}
|
||||||
@@ -1483,9 +1536,6 @@ void MainWindow::open_debug_widget(int tab) {
|
|||||||
if (!m_debug_widget) {
|
if (!m_debug_widget) {
|
||||||
m_debug_widget = new DebugWidget(emuenv, this);
|
m_debug_widget = new DebugWidget(emuenv, this);
|
||||||
m_debug_widget->setFloating(true);
|
m_debug_widget->setFloating(true);
|
||||||
connect(m_debug_widget, &QObject::destroyed, this, [this] {
|
|
||||||
m_debug_widget = nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
m_debug_widget->show_tab(tab);
|
m_debug_widget->show_tab(tab);
|
||||||
}
|
}
|
||||||
@@ -1537,7 +1587,7 @@ void MainWindow::setup_toolbar() {
|
|||||||
connect(m_ui->toolbar_controls, &QAction::triggered,
|
connect(m_ui->toolbar_controls, &QAction::triggered,
|
||||||
this, &MainWindow::on_controls_action_triggered);
|
this, &MainWindow::on_controls_action_triggered);
|
||||||
|
|
||||||
m_slider_container = new QWidget(this);
|
m_slider_container = new ThemeSurface(this);
|
||||||
m_slider_container->setObjectName(QStringLiteral("toolbar_icon_size_container"));
|
m_slider_container->setObjectName(QStringLiteral("toolbar_icon_size_container"));
|
||||||
auto *slider_layout = new QHBoxLayout(m_slider_container);
|
auto *slider_layout = new QHBoxLayout(m_slider_container);
|
||||||
slider_layout->setContentsMargins(14, 0, 14, 0);
|
slider_layout->setContentsMargins(14, 0, 14, 0);
|
||||||
@@ -1558,6 +1608,7 @@ void MainWindow::setup_toolbar() {
|
|||||||
this, &MainWindow::resize_icons);
|
this, &MainWindow::resize_icons);
|
||||||
|
|
||||||
m_search_bar = new QLineEdit(this);
|
m_search_bar = new QLineEdit(this);
|
||||||
|
m_search_bar->setObjectName(QStringLiteral("mw_searchbar"));
|
||||||
m_search_bar->setPlaceholderText(tr("Search..."));
|
m_search_bar->setPlaceholderText(tr("Search..."));
|
||||||
m_search_bar->setFocusPolicy(Qt::ClickFocus);
|
m_search_bar->setFocusPolicy(Qt::ClickFocus);
|
||||||
m_search_bar->setClearButtonEnabled(true);
|
m_search_bar->setClearButtonEnabled(true);
|
||||||
@@ -1591,20 +1642,6 @@ void MainWindow::repaint_gui() {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::init_default_style() {
|
|
||||||
if (QStyleFactory::keys().contains(QStringLiteral("Fusion"), Qt::CaseInsensitive))
|
|
||||||
m_default_style = QStringLiteral("Fusion");
|
|
||||||
|
|
||||||
if (m_default_style.isEmpty()) {
|
|
||||||
if (const QStyle *style = QApplication::style())
|
|
||||||
m_default_style = style->name();
|
|
||||||
}
|
|
||||||
if (m_default_style.isEmpty()) {
|
|
||||||
if (const QStringList styles = QStyleFactory::keys(); !styles.empty())
|
|
||||||
m_default_style = styles.front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::init_first_run_stylesheet() {
|
void MainWindow::init_first_run_stylesheet() {
|
||||||
if (!m_gui_settings || m_gui_settings->contains_value(gui::m_currentStylesheet))
|
if (!m_gui_settings || m_gui_settings->contains_value(gui::m_currentStylesheet))
|
||||||
return;
|
return;
|
||||||
@@ -1617,69 +1654,116 @@ void MainWindow::init_first_run_stylesheet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::apply_stylesheet() {
|
void MainWindow::apply_stylesheet() {
|
||||||
if (!m_gui_settings)
|
if (!m_theme_manager)
|
||||||
|
return;
|
||||||
|
m_theme_manager->ensure_generated_theme_ready(emuenv.pref_path / "ux0/theme");
|
||||||
|
m_theme_manager->apply_theme({}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::apply_theme_background_presentation() {
|
||||||
|
if (!m_theme_manager) {
|
||||||
|
clear_theme_background_presentation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto state = m_theme_manager->current_applied_theme_state();
|
||||||
|
if (!state || state->background.image_paths.isEmpty()) {
|
||||||
|
clear_theme_background_presentation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QPixmap> next_pixmaps;
|
||||||
|
next_pixmaps.reserve(static_cast<std::size_t>(state->background.image_paths.size()));
|
||||||
|
for (const QString &path : state->background.image_paths) {
|
||||||
|
QPixmap pixmap(path);
|
||||||
|
if (!pixmap.isNull())
|
||||||
|
next_pixmaps.push_back(std::move(pixmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_pixmaps.empty()) {
|
||||||
|
clear_theme_background_presentation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_theme_background_cycle_timer)
|
||||||
|
m_theme_background_cycle_timer->stop();
|
||||||
|
if (m_theme_background_fade_animation)
|
||||||
|
m_theme_background_fade_animation->stop();
|
||||||
|
|
||||||
|
m_theme_background_original_pixmaps = std::move(next_pixmaps);
|
||||||
|
m_theme_background_scaled_pixmaps.clear();
|
||||||
|
m_theme_background_scaled_size = {};
|
||||||
|
m_theme_background_cycle_interval_ms = state->background.interval_ms;
|
||||||
|
m_theme_background_current_index = 0;
|
||||||
|
m_theme_background_next_index = -1;
|
||||||
|
m_theme_background_fade_progress = 0.0;
|
||||||
|
rescale_theme_background_pixmaps();
|
||||||
|
|
||||||
|
if (m_theme_background_cycle_timer && state->background.animated && m_theme_background_scaled_pixmaps.size() > 1)
|
||||||
|
m_theme_background_cycle_timer->start(m_theme_background_cycle_interval_ms);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::clear_theme_background_presentation() {
|
||||||
|
if (m_theme_background_cycle_timer)
|
||||||
|
m_theme_background_cycle_timer->stop();
|
||||||
|
if (m_theme_background_fade_animation)
|
||||||
|
m_theme_background_fade_animation->stop();
|
||||||
|
|
||||||
|
m_theme_background_original_pixmaps.clear();
|
||||||
|
m_theme_background_scaled_pixmaps.clear();
|
||||||
|
m_theme_background_scaled_size = {};
|
||||||
|
m_theme_background_current_index = 0;
|
||||||
|
m_theme_background_next_index = -1;
|
||||||
|
m_theme_background_fade_progress = 0.0;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::rescale_theme_background_pixmaps() {
|
||||||
|
m_theme_background_scaled_pixmaps.clear();
|
||||||
|
if (m_theme_background_original_pixmaps.empty() || size().isEmpty()) {
|
||||||
|
m_theme_background_scaled_size = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_theme_background_scaled_size = size();
|
||||||
|
m_theme_background_scaled_pixmaps.reserve(m_theme_background_original_pixmaps.size());
|
||||||
|
for (const QPixmap &pixmap : m_theme_background_original_pixmaps) {
|
||||||
|
m_theme_background_scaled_pixmaps.push_back(pixmap.scaled(
|
||||||
|
m_theme_background_scaled_size,
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::advance_theme_background_presentation() {
|
||||||
|
if (!m_theme_background_fade_animation || m_theme_background_scaled_pixmaps.size() < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QString stylesheet_name = m_gui_settings->get_value(gui::m_currentStylesheet).toString();
|
if (!isVisible() || isMinimized()) {
|
||||||
|
if (m_theme_background_cycle_timer)
|
||||||
if (QStyle *style = QStyleFactory::create(m_default_style)) {
|
m_theme_background_cycle_timer->start(1000);
|
||||||
QApplication::setStyle(style);
|
return;
|
||||||
QApplication::setPalette(style->standardPalette());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stylesheet_name.isEmpty() || stylesheet_name == gui::LightStylesheet) {
|
if (m_theme_background_fade_animation->state() == QAbstractAnimation::Running)
|
||||||
LOG_INFO("Using light stylesheet");
|
return;
|
||||||
if (QStyle *fusion = QStyleFactory::create(QStringLiteral("Fusion"))) {
|
|
||||||
QApplication::setStyle(fusion);
|
|
||||||
QApplication::setPalette(fusion->standardPalette());
|
|
||||||
}
|
|
||||||
qApp->setStyleSheet(gui::stylesheets::light_style_sheet);
|
|
||||||
} else if (stylesheet_name == gui::DarkStylesheet) {
|
|
||||||
LOG_INFO("Using dark stylesheet");
|
|
||||||
if (QStyle *fusion = QStyleFactory::create(QStringLiteral("Fusion"))) {
|
|
||||||
QApplication::setStyle(fusion);
|
|
||||||
QApplication::setPalette(fusion->standardPalette());
|
|
||||||
}
|
|
||||||
QApplication::setPalette(make_dark_palette());
|
|
||||||
qApp->setStyleSheet(gui::stylesheets::dark_style_sheet);
|
|
||||||
} else if (stylesheet_name == gui::NoStylesheet) {
|
|
||||||
LOG_INFO("Using empty stylesheet");
|
|
||||||
qApp->setStyleSheet(QStringLiteral("/* none */"));
|
|
||||||
} else {
|
|
||||||
const QString settings_dir = m_gui_settings->get_settings_dir();
|
|
||||||
const QString stylesheet_path = settings_dir + QStringLiteral("/") + stylesheet_name + QStringLiteral(".qss");
|
|
||||||
|
|
||||||
if (QFile file(stylesheet_path); file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
m_theme_background_next_index = (m_theme_background_current_index + 1)
|
||||||
QString qss = file.readAll();
|
% static_cast<int>(m_theme_background_scaled_pixmaps.size());
|
||||||
file.close();
|
m_theme_background_fade_progress = 0.0;
|
||||||
|
m_theme_background_fade_animation->start();
|
||||||
qss.replace(
|
|
||||||
QStringLiteral("url(\"gui-configs/"),
|
|
||||||
QStringLiteral("url(\"") + settings_dir + QStringLiteral("/"));
|
|
||||||
|
|
||||||
LOG_INFO("Loaded stylesheet '{}' from {}", stylesheet_name.toUtf8().constData(), stylesheet_path.toUtf8().constData());
|
|
||||||
qApp->setStyleSheet(qss);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR("Could not find stylesheet '{}', falling back to light", stylesheet_name.toUtf8().constData());
|
|
||||||
qApp->setStyleSheet(gui::stylesheets::light_style_sheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repaint_gui();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::repaint_toolbar_icons() {
|
void MainWindow::repaint_toolbar_icons() {
|
||||||
auto *tb = m_ui->toolBar;
|
auto *tb = m_ui->toolBar;
|
||||||
const QColor fg = gui::utils::get_label_color(
|
const QColor fg = gui::utils::toolbar_icon_tint();
|
||||||
QStringLiteral("toolbar_icon_color"),
|
|
||||||
QColor(QStringLiteral("#5b5b5b")),
|
|
||||||
QColor(QStringLiteral("#f3f3f3")));
|
|
||||||
|
|
||||||
std::map<QIcon::Mode, QColor> new_colors;
|
std::map<QIcon::Mode, QColor> new_colors;
|
||||||
new_colors[QIcon::Normal] = gui::utils::get_label_color(fg, fg);
|
new_colors[QIcon::Normal] = fg;
|
||||||
new_colors[QIcon::Disabled] = gui::utils::get_label_color(Qt::gray, Qt::darkGray);
|
new_colors[QIcon::Disabled] = gui::utils::muted_text_color();
|
||||||
new_colors[QIcon::Active] = gui::utils::get_label_color(fg, fg);
|
new_colors[QIcon::Active] = fg;
|
||||||
new_colors[QIcon::Selected] = gui::utils::get_label_color(fg, fg);
|
new_colors[QIcon::Selected] = fg;
|
||||||
|
|
||||||
const auto icon = [&new_colors](const QString &path) {
|
const auto icon = [&new_colors](const QString &path) {
|
||||||
return gui::utils::get_colorized_icon(QIcon(path), Qt::black, new_colors);
|
return gui::utils::get_colorized_icon(QIcon(path), Qt::black, new_colors);
|
||||||
@@ -1776,9 +1860,13 @@ void MainWindow::on_context_menu_requested(const QPoint &global_pos, const std::
|
|||||||
});
|
});
|
||||||
connect(&menu, &AppsListContextMenu::open_settings_requested,
|
connect(&menu, &AppsListContextMenu::open_settings_requested,
|
||||||
this, [this](const std::string &app_path, int tab) {
|
this, [this](const std::string &app_path, int tab) {
|
||||||
auto *dlg = new SettingsDialog(emuenv, m_gui_settings, app_path, tab);
|
auto *dlg = new SettingsDialog(emuenv, m_gui_settings, *m_theme_manager, app_path, tab);
|
||||||
register_auxiliary_window(dlg);
|
register_auxiliary_window(dlg);
|
||||||
connect(dlg, &SettingsDialog::gui_stylesheet_request, this, &MainWindow::apply_stylesheet);
|
connect(dlg, &SettingsDialog::gui_stylesheet_request, this, &MainWindow::apply_stylesheet);
|
||||||
|
connect(dlg, &SettingsDialog::gui_theme_music_settings_request, this, [this] {
|
||||||
|
if (m_theme_manager)
|
||||||
|
m_theme_manager->refresh_vita_theme_bgm();
|
||||||
|
});
|
||||||
connect(dlg, &SettingsDialog::gui_log_settings_request, this, &MainWindow::apply_log_gui_settings);
|
connect(dlg, &SettingsDialog::gui_log_settings_request, this, &MainWindow::apply_log_gui_settings);
|
||||||
connect(dlg, &SettingsDialog::ui_language_request, this, &MainWindow::apply_ui_language);
|
connect(dlg, &SettingsDialog::ui_language_request, this, &MainWindow::apply_ui_language);
|
||||||
connect(dlg, &SettingsDialog::restart_game_requested, this, &MainWindow::restart_running_app);
|
connect(dlg, &SettingsDialog::restart_game_requested, this, &MainWindow::restart_running_app);
|
||||||
@@ -1807,7 +1895,7 @@ void MainWindow::resize_icons(int index) {
|
|||||||
|
|
||||||
void MainWindow::setup_status_bar() {
|
void MainWindow::setup_status_bar() {
|
||||||
auto *sb = statusBar();
|
auto *sb = statusBar();
|
||||||
sb->setObjectName(QStringLiteral("emulatorStatusBar"));
|
sb->setObjectName(QStringLiteral("emulator_status_bar"));
|
||||||
|
|
||||||
m_renderer_button = new QPushButton(this);
|
m_renderer_button = new QPushButton(this);
|
||||||
m_renderer_button->setObjectName(QStringLiteral("status_button"));
|
m_renderer_button->setObjectName(QStringLiteral("status_button"));
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Manage</string>
|
<string>Manage</string>
|
||||||
</property>
|
</property>
|
||||||
|
<addaction name="vita_themes_action"/>
|
||||||
<addaction name="trophy_collection_action"/>
|
<addaction name="trophy_collection_action"/>
|
||||||
<addaction name="user_management_action"/>
|
<addaction name="user_management_action"/>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -241,6 +242,11 @@
|
|||||||
<string>Trophy Collection</string>
|
<string>Trophy Collection</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="vita_themes_action">
|
||||||
|
<property name="text">
|
||||||
|
<string>Vita Themes</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="user_management_action">
|
<action name="user_management_action">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>User Management</string>
|
<string>User Management</string>
|
||||||
|
|||||||
+109
-22
@@ -20,6 +20,8 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QHash>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@@ -28,7 +30,9 @@
|
|||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QRgb>
|
#include <QRgb>
|
||||||
|
#include <QStyle>
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
@@ -37,6 +41,22 @@
|
|||||||
|
|
||||||
namespace gui::utils {
|
namespace gui::utils {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QColor default_label_color(QPalette::ColorRole color_role) {
|
||||||
|
QLabel dummy;
|
||||||
|
dummy.ensurePolished();
|
||||||
|
return dummy.palette().color(color_role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, QColor> s_theme_role_colors;
|
||||||
|
|
||||||
|
QString theme_role_cache_key(const QString &role, QPalette::ColorRole color_role) {
|
||||||
|
return role + QLatin1Char(':') + QString::number(static_cast<int>(color_role));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void open_dir(const fs::path &path) {
|
void open_dir(const fs::path &path) {
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(to_qt_path(path)));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(to_qt_path(path)));
|
||||||
}
|
}
|
||||||
@@ -49,40 +69,107 @@ QString to_qt_path(const fs::path &path) {
|
|||||||
return QString::fromStdWString(path.wstring());
|
return QString::fromStdWString(path.wstring());
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor get_foreground_color(QWidget *widget) {
|
QString sanitize_relative_path_reference(const QString &path) {
|
||||||
|
QString normalized = path.trimmed();
|
||||||
|
normalized.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
||||||
|
if (normalized.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
static const QRegularExpression windows_drive_prefix(QStringLiteral(R"(^[A-Za-z]:/)"));
|
||||||
|
static const QRegularExpression url_scheme_prefix(
|
||||||
|
QStringLiteral(R"(^[A-Za-z][A-Za-z0-9+.-]*:)"),
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
|
if (windows_drive_prefix.match(normalized).hasMatch() || url_scheme_prefix.match(normalized).hasMatch())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QString relative = normalized;
|
||||||
|
while (relative.startsWith(QStringLiteral("./")))
|
||||||
|
relative.remove(0, 2);
|
||||||
|
while (relative.startsWith(QLatin1Char('/')))
|
||||||
|
relative.remove(0, 1);
|
||||||
|
|
||||||
|
relative = QDir::cleanPath(relative);
|
||||||
|
if (relative.isEmpty() || relative == QStringLiteral(".")
|
||||||
|
|| relative == QStringLiteral("..")
|
||||||
|
|| relative.startsWith(QStringLiteral("../"))) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString resolve_relative_path_in_root(const QString &relative_path, const QString &root_path) {
|
||||||
|
if (relative_path.isEmpty() || root_path.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const QDir root_dir(QDir::cleanPath(root_path));
|
||||||
|
const QString resolved_path = QDir::cleanPath(root_dir.absoluteFilePath(relative_path));
|
||||||
|
const QString root_relative = QDir::cleanPath(root_dir.relativeFilePath(resolved_path));
|
||||||
|
if (root_relative == QStringLiteral("..") || root_relative.startsWith(QStringLiteral("../")))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return QDir::fromNativeSeparators(resolved_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor foreground_color(QWidget *widget) {
|
||||||
if (widget) {
|
if (widget) {
|
||||||
widget->ensurePolished();
|
widget->ensurePolished();
|
||||||
return widget->palette().color(QPalette::ColorRole::WindowText);
|
return widget->palette().color(QPalette::ColorRole::WindowText);
|
||||||
}
|
}
|
||||||
|
return default_label_color(QPalette::ColorRole::WindowText);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor theme_role_color(
|
||||||
|
const QString &role,
|
||||||
|
QPalette::ColorRole color_role) {
|
||||||
|
const QString cache_key = theme_role_cache_key(role, color_role);
|
||||||
|
if (const auto it = s_theme_role_colors.constFind(cache_key); it != s_theme_role_colors.cend())
|
||||||
|
return *it;
|
||||||
|
|
||||||
QLabel dummy;
|
QLabel dummy;
|
||||||
|
dummy.setProperty("themeRole", role);
|
||||||
dummy.ensurePolished();
|
dummy.ensurePolished();
|
||||||
return dummy.palette().color(QPalette::ColorRole::WindowText);
|
const QColor color = dummy.palette().color(color_role);
|
||||||
|
s_theme_role_colors.insert(cache_key, color);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString format_help_html(const QString &title, const QString &body) {
|
||||||
|
QString escaped = body.toHtmlEscaped();
|
||||||
|
escaped.replace(QLatin1Char('\n'), QStringLiteral("<br>"));
|
||||||
|
return QStringLiteral(
|
||||||
|
"<div style=\"margin:0;\"><strong>%1</strong></div>"
|
||||||
|
"<hr style=\"margin:1px 0 3px 0; border:0; border-top:1px solid rgba(255,255,255,0.28);\">"
|
||||||
|
"<div style=\"margin:0;\">%2</div>")
|
||||||
|
.arg(title.toHtmlEscaped(), escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidate_theme_cache() {
|
||||||
|
s_theme_role_colors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor muted_text_color() {
|
||||||
|
return theme_role_color(QStringLiteral("mutedText"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor toolbar_icon_tint() {
|
||||||
|
return theme_role_color(QStringLiteral("toolbarIconTint"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh_theme_state(QWidget *widget) {
|
||||||
|
if (!widget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
widget->style()->unpolish(widget);
|
||||||
|
widget->style()->polish(widget);
|
||||||
|
widget->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dark_mode_active() {
|
bool dark_mode_active() {
|
||||||
return QApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark;
|
return QApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor get_label_color(
|
|
||||||
const QString &object_name,
|
|
||||||
const QColor &fallback_light,
|
|
||||||
const QColor &fallback_dark,
|
|
||||||
QPalette::ColorRole color_role) {
|
|
||||||
if (!qApp || qApp->styleSheet().isEmpty() || !qApp->styleSheet().contains(object_name))
|
|
||||||
return dark_mode_active() ? fallback_dark : fallback_light;
|
|
||||||
|
|
||||||
QLabel dummy;
|
|
||||||
dummy.setObjectName(object_name);
|
|
||||||
dummy.ensurePolished();
|
|
||||||
return dummy.palette().color(color_role);
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor get_label_color(
|
|
||||||
const QColor &fallback_light,
|
|
||||||
const QColor &fallback_dark) {
|
|
||||||
return dark_mode_active() ? fallback_dark : fallback_light;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_colorized_icon(const QIcon &icon,
|
QIcon get_colorized_icon(const QIcon &icon,
|
||||||
const QColor &source_color,
|
const QColor &source_color,
|
||||||
const std::map<QIcon::Mode, QColor> &new_colors) {
|
const std::map<QIcon::Mode, QColor> &new_colors) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <gui-qt/qt_utils.h>
|
#include <gui-qt/qt_utils.h>
|
||||||
#include <gui-qt/settings_dialog.h>
|
#include <gui-qt/settings_dialog.h>
|
||||||
#include <gui-qt/settings_dialog_tooltips.h>
|
#include <gui-qt/settings_dialog_tooltips.h>
|
||||||
|
#include <gui-qt/theme_manager.h>
|
||||||
#include <io/state.h>
|
#include <io/state.h>
|
||||||
#include <kernel/state.h>
|
#include <kernel/state.h>
|
||||||
#include <renderer/functions.h>
|
#include <renderer/functions.h>
|
||||||
@@ -59,19 +60,12 @@
|
|||||||
#include <QTabBar>
|
#include <QTabBar>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
QString format_help_html(const QString &title, const QString &body) {
|
|
||||||
QString escaped = body.toHtmlEscaped();
|
|
||||||
escaped.replace(QLatin1Char('\n'), QStringLiteral("<br>"));
|
|
||||||
return QStringLiteral(
|
|
||||||
"<div style=\"margin:0;\"><strong>%1</strong></div>"
|
|
||||||
"<hr style=\"margin:1px 0 3px 0; border:0; border-top:1px solid rgba(255,255,255,0.28);\">"
|
|
||||||
"<div style=\"margin:0;\">%2</div>")
|
|
||||||
.arg(title.toHtmlEscaped(), escaped);
|
|
||||||
}
|
|
||||||
|
|
||||||
int first_visible_category(const QListWidget *list, int preferred) {
|
int first_visible_category(const QListWidget *list, int preferred) {
|
||||||
const int count = list->count();
|
const int count = list->count();
|
||||||
if (preferred >= 0 && preferred < count) {
|
if (preferred >= 0 && preferred < count) {
|
||||||
@@ -91,11 +85,13 @@ int first_visible_category(const QListWidget *list, int preferred) {
|
|||||||
|
|
||||||
SettingsDialog::SettingsDialog(EmuEnvState &emuenv,
|
SettingsDialog::SettingsDialog(EmuEnvState &emuenv,
|
||||||
std::shared_ptr<GuiSettings> gui_settings,
|
std::shared_ptr<GuiSettings> gui_settings,
|
||||||
|
ThemeManager &theme_manager,
|
||||||
const std::string &app_path,
|
const std::string &app_path,
|
||||||
int initial_tab)
|
int initial_tab)
|
||||||
: QDialog(nullptr)
|
: QDialog(nullptr)
|
||||||
, emuenv(emuenv)
|
, emuenv(emuenv)
|
||||||
, m_gui_settings(std::move(gui_settings))
|
, m_gui_settings(std::move(gui_settings))
|
||||||
|
, m_theme_manager(theme_manager)
|
||||||
, m_tooltips(std::make_unique<SettingsDialogTooltips>(this))
|
, m_tooltips(std::make_unique<SettingsDialogTooltips>(this))
|
||||||
, m_app_path(app_path)
|
, m_app_path(app_path)
|
||||||
, m_initial_tab(initial_tab)
|
, m_initial_tab(initial_tab)
|
||||||
@@ -123,7 +119,12 @@ SettingsDialog::SettingsDialog(EmuEnvState &emuenv,
|
|||||||
}
|
}
|
||||||
if (!m_gui_settings || !restoreGeometry(m_gui_settings->get_value(gui::sd_geometry).toByteArray()))
|
if (!m_gui_settings || !restoreGeometry(m_gui_settings->get_value(gui::sd_geometry).toByteArray()))
|
||||||
resize(780, 676);
|
resize(780, 676);
|
||||||
|
connect(&m_theme_manager, &ThemeManager::theme_state_changed, this, &SettingsDialog::refresh_themes);
|
||||||
|
connect(&m_theme_manager, &ThemeManager::theme_catalog_changed, this, &SettingsDialog::refresh_themes);
|
||||||
m_ui->settingsCategory->setSpacing(0);
|
m_ui->settingsCategory->setSpacing(0);
|
||||||
|
m_ui->modules_list->setSelectionMode(QAbstractItemView::NoSelection);
|
||||||
|
m_ui->modules_list->setFocusPolicy(Qt::NoFocus);
|
||||||
|
m_ui->modules_list->setAlternatingRowColors(true);
|
||||||
|
|
||||||
for (int i = 0; i < m_ui->tab_widget_settings->count(); ++i) {
|
for (int i = 0; i < m_ui->tab_widget_settings->count(); ++i) {
|
||||||
auto *item = new QListWidgetItem(m_ui->tab_widget_settings->tabText(i), m_ui->settingsCategory);
|
auto *item = new QListWidgetItem(m_ui->tab_widget_settings->tabText(i), m_ui->settingsCategory);
|
||||||
@@ -169,6 +170,7 @@ SettingsDialog::SettingsDialog(EmuEnvState &emuenv,
|
|||||||
m_ui->show_mode->setVisible(false);
|
m_ui->show_mode->setVisible(false);
|
||||||
m_ui->demo_mode->setVisible(false);
|
m_ui->demo_mode->setVisible(false);
|
||||||
|
|
||||||
|
m_ui->gb_theme_music_section->setVisible(false);
|
||||||
m_ui->boot_apps_fullscreen->setVisible(false);
|
m_ui->boot_apps_fullscreen->setVisible(false);
|
||||||
m_ui->show_live_area_screen->setVisible(false);
|
m_ui->show_live_area_screen->setVisible(false);
|
||||||
m_ui->show_compile_shaders->setVisible(false);
|
m_ui->show_compile_shaders->setVisible(false);
|
||||||
@@ -198,6 +200,13 @@ SettingsDialog::SettingsDialog(EmuEnvState &emuenv,
|
|||||||
bb->button(QDialogButtonBox::Apply)->setEnabled(false);
|
bb->button(QDialogButtonBox::Apply)->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::refresh_themes() {
|
||||||
|
const QString preferred_name = m_ui->combo_stylesheets
|
||||||
|
? m_ui->combo_stylesheets->currentData().toString()
|
||||||
|
: m_current_stylesheet;
|
||||||
|
add_stylesheets(preferred_name);
|
||||||
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog() {
|
SettingsDialog::~SettingsDialog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +369,17 @@ void SettingsDialog::load_config() {
|
|||||||
|
|
||||||
m_ui->audio_volume->setValue(m_config.audio_volume);
|
m_ui->audio_volume->setValue(m_config.audio_volume);
|
||||||
m_ui->audio_volume_label->setText(tr("Current volume: %1%").arg(m_config.audio_volume));
|
m_ui->audio_volume_label->setText(tr("Current volume: %1%").arg(m_config.audio_volume));
|
||||||
|
const bool theme_music_enabled = m_gui_settings
|
||||||
|
? m_gui_settings->get_value(gui::vt_bgmEnabled).toBool()
|
||||||
|
: gui::vt_bgmEnabled.def.toBool();
|
||||||
|
const int theme_music_volume = std::clamp(
|
||||||
|
m_gui_settings
|
||||||
|
? m_gui_settings->get_value(gui::vt_bgmVolume).toInt()
|
||||||
|
: gui::vt_bgmVolume.def.toInt(),
|
||||||
|
0, 100);
|
||||||
|
m_ui->theme_music_enable->setChecked(theme_music_enabled);
|
||||||
|
m_ui->theme_music_volume->setValue(theme_music_volume);
|
||||||
|
m_ui->theme_music_volume_label->setText(tr("Theme music volume: %1%").arg(theme_music_volume));
|
||||||
|
|
||||||
m_ui->ngs_enable->setChecked(m_config.ngs_enable);
|
m_ui->ngs_enable->setChecked(m_config.ngs_enable);
|
||||||
|
|
||||||
@@ -707,6 +727,12 @@ bool SettingsDialog::commit_changes(bool close_after) {
|
|||||||
const bool previous_windows_rounded_corners = update_gui_settings
|
const bool previous_windows_rounded_corners = update_gui_settings
|
||||||
? m_gui_settings->get_value(gui::gw_roundedCorners).toBool()
|
? m_gui_settings->get_value(gui::gw_roundedCorners).toBool()
|
||||||
: false;
|
: false;
|
||||||
|
const bool previous_theme_music_enabled = update_gui_settings
|
||||||
|
? m_gui_settings->get_value(gui::vt_bgmEnabled).toBool()
|
||||||
|
: gui::vt_bgmEnabled.def.toBool();
|
||||||
|
const int previous_theme_music_volume = update_gui_settings
|
||||||
|
? m_gui_settings->get_value(gui::vt_bgmVolume).toInt()
|
||||||
|
: gui::vt_bgmVolume.def.toInt();
|
||||||
|
|
||||||
if (storage_path_changed && !apply_pending_storage_path())
|
if (storage_path_changed && !apply_pending_storage_path())
|
||||||
return false;
|
return false;
|
||||||
@@ -737,8 +763,19 @@ bool SettingsDialog::commit_changes(bool close_after) {
|
|||||||
m_gui_settings->set_value(gui::mw_warnAdminPrivileges, m_ui->warn_admin_privileges->isChecked());
|
m_gui_settings->set_value(gui::mw_warnAdminPrivileges, m_ui->warn_admin_privileges->isChecked());
|
||||||
if (update_gui_settings && previous_windows_rounded_corners != m_ui->windows_rounded_corners->isChecked())
|
if (update_gui_settings && previous_windows_rounded_corners != m_ui->windows_rounded_corners->isChecked())
|
||||||
m_gui_settings->set_value(gui::gw_roundedCorners, m_ui->windows_rounded_corners->isChecked());
|
m_gui_settings->set_value(gui::gw_roundedCorners, m_ui->windows_rounded_corners->isChecked());
|
||||||
|
bool theme_music_settings_changed = false;
|
||||||
|
if (update_gui_settings && previous_theme_music_enabled != m_ui->theme_music_enable->isChecked()) {
|
||||||
|
m_gui_settings->set_value(gui::vt_bgmEnabled, m_ui->theme_music_enable->isChecked(), false);
|
||||||
|
theme_music_settings_changed = true;
|
||||||
|
}
|
||||||
|
if (update_gui_settings && previous_theme_music_volume != m_ui->theme_music_volume->value()) {
|
||||||
|
m_gui_settings->set_value(gui::vt_bgmVolume, m_ui->theme_music_volume->value(), false);
|
||||||
|
theme_music_settings_changed = true;
|
||||||
|
}
|
||||||
if (log_settings_changed)
|
if (log_settings_changed)
|
||||||
Q_EMIT gui_log_settings_request();
|
Q_EMIT gui_log_settings_request();
|
||||||
|
if (theme_music_settings_changed)
|
||||||
|
Q_EMIT gui_theme_music_settings_request();
|
||||||
|
|
||||||
emuenv.kernel.debugger.log_imports = m_ui->log_imports->isChecked();
|
emuenv.kernel.debugger.log_imports = m_ui->log_imports->isChecked();
|
||||||
emuenv.kernel.debugger.log_exports = m_ui->log_exports->isChecked();
|
emuenv.kernel.debugger.log_exports = m_ui->log_exports->isChecked();
|
||||||
@@ -756,7 +793,7 @@ void SettingsDialog::populate_modules_list() {
|
|||||||
const auto modules = config::get_modules_list(emuenv.pref_path, m_config.lle_modules);
|
const auto modules = config::get_modules_list(emuenv.pref_path, m_config.lle_modules);
|
||||||
for (const auto &[name, enabled] : modules) {
|
for (const auto &[name, enabled] : modules) {
|
||||||
auto *item = new QListWidgetItem(QString::fromStdString(name), m_ui->modules_list);
|
auto *item = new QListWidgetItem(QString::fromStdString(name), m_ui->modules_list);
|
||||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
item->setFlags((item->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsSelectable);
|
||||||
item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
||||||
}
|
}
|
||||||
update_modules_list_enabled();
|
update_modules_list_enabled();
|
||||||
@@ -951,6 +988,9 @@ void SettingsDialog::setup_connections() {
|
|||||||
connect(m_ui->audio_volume, &QSlider::valueChanged, this, [this](int val) {
|
connect(m_ui->audio_volume, &QSlider::valueChanged, this, [this](int val) {
|
||||||
m_ui->audio_volume_label->setText(tr("Current volume: %1%").arg(val));
|
m_ui->audio_volume_label->setText(tr("Current volume: %1%").arg(val));
|
||||||
});
|
});
|
||||||
|
connect(m_ui->theme_music_volume, &QSlider::valueChanged, this, [this](int val) {
|
||||||
|
m_ui->theme_music_volume_label->setText(tr("Theme music volume: %1%").arg(val));
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_ui->perf_overlay_enabled, &QCheckBox::toggled, this, [this](bool on) {
|
connect(m_ui->perf_overlay_enabled, &QCheckBox::toggled, this, [this](bool on) {
|
||||||
m_ui->label_perf_detail->setEnabled(on);
|
m_ui->label_perf_detail->setEnabled(on);
|
||||||
@@ -1122,6 +1162,8 @@ void SettingsDialog::setup_connections() {
|
|||||||
// Audio
|
// Audio
|
||||||
{ m_ui->audio_backend_box, tr("Audio Backend"), m_tooltips->audio_backend },
|
{ m_ui->audio_backend_box, tr("Audio Backend"), m_tooltips->audio_backend },
|
||||||
{ m_ui->audio_volume, tr("Volume"), m_tooltips->audio_volume },
|
{ m_ui->audio_volume, tr("Volume"), m_tooltips->audio_volume },
|
||||||
|
{ m_ui->theme_music_enable, tr("Enable Vita Theme Background Music"), m_tooltips->theme_music_enable },
|
||||||
|
{ m_ui->theme_music_volume, tr("Theme Music Volume"), m_tooltips->theme_music_volume },
|
||||||
{ m_ui->ngs_enable, tr("Enable NGS Support"), m_tooltips->ngs_enable },
|
{ m_ui->ngs_enable, tr("Enable NGS Support"), m_tooltips->ngs_enable },
|
||||||
// Camera
|
// Camera
|
||||||
{ m_ui->front_camera_box, tr("Front Camera Source"), m_tooltips->front_camera },
|
{ m_ui->front_camera_box, tr("Front Camera Source"), m_tooltips->front_camera },
|
||||||
@@ -1243,9 +1285,9 @@ void SettingsDialog::set_description(QWidget *tab, const QString &title_override
|
|||||||
}();
|
}();
|
||||||
|
|
||||||
if (text == m_tooltips->default_description) {
|
if (text == m_tooltips->default_description) {
|
||||||
m_ui->helpText->setHtml(format_help_html(title, m_tooltips->category_summary(static_cast<SettingsTab>(index))));
|
m_ui->helpText->setHtml(gui::utils::format_help_html(title, m_tooltips->category_summary(static_cast<SettingsTab>(index))));
|
||||||
} else {
|
} else {
|
||||||
m_ui->helpText->setHtml(format_help_html(title, text));
|
m_ui->helpText->setHtml(gui::utils::format_help_html(title, text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1392,30 +1434,27 @@ void SettingsDialog::update_gpu_visibility() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::add_stylesheets() {
|
void SettingsDialog::add_stylesheets(const QString &preferred_name) {
|
||||||
m_ui->combo_stylesheets->blockSignals(true);
|
m_ui->combo_stylesheets->blockSignals(true);
|
||||||
m_ui->combo_stylesheets->clear();
|
m_ui->combo_stylesheets->clear();
|
||||||
|
|
||||||
m_ui->combo_stylesheets->addItem(tr("None", "Stylesheets"), gui::NoStylesheet);
|
const auto themes = m_theme_manager.available_themes();
|
||||||
|
for (const auto &theme : themes)
|
||||||
|
m_ui->combo_stylesheets->addItem(theme.display_name, theme.name);
|
||||||
|
|
||||||
m_ui->combo_stylesheets->addItem(tr("Light", "Stylesheets"), gui::LightStylesheet);
|
if (!preferred_name.isEmpty())
|
||||||
m_ui->combo_stylesheets->addItem(tr("Dark", "Stylesheets"), gui::DarkStylesheet);
|
m_current_stylesheet = preferred_name;
|
||||||
|
else if (m_gui_settings)
|
||||||
if (m_gui_settings) {
|
m_current_stylesheet = m_gui_settings->get_value(gui::m_currentStylesheet).toString();
|
||||||
for (const QString &entry : m_gui_settings->get_stylesheet_entries()) {
|
else
|
||||||
if (entry != gui::LightStylesheet && entry != gui::DarkStylesheet) {
|
m_current_stylesheet.clear();
|
||||||
m_ui->combo_stylesheets->addItem(entry, entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_current_stylesheet = m_gui_settings
|
|
||||||
? m_gui_settings->get_value(gui::m_currentStylesheet).toString()
|
|
||||||
: gui::DarkStylesheet;
|
|
||||||
|
|
||||||
const int index = m_ui->combo_stylesheets->findData(m_current_stylesheet);
|
const int index = m_ui->combo_stylesheets->findData(m_current_stylesheet);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
m_ui->combo_stylesheets->setCurrentIndex(index);
|
m_ui->combo_stylesheets->setCurrentIndex(index);
|
||||||
|
} else if (m_ui->combo_stylesheets->count() > 0) {
|
||||||
|
m_ui->combo_stylesheets->setCurrentIndex(0);
|
||||||
|
m_current_stylesheet = m_ui->combo_stylesheets->currentData().toString();
|
||||||
}
|
}
|
||||||
m_ui->combo_stylesheets->blockSignals(false);
|
m_ui->combo_stylesheets->blockSignals(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1266,6 +1266,78 @@ QGroupBox[flat="true"]::title {
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_theme_music_section">
|
||||||
|
<property name="title">
|
||||||
|
<string>Theme Music</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="gb_theme_music_section_layout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_theme_music_controls">
|
||||||
|
<property name="title">
|
||||||
|
<string>Vita Theme Music</string>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="gb_theme_music_controls_layout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="theme_music_enable">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable Vita theme background music</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="theme_music_volume_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Theme music volume: 50%</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="theme_music_volume">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickPosition">
|
||||||
|
<enum>QSlider::TickPosition::TicksBelow</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickInterval">
|
||||||
|
<number>25</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="audio_tab_content_spacer">
|
<spacer name="audio_tab_content_spacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ SettingsDialogTooltips::SettingsDialogTooltips(QObject *parent)
|
|||||||
, memory_mapping(tr("Memory mapping improved performance, reduces memory usage and fixes many graphical issues. However, it may be unstable on some GPUs."))
|
, memory_mapping(tr("Memory mapping improved performance, reduces memory usage and fixes many graphical issues. However, it may be unstable on some GPUs."))
|
||||||
, audio_backend(tr("Select the audio backend. Cubeb is recommended for most systems."))
|
, audio_backend(tr("Select the audio backend. Cubeb is recommended for most systems."))
|
||||||
, audio_volume(tr("Set the in-game audio volume."))
|
, audio_volume(tr("Set the in-game audio volume."))
|
||||||
|
, theme_music_enable(tr("Enable background music for generated Vita themes when the selected theme provides an ATRAC9 BGM file."))
|
||||||
|
, theme_music_volume(tr("Set the playback volume for Vita theme background music. This only affects interface theme music."))
|
||||||
, ngs_enable(tr("Enable advanced audio library NGS support."))
|
, ngs_enable(tr("Enable advanced audio library NGS support."))
|
||||||
, front_camera(tr("Select the front camera source.\nSolid Color or Static Image can be used as substitutes."))
|
, front_camera(tr("Select the front camera source.\nSolid Color or Static Image can be used as substitutes."))
|
||||||
, back_camera(tr("Select the back camera source.\nSolid Color or Static Image can be used as substitutes."))
|
, back_camera(tr("Select the back camera source.\nSolid Color or Static Image can be used as substitutes."))
|
||||||
|
|||||||
@@ -1,321 +0,0 @@
|
|||||||
// Vita3K emulator project
|
|
||||||
// Copyright (C) 2026 Vita3K team
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation; either version 2 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
#include <gui-qt/stylesheets.h>
|
|
||||||
|
|
||||||
namespace gui::stylesheets {
|
|
||||||
|
|
||||||
// Light built-in stylesheet
|
|
||||||
const QString light_style_sheet(
|
|
||||||
"QWidget { color: #202020; background-color: #f5f5f5; }"
|
|
||||||
"QWidget:disabled { color: #8a8a8a; }"
|
|
||||||
"QMainWindow { background-color: #f5f5f5; }"
|
|
||||||
"QDialog { background-color: #f5f5f5; }"
|
|
||||||
"QMenuBar { background-color: #f0f0f0; color: #202020; }"
|
|
||||||
"QMenuBar::item { background: transparent; padding: 4px 8px; }"
|
|
||||||
"QMenuBar::item:selected { background: #e1e1e1; }"
|
|
||||||
"QMenu { background-color: #f7f7f7; color: #202020; border: 1px solid #c6c6c6; }"
|
|
||||||
"QMenu::item:selected { background-color: #d6e8ff; color: #202020; }"
|
|
||||||
"QMenu::item:disabled { color: #808080; }"
|
|
||||||
"QMenu::separator { height: 1px; background: #d0d0d0; margin: 4px 8px; }"
|
|
||||||
"QToolBar#mw_toolbar { background-color: #f0f0f0; border: none; border-top: 1px solid #cfcfcf; }"
|
|
||||||
"QToolBar#mw_toolbar::separator { background-color: rgba(207, 207, 207, 235); width: 0.125em; margin-top: 0.250em; margin-bottom: 0.250em; }"
|
|
||||||
"QToolButton { color: #202020; background: transparent; border: 1px solid transparent; padding: 3px; }"
|
|
||||||
"QToolButton:hover { background-color: #e8e8e8; border: 1px solid #bdbdbd; }"
|
|
||||||
"QToolButton:pressed { background-color: #d8d8d8; color: #202020; }"
|
|
||||||
"QToolButton:disabled { color: #808080; }"
|
|
||||||
"QToolButton#welcome_firmware_button { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 4px 32px 4px 12px; text-align: left; }"
|
|
||||||
"QToolButton#welcome_firmware_button:hover { background-color: #f3f3f3; border-color: #9f9f9f; }"
|
|
||||||
"QToolButton#welcome_firmware_button:pressed { background-color: #e3e3e3; }"
|
|
||||||
"QToolButton#welcome_firmware_button::menu-button { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border-left: 1px solid #b8b8b8; border-top-right-radius: 2px; border-bottom-right-radius: 2px; background: #f3f3f3; }"
|
|
||||||
"QToolButton#welcome_firmware_button::menu-button:hover { background: #e8e8e8; border-left-color: #9f9f9f; }"
|
|
||||||
"QToolButton#welcome_firmware_button::menu-button:pressed { background: #dcdcdc; }"
|
|
||||||
"QLabel#toolbar_icon_color { color: #5b5b5b; }"
|
|
||||||
"QPushButton { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 4px 16px; min-height: 1.2em; }"
|
|
||||||
"QPushButton:hover { background-color: #f3f3f3; border-color: #9f9f9f; }"
|
|
||||||
"QPushButton:pressed { background-color: #e3e3e3; }"
|
|
||||||
"QPushButton:disabled { color: #8a8a8a; background-color: #efefef; border-color: #d4d4d4; }"
|
|
||||||
"QPushButton:default { border-color: #148aff; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton { background-color: #ececec; color: #202020; border: none; border-radius: 6px; padding: 6px 12px; outline: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton:hover { background-color: #e3e3e3; border: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton:pressed { background-color: #d8d8d8; border: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton:disabled { color: #8a8a8a; background-color: #e4e4e4; border: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"] { background-color: #e6e6e6; border-radius: 6px; padding: 6px 12px; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"]:hover { background-color: #dddddd; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"]:pressed { background-color: #d2d2d2; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"][captureActive=\"true\"] { background-color: #148aff; color: #ffffff; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"][captureActive=\"true\"]:hover { background-color: #2a97ff; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"][captureActive=\"true\"]:pressed { background-color: #0f74d6; }"
|
|
||||||
"QStatusBar#emulatorStatusBar { background-color: #f0f0f0; color: #202020; }"
|
|
||||||
"QStatusBar#emulatorStatusBar::item { border: none; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button { border: 1px solid transparent; padding: 2px 8px; font-weight: bold; font-size: 11px; background: transparent; border-radius: 6px; color: #202020; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button:hover { border: 1px solid #a0a0a0; background: rgba(0, 0, 0, 0.03); }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button:pressed { border: 1px solid #8a8a8a; background: rgba(0, 0, 0, 0.06); }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"renderer_vulkan\"] { color: #ff4444; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"renderer_opengl\"] { color: #4488ff; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"high\"] { color: #ff8800; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"standard\"] { color: #aaaaaa; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"neutral\"] { color: #5f5f5f; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"audio\"] { color: #4b9b2f; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"muted\"] { color: #777777; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"update\"] { color: #1f8f4e; }"
|
|
||||||
"QLineEdit { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 2px 4px; selection-background-color: #148aff; selection-color: #ffffff; }"
|
|
||||||
"QLineEdit:focus { border-color: #148aff; }"
|
|
||||||
"QLineEdit:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }"
|
|
||||||
"QComboBox { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 2px 28px 2px 6px; }"
|
|
||||||
"QComboBox:hover { border-color: #9f9f9f; }"
|
|
||||||
"QComboBox:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }"
|
|
||||||
"QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }"
|
|
||||||
"QComboBox::down-arrow { image: url(:/icons/chevron_down_dark.svg); width: 10px; height: 6px; }"
|
|
||||||
"QComboBox::down-arrow:on { top: 1px; }"
|
|
||||||
"QComboBox QAbstractItemView { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; selection-background-color: #148aff; selection-color: #ffffff; }"
|
|
||||||
"QSpinBox, QDoubleSpinBox { background-color: #ffffff; color: #202020; border: 1px solid #b8b8b8; border-radius: 2px; padding: 2px 24px 2px 6px; selection-background-color: #148aff; selection-color: #ffffff; }"
|
|
||||||
"QSpinBox:disabled, QDoubleSpinBox:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }"
|
|
||||||
"QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 18px; border: none; background: transparent; }"
|
|
||||||
"QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 18px; border: none; background: transparent; }"
|
|
||||||
"QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { image: url(:/icons/chevron_up_dark.svg); width: 10px; height: 6px; }"
|
|
||||||
"QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { image: url(:/icons/chevron_down_dark.svg); width: 10px; height: 6px; }"
|
|
||||||
"QCheckBox { color: #202020; spacing: 6px; }"
|
|
||||||
"QCheckBox:disabled { color: #8a8a8a; }"
|
|
||||||
"QRadioButton { color: #202020; spacing: 6px; }"
|
|
||||||
"QRadioButton:disabled { color: #8a8a8a; }"
|
|
||||||
"QGroupBox { color: #202020; border: 1px solid #c8c8c8; border-radius: 4px; margin-top: 0.5em; padding-top: 0.6em; }"
|
|
||||||
"QGroupBox:disabled { color: #9a9a9a; border-color: #d8d8d8; }"
|
|
||||||
"QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: #202020; }"
|
|
||||||
"QLabel { color: #202020; background: transparent; }"
|
|
||||||
"QLabel:disabled { color: #8a8a8a; }"
|
|
||||||
"QTableWidget, QTableView { alternate-background-color: #f2f2f2; background-color: #ffffff; border: none; color: #202020; gridline-color: #e0e0e0; }"
|
|
||||||
"QTableWidget:disabled, QTableView:disabled { color: #8a8a8a; background-color: #f0f0f0; }"
|
|
||||||
"QTableCornerButton::section { background-color: #ececec; border: 1px solid #d4d4d4; }"
|
|
||||||
"QTableView::item { border-left: 0.063em solid #ffffff; border-right: 0.063em solid #ffffff; padding-left: 0.313em; }"
|
|
||||||
"QTableView::item:selected { background-color: #148aff; color: #ffffff; }"
|
|
||||||
"QTableWidget#apps_list_table::item:selected, QTableView#apps_list_table::item:selected { background-color: rgba(84, 140, 188, 48); color: #202020; border-left: 0.063em solid rgba(84, 140, 188, 0); border-right: 0.063em solid rgba(84, 140, 188, 0); }"
|
|
||||||
"QTableView#apps_list_table QHeaderView::section, QTableWidget#apps_list_table QHeaderView::section { padding-top: 0; padding-bottom: 0; }"
|
|
||||||
"QTableView#trophy_app_table::item:selected, QTableView#trophy_table::item:selected { background-color: rgba(84, 140, 188, 48); color: #202020; border-left: 0.063em solid rgba(84, 140, 188, 0); border-right: 0.063em solid rgba(84, 140, 188, 0); }"
|
|
||||||
"QHeaderView::section { background-color: #ececec; color: #202020; padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }"
|
|
||||||
"QHeaderView::section:hover { background: #e0e0e0; }"
|
|
||||||
"QDockWidget { background: transparent; color: #202020; }"
|
|
||||||
"QDockWidget[floating=\"true\"] { background: #f5f5f5; }"
|
|
||||||
"QDockWidget::title { background: #ececec; border: none; padding-top: 0.2em; padding-left: 0.2em; }"
|
|
||||||
"QDockWidget::close-button, QDockWidget::float-button { background-color: #ececec; border: none; }"
|
|
||||||
"QDockWidget::close-button:hover, QDockWidget::float-button:hover { background-color: #e0e0e0; }"
|
|
||||||
"QPlainTextEdit#log_frame { background-color: #ffffff; color: #202020; }"
|
|
||||||
"QWidget#log_search_container { background-color: #ffffff; border: 1px solid #c8c8c8; border-radius: 4px; }"
|
|
||||||
"QWidget#toolbar_icon_size_container { background-color: #f0f0f0; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider { background: transparent; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::sub-page:horizontal { background: #7f7f7f; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::add-page:horizontal { background: #c8c8c8; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::handle:horizontal { background: #5b5b5b; width: 14px; margin: -5px 0; border-radius: 7px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::handle:horizontal:hover { background: #404040; }"
|
|
||||||
"QLabel#log_level_fatal { color: #ff00ff; }"
|
|
||||||
"QLabel#log_level_error { color: #C02F1D; }"
|
|
||||||
"QLabel#log_level_warning { color: #BA8745; }"
|
|
||||||
"QLabel#log_level_notice { color: #000000; }"
|
|
||||||
"QLabel#log_level_trace { color: #808080; }"
|
|
||||||
"QTabWidget::pane { border: 1px solid #c8c8c8; background-color: #f5f5f5; }"
|
|
||||||
"QTabBar::tab { background-color: #e9e9e9; color: #202020; padding: 6px 14px; border: 1px solid #c8c8c8; border-bottom: none; border-top-left-radius: 3px; border-top-right-radius: 3px; }"
|
|
||||||
"QTabBar::tab:selected { background-color: #f5f5f5; }"
|
|
||||||
"QTabBar::tab:!selected { margin-top: 2px; }"
|
|
||||||
"QTabBar::tab:hover:!selected { background-color: #dfdfdf; }"
|
|
||||||
"QListWidget { background-color: #ffffff; color: #202020; border: 1px solid #c8c8c8; }"
|
|
||||||
"QListWidget:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d6d6d6; }"
|
|
||||||
"QListWidget::item:selected { background-color: #148aff; color: #ffffff; }"
|
|
||||||
"QListWidget::item:hover:!selected { background-color: #f2f2f2; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory { background-color: #ffffff; border: none; border-radius: 10px; padding: 6px; outline: none; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item { background: transparent; border: none; border-radius: 10px; padding: 8px 12px; margin: 0px; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:selected,"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:selected:active,"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:selected:!active { background-color: #148aff; color: #ffffff; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:hover:!selected { background-color: #f2f2f2; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QTextBrowser#helpText { background-color: #ffffff; color: #202020; border: none; border-radius: 10px; padding: 3px 4px; }"
|
|
||||||
"QScrollBar:vertical { background: #f0f0f0; width: 14px; margin: 0; }"
|
|
||||||
"QScrollBar::handle:vertical { background: #b0b0b0; min-height: 20px; border-radius: 3px; margin: 2px; }"
|
|
||||||
"QScrollBar::handle:vertical:hover { background: #989898; }"
|
|
||||||
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }"
|
|
||||||
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }"
|
|
||||||
"QScrollBar:horizontal { background: #f0f0f0; height: 14px; margin: 0; }"
|
|
||||||
"QScrollBar::handle:horizontal { background: #b0b0b0; min-width: 20px; border-radius: 3px; margin: 2px; }"
|
|
||||||
"QScrollBar::handle:horizontal:hover { background: #989898; }"
|
|
||||||
"QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }"
|
|
||||||
"QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; }"
|
|
||||||
"QSlider::groove:horizontal { background: #c8c8c8; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::handle:horizontal { background: #7f7f7f; width: 14px; margin: -5px 0; border-radius: 7px; }"
|
|
||||||
"QSlider::handle:horizontal:hover { background: #666666; }"
|
|
||||||
"QSlider::groove:vertical { background: #c8c8c8; width: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::handle:vertical { background: #7f7f7f; height: 14px; margin: 0 -5px; border-radius: 7px; }"
|
|
||||||
"QSlider::handle:vertical:hover { background: #666666; }"
|
|
||||||
"QProgressBar { background-color: #ffffff; color: #202020; border: 1px solid #c8c8c8; text-align: center; border-radius: 2px; }"
|
|
||||||
"QProgressBar::chunk { background-color: #148aff; border-radius: 2px; }"
|
|
||||||
"QSplitter::handle { background-color: #d4d4d4; }"
|
|
||||||
"QToolTip { background-color: #f7f7f7; color: #202020; border: 1px solid #c8c8c8; padding: 2px; }"
|
|
||||||
"QPlainTextEdit, QTextEdit, QTextBrowser { background-color: #ffffff; color: #202020; border: 1px solid #c8c8c8; }"
|
|
||||||
"QPlainTextEdit:disabled, QTextEdit:disabled, QTextBrowser:disabled { color: #8a8a8a; background-color: #f0f0f0; border-color: #d0d0d0; }"
|
|
||||||
"QDialogButtonBox QPushButton { min-width: 80px; }");
|
|
||||||
|
|
||||||
const QString dark_style_sheet(
|
|
||||||
"QWidget { color: #e0e0e0; background-color: #353535; }"
|
|
||||||
"QWidget:disabled { color: #777777; }"
|
|
||||||
"QMainWindow { background-color: #353535; }"
|
|
||||||
"QDialog { background-color: #353535; }"
|
|
||||||
"QMenuBar { background-color: #353535; color: #e0e0e0; }"
|
|
||||||
"QMenuBar::item { background: transparent; padding: 4px 8px; }"
|
|
||||||
"QMenuBar::item:selected { background: #4a4a4a; }"
|
|
||||||
"QMenu { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; }"
|
|
||||||
"QMenu::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }"
|
|
||||||
"QMenu::item:disabled { color: #808080; }"
|
|
||||||
"QMenu::separator { height: 1px; background: #555555; margin: 4px 8px; }"
|
|
||||||
"QToolBar#mw_toolbar { background-color: #353535; border: none; border-top: 1px solid #555555; }"
|
|
||||||
"QToolBar#mw_toolbar::separator { background-color: rgba(100, 100, 100, 235); width: 0.125em; margin-top: 0.250em; margin-bottom: 0.250em; }"
|
|
||||||
"QToolButton { color: #e0e0e0; background: transparent; border: 1px solid transparent; padding: 3px; }"
|
|
||||||
"QToolButton:hover { background-color: #4a4a4a; border: 1px solid #666666; }"
|
|
||||||
"QToolButton:pressed { background-color: rgba(255, 255, 255, 0.12); }"
|
|
||||||
"QToolButton:disabled { color: #808080; }"
|
|
||||||
"QToolButton#welcome_firmware_button { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 4px 32px 4px 12px; text-align: left; }"
|
|
||||||
"QToolButton#welcome_firmware_button:hover { background-color: #4a4a4a; border-color: #666666; }"
|
|
||||||
"QToolButton#welcome_firmware_button:pressed { background-color: rgba(255, 255, 255, 0.12); }"
|
|
||||||
"QToolButton#welcome_firmware_button::menu-button { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border-left: 1px solid #555555; border-top-right-radius: 2px; border-bottom-right-radius: 2px; background: #303030; }"
|
|
||||||
"QToolButton#welcome_firmware_button::menu-button:hover { background: #444444; border-left-color: #666666; }"
|
|
||||||
"QToolButton#welcome_firmware_button::menu-button:pressed { background: rgba(255, 255, 255, 0.12); }"
|
|
||||||
"QLabel#toolbar_icon_color { color: #f3f3f3; }"
|
|
||||||
"QPushButton { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 4px 16px; min-height: 1.2em; }"
|
|
||||||
"QPushButton:hover { background-color: #4a4a4a; border-color: #666666; }"
|
|
||||||
"QPushButton:pressed { background-color: rgba(255, 255, 255, 0.12); }"
|
|
||||||
"QPushButton:disabled { color: #808080; background-color: #303030; border-color: #444444; }"
|
|
||||||
"QPushButton:default { border-color: rgba(255, 255, 255, 0.24); }"
|
|
||||||
"QDialog#ControlsDialog QPushButton { background-color: #303030; color: #e0e0e0; border: none; border-radius: 6px; padding: 6px 12px; outline: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton:hover { background-color: #3a3a3a; border: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton:pressed { background-color: #464646; border: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton:disabled { color: #808080; background-color: #2c2c2c; border: none; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"] { background-color: #2f2f2f; border-radius: 6px; padding: 6px 12px; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"]:hover { background-color: #393939; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"]:pressed { background-color: #454545; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"][captureActive=\"true\"] { background-color: rgba(255, 255, 255, 0.18); color: #ffffff; }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"][captureActive=\"true\"]:hover { background-color: rgba(255, 255, 255, 0.24); }"
|
|
||||||
"QDialog#ControlsDialog QPushButton[bindButton=\"true\"][captureActive=\"true\"]:pressed { background-color: rgba(255, 255, 255, 0.30); }"
|
|
||||||
"QStatusBar#emulatorStatusBar { background-color: #353535; color: #e0e0e0; }"
|
|
||||||
"QStatusBar#emulatorStatusBar::item { border: none; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button { border: 1px solid transparent; padding: 2px 8px; font-weight: bold; font-size: 11px; background: transparent; border-radius: 6px; color: #e0e0e0; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button:hover { border: 1px solid #666666; background: rgba(255, 255, 255, 0.04); }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button:pressed { border: 1px solid #888888; background: rgba(255, 255, 255, 0.07); }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"renderer_vulkan\"] { color: #ff4444; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"renderer_opengl\"] { color: #4488ff; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"high\"] { color: #ff8800; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"standard\"] { color: #aaaaaa; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"neutral\"] { color: #cccccc; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"audio\"] { color: #88dd44; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"muted\"] { color: #777777; }"
|
|
||||||
"QStatusBar#emulatorStatusBar QPushButton#status_button[accent=\"update\"] { color: #6de089; }"
|
|
||||||
"QLineEdit { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 2px 4px; selection-background-color: rgba(255, 255, 255, 0.18); selection-color: #ffffff; }"
|
|
||||||
"QLineEdit:focus { border-color: rgba(255, 255, 255, 0.24); }"
|
|
||||||
"QLineEdit:disabled { color: #777777; background-color: #303030; border-color: #444444; }"
|
|
||||||
"QComboBox { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 2px 28px 2px 6px; }"
|
|
||||||
"QComboBox:hover { border-color: #666666; }"
|
|
||||||
"QComboBox:disabled { color: #777777; background-color: #303030; border-color: #444444; }"
|
|
||||||
"QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }"
|
|
||||||
"QComboBox::down-arrow { image: url(:/icons/chevron_down_light.svg); width: 10px; height: 6px; }"
|
|
||||||
"QComboBox::down-arrow:on { top: 1px; }"
|
|
||||||
"QComboBox QAbstractItemView { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; selection-background-color: rgba(255, 255, 255, 0.12); selection-color: #ffffff; }"
|
|
||||||
"QSpinBox, QDoubleSpinBox { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; border-radius: 2px; padding: 2px 24px 2px 6px; selection-background-color: rgba(255, 255, 255, 0.18); selection-color: #ffffff; }"
|
|
||||||
"QSpinBox:disabled, QDoubleSpinBox:disabled { color: #777777; background-color: #303030; border-color: #444444; }"
|
|
||||||
"QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 18px; border: none; background: transparent; }"
|
|
||||||
"QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 18px; border: none; background: transparent; }"
|
|
||||||
"QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { image: url(:/icons/chevron_up_light.svg); width: 10px; height: 6px; }"
|
|
||||||
"QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { image: url(:/icons/chevron_down_light.svg); width: 10px; height: 6px; }"
|
|
||||||
"QCheckBox { color: #e0e0e0; spacing: 6px; }"
|
|
||||||
"QCheckBox:disabled { color: #777777; }"
|
|
||||||
"QRadioButton { color: #e0e0e0; spacing: 6px; }"
|
|
||||||
"QRadioButton:disabled { color: #777777; }"
|
|
||||||
"QGroupBox { color: #e0e0e0; border: 1px solid #555555; border-radius: 4px; margin-top: 0.5em; padding-top: 0.6em; }"
|
|
||||||
"QGroupBox:disabled { color: #7f7f7f; border-color: #4a4a4a; }"
|
|
||||||
"QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: #e0e0e0; }"
|
|
||||||
"QLabel { color: #e0e0e0; background: transparent; }"
|
|
||||||
"QLabel:disabled { color: #777777; }"
|
|
||||||
"QTableWidget, QTableView { alternate-background-color: #3a3a3a; background-color: #2a2a2a; border: none; color: #e0e0e0; gridline-color: #444444; }"
|
|
||||||
"QTableWidget:disabled, QTableView:disabled { color: #777777; background-color: #303030; }"
|
|
||||||
"QTableCornerButton::section { background-color: #353535; border: 1px solid #555555; }"
|
|
||||||
"QTableView::item { border-left: 0.063em solid #2a2a2a; border-right: 0.063em solid #2a2a2a; padding-left: 0.313em; }"
|
|
||||||
"QTableView::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }"
|
|
||||||
"QTableWidget#apps_list_table::item:selected, QTableView#apps_list_table::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #f0f0f0; border-left: 0.063em solid rgba(255, 255, 255, 0); border-right: 0.063em solid rgba(255, 255, 255, 0); }"
|
|
||||||
"QTableView#apps_list_table QHeaderView::section, QTableWidget#apps_list_table QHeaderView::section { padding-top: 0; padding-bottom: 0; }"
|
|
||||||
"QTableView#trophy_app_table::item:selected, QTableView#trophy_table::item:selected { background-color: rgba(255, 255, 255, 0.13); color: #ffffff; border-left: 0.063em solid rgba(255, 255, 255, 0); border-right: 0.063em solid rgba(255, 255, 255, 0); }"
|
|
||||||
"QHeaderView::section { background-color: #353535; color: #ffffff; padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #2a2a2a; }"
|
|
||||||
"QHeaderView::section:hover { background: #4a4a4a; }"
|
|
||||||
"QDockWidget { background: transparent; color: #ffffff; }"
|
|
||||||
"QDockWidget[floating=\"true\"] { background: #353535; }"
|
|
||||||
"QDockWidget::title { background: #353535; border: none; padding-top: 0.2em; padding-left: 0.2em; }"
|
|
||||||
"QDockWidget::close-button, QDockWidget::float-button { background-color: #353535; border: none; }"
|
|
||||||
"QDockWidget::close-button:hover, QDockWidget::float-button:hover { background-color: #4a4a4a; }"
|
|
||||||
"QPlainTextEdit#log_frame { background-color: #2a2a2a; color: #ffffff; }"
|
|
||||||
"QWidget#log_search_container { background-color: #2a2a2a; border: 1px solid #555555; border-radius: 4px; }"
|
|
||||||
"QWidget#toolbar_icon_size_container { background-color: #353535; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider { background: transparent; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::sub-page:horizontal { background: rgba(255, 255, 255, 0.82); height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::add-page:horizontal { background: #555555; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::handle:horizontal { background: #f3f3f3; width: 14px; margin: -5px 0; border-radius: 7px; }"
|
|
||||||
"QSlider#toolbar_icon_size_slider::handle:horizontal:hover { background: #ffffff; }"
|
|
||||||
"QLabel#log_level_fatal { color: #ff66ff; }"
|
|
||||||
"QLabel#log_level_error { color: #ff8080; }"
|
|
||||||
"QLabel#log_level_warning { color: #ffd27f; }"
|
|
||||||
"QLabel#log_level_notice { color: #ffffff; }"
|
|
||||||
"QLabel#log_level_trace { color: #ffffff; }"
|
|
||||||
"QTabWidget::pane { border: 1px solid #555555; background-color: #353535; }"
|
|
||||||
"QTabBar::tab { background-color: #2d2d2d; color: #e0e0e0; padding: 6px 14px; border: 1px solid #555555; border-bottom: none; border-top-left-radius: 3px; border-top-right-radius: 3px; }"
|
|
||||||
"QTabBar::tab:selected { background-color: #353535; }"
|
|
||||||
"QTabBar::tab:!selected { margin-top: 2px; }"
|
|
||||||
"QTabBar::tab:hover:!selected { background-color: #3a3a3a; }"
|
|
||||||
"QListWidget { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; }"
|
|
||||||
"QListWidget:disabled { color: #777777; background-color: #303030; border-color: #444444; }"
|
|
||||||
"QListWidget::item:selected { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }"
|
|
||||||
"QListWidget::item:hover:!selected { background-color: #3a3a3a; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory { background-color: #2a2a2a; border: none; border-radius: 10px; padding: 6px; outline: none; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item { background: transparent; border: none; border-radius: 10px; padding: 8px 12px; margin: 0px; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:selected,"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:selected:active,"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:selected:!active { background-color: rgba(255, 255, 255, 0.12); color: #ffffff; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QListWidget#settingsCategory::item:hover:!selected { background-color: #3a3a3a; }"
|
|
||||||
"QDialog[class=\"settings-window\"] QTextBrowser#helpText { background-color: #2a2a2a; color: #e0e0e0; border: none; border-radius: 10px; padding: 3px 4px; }"
|
|
||||||
"QScrollBar:vertical { background: #2a2a2a; width: 14px; margin: 0; }"
|
|
||||||
"QScrollBar::handle:vertical { background: #5a5a5a; min-height: 20px; border-radius: 3px; margin: 2px; }"
|
|
||||||
"QScrollBar::handle:vertical:hover { background: #6a6a6a; }"
|
|
||||||
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }"
|
|
||||||
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }"
|
|
||||||
"QScrollBar:horizontal { background: #2a2a2a; height: 14px; margin: 0; }"
|
|
||||||
"QScrollBar::handle:horizontal { background: #5a5a5a; min-width: 20px; border-radius: 3px; margin: 2px; }"
|
|
||||||
"QScrollBar::handle:horizontal:hover { background: #6a6a6a; }"
|
|
||||||
"QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }"
|
|
||||||
"QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; }"
|
|
||||||
"QSlider::groove:horizontal { background: transparent; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::sub-page:horizontal { background: rgba(255, 255, 255, 0.82); height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::add-page:horizontal { background: #555555; height: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::handle:horizontal { background: #c0c0c0; width: 14px; margin: -5px 0; border-radius: 7px; }"
|
|
||||||
"QSlider::handle:horizontal:hover { background: #ffffff; }"
|
|
||||||
"QSlider::groove:vertical { background: transparent; width: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::sub-page:vertical { background: rgba(255, 255, 255, 0.82); width: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::add-page:vertical { background: #555555; width: 4px; border-radius: 2px; }"
|
|
||||||
"QSlider::handle:vertical { background: #c0c0c0; height: 14px; margin: 0 -5px; border-radius: 7px; }"
|
|
||||||
"QSlider::handle:vertical:hover { background: #ffffff; }"
|
|
||||||
"QProgressBar { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; text-align: center; border-radius: 2px; }"
|
|
||||||
"QProgressBar::chunk { background-color: #6a6a6a; border-radius: 2px; }"
|
|
||||||
"QSplitter::handle { background-color: #555555; }"
|
|
||||||
"QToolTip { background-color: #353535; color: #e0e0e0; border: 1px solid #555555; padding: 2px; }"
|
|
||||||
"QPlainTextEdit, QTextEdit, QTextBrowser { background-color: #2a2a2a; color: #e0e0e0; border: 1px solid #555555; }"
|
|
||||||
"QPlainTextEdit:disabled, QTextEdit:disabled, QTextBrowser:disabled { color: #777777; background-color: #303030; border-color: #444444; }"
|
|
||||||
"QDialogButtonBox QPushButton { min-width: 80px; }");
|
|
||||||
|
|
||||||
} // namespace gui::stylesheets
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#include <gui-qt/theme_surface.h>
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
ThemeSurface::ThemeSurface(QWidget *parent)
|
||||||
|
: QWidget(parent) {
|
||||||
|
setAttribute(Qt::WA_StyledBackground, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeSurface::paintEvent(QPaintEvent *event) {
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
|
QStyleOption opt;
|
||||||
|
opt.initFrom(this);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
|
||||||
|
}
|
||||||
@@ -128,7 +128,7 @@ TrophyCollectionDialog::TrophyCollectionDialog(EmuEnvState &emuenv,
|
|||||||
, m_emuenv(emuenv)
|
, m_emuenv(emuenv)
|
||||||
, m_gui_settings(std::move(gui_settings)) {
|
, m_gui_settings(std::move(gui_settings)) {
|
||||||
setWindowTitle(tr("Trophy Collection"));
|
setWindowTitle(tr("Trophy Collection"));
|
||||||
setObjectName(QStringLiteral("TrophyCollectionDialog"));
|
setObjectName(QStringLiteral("trophy_collection_dialog"));
|
||||||
setWindowModality(Qt::NonModal);
|
setWindowModality(Qt::NonModal);
|
||||||
setWindowFlag(Qt::WindowSystemMenuHint, true);
|
setWindowFlag(Qt::WindowSystemMenuHint, true);
|
||||||
setWindowFlag(Qt::WindowMinMaxButtonsHint, true);
|
setWindowFlag(Qt::WindowMinMaxButtonsHint, true);
|
||||||
|
|||||||
@@ -0,0 +1,921 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#include <gui-qt/qt_utils.h>
|
||||||
|
#include <gui-qt/vita_theme.h>
|
||||||
|
|
||||||
|
#include <util/log.h>
|
||||||
|
#include <util/string_utils.h>
|
||||||
|
#include <util/vita_theme_utils.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cmath>
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string lowercase_copy(std::string value) {
|
||||||
|
std::transform(value.begin(), value.end(), value.begin(), [](const unsigned char ch) {
|
||||||
|
return static_cast<char>(std::tolower(ch));
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalize_locale(std::string locale) {
|
||||||
|
locale = string_utils::trim_copy(std::move(locale));
|
||||||
|
std::replace(locale.begin(), locale.end(), '_', '-');
|
||||||
|
return lowercase_copy(std::move(locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path_leaf_string(const fs::path &path) {
|
||||||
|
return fs_utils::path_to_utf8(path.filename());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string child_text(const pugi::xml_node &parent, const char *name) {
|
||||||
|
return string_utils::trim_copy(parent.child(name).text().as_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string localized_text(const pugi::xml_node &node, const std::string &preferred_locale) {
|
||||||
|
if (!node)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const std::string normalized_locale = normalize_locale(preferred_locale);
|
||||||
|
const auto try_key = [&node](const std::string &name) -> std::string {
|
||||||
|
return child_text(node, name.c_str());
|
||||||
|
};
|
||||||
|
const auto try_locale = [&try_key](const std::string &name) -> std::string {
|
||||||
|
if (name.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (const std::string exact = try_key(name); !exact.empty())
|
||||||
|
return exact;
|
||||||
|
|
||||||
|
return try_key("m_" + name);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!normalized_locale.empty()) {
|
||||||
|
if (const std::string exact = try_locale(normalized_locale); !exact.empty())
|
||||||
|
return exact;
|
||||||
|
|
||||||
|
if (const auto separator = normalized_locale.find('-'); separator != std::string::npos) {
|
||||||
|
if (const std::string base = try_locale(normalized_locale.substr(0, separator)); !base.empty())
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const std::string fallback = child_text(node, "m_default"); !fallback.empty())
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
for (const auto &child : node.children()) {
|
||||||
|
if (child.type() != pugi::node_element)
|
||||||
|
continue;
|
||||||
|
if (const std::string value = string_utils::trim_copy(child.text().as_string()); !value.empty())
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path resolve_asset_path(const fs::path &theme_dir, std::string asset_value) {
|
||||||
|
const QString relative_path = gui::utils::sanitize_relative_path_reference(
|
||||||
|
QString::fromStdString(string_utils::trim_copy(std::move(asset_value))));
|
||||||
|
if (relative_path.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return (theme_dir / gui::utils::to_fs_path(relative_path)).lexically_normal();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<VitaThemeColor> parse_theme_color(std::string raw_value) {
|
||||||
|
raw_value = string_utils::trim_copy(std::move(raw_value));
|
||||||
|
if (raw_value.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (raw_value.front() == '#')
|
||||||
|
raw_value.erase(raw_value.begin());
|
||||||
|
else if (raw_value.starts_with("0x") || raw_value.starts_with("0X"))
|
||||||
|
raw_value.erase(0, 2);
|
||||||
|
|
||||||
|
unsigned long long value = 0;
|
||||||
|
try {
|
||||||
|
value = std::stoull(raw_value, nullptr, 16);
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
try {
|
||||||
|
value = std::stoull(raw_value, nullptr, 10);
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
LOG_WARN("Unable to parse Vita theme color '{}'", raw_value);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw_value.size() <= 6 && value <= 0xFFFFFFULL) {
|
||||||
|
return VitaThemeColor{
|
||||||
|
static_cast<std::uint8_t>((value >> 16) & 0xFF),
|
||||||
|
static_cast<std::uint8_t>((value >> 8) & 0xFF),
|
||||||
|
static_cast<std::uint8_t>(value & 0xFF),
|
||||||
|
255,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return VitaThemeColor{
|
||||||
|
static_cast<std::uint8_t>((value >> 16) & 0xFF),
|
||||||
|
static_cast<std::uint8_t>((value >> 8) & 0xFF),
|
||||||
|
static_cast<std::uint8_t>(value & 0xFF),
|
||||||
|
static_cast<std::uint8_t>((value >> 24) & 0xFF),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
double luminance(const VitaThemeColor &color) {
|
||||||
|
return (0.2126 * color.red + 0.7152 * color.green + 0.0722 * color.blue) / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double color_distance(const VitaThemeColor &lhs, const VitaThemeColor &rhs) {
|
||||||
|
const auto component_delta = [](const std::uint8_t first, const std::uint8_t second) {
|
||||||
|
return static_cast<double>(static_cast<int>(first) - static_cast<int>(second)) / 255.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const double red = component_delta(lhs.red, rhs.red);
|
||||||
|
const double green = component_delta(lhs.green, rhs.green);
|
||||||
|
const double blue = component_delta(lhs.blue, rhs.blue);
|
||||||
|
return std::sqrt((red * red + green * green + blue * blue) / 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeColor mix(const VitaThemeColor &lhs, const VitaThemeColor &rhs, const double ratio) {
|
||||||
|
const double clamped = std::clamp(ratio, 0.0, 1.0);
|
||||||
|
const auto blend = [clamped](const std::uint8_t first, const std::uint8_t second) {
|
||||||
|
return static_cast<std::uint8_t>(std::lround((first * (1.0 - clamped)) + (second * clamped)));
|
||||||
|
};
|
||||||
|
|
||||||
|
return VitaThemeColor{
|
||||||
|
blend(lhs.red, rhs.red),
|
||||||
|
blend(lhs.green, rhs.green),
|
||||||
|
blend(lhs.blue, rhs.blue),
|
||||||
|
blend(lhs.alpha, rhs.alpha),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeColor darken(const VitaThemeColor &color, const double ratio) {
|
||||||
|
return mix(color, VitaThemeColor{}, ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeColor lighten(const VitaThemeColor &color, const double ratio) {
|
||||||
|
return mix(color, VitaThemeColor{ 255, 255, 255, 255 }, ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeColor contrasting_text(const VitaThemeColor &background) {
|
||||||
|
return luminance(background) > 0.55
|
||||||
|
? VitaThemeColor{ 10, 18, 24, 255 }
|
||||||
|
: VitaThemeColor{ 245, 247, 250, 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeColor ensure_readable(const VitaThemeColor &foreground, const VitaThemeColor &background) {
|
||||||
|
return std::abs(luminance(foreground) - luminance(background)) < 0.28
|
||||||
|
? contrasting_text(background)
|
||||||
|
: foreground;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string css_hex(const VitaThemeColor &color) {
|
||||||
|
return fmt::format("#{:02X}{:02X}{:02X}", color.red, color.green, color.blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string css_argb(const VitaThemeColor &color, const std::uint8_t alpha) {
|
||||||
|
return fmt::format("#{:02X}{:02X}{:02X}{:02X}", alpha, color.red, color.green, color.blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string css_rgba(const VitaThemeColor &color, const std::uint8_t alpha) {
|
||||||
|
return fmt::format("rgba({}, {}, {}, {:.3f})",
|
||||||
|
color.red,
|
||||||
|
color.green,
|
||||||
|
color.blue,
|
||||||
|
static_cast<double>(alpha) / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t readability_alpha(const int readability_percent, const std::uint8_t minimum, const std::uint8_t maximum) {
|
||||||
|
const double amount = std::clamp(static_cast<double>(readability_percent) / 100.0, 0.0, 1.0);
|
||||||
|
return static_cast<std::uint8_t>(std::lround((minimum * (1.0 - amount)) + (maximum * amount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string stylesheet_path_url(const fs::path &path, const fs::path &pref_path) {
|
||||||
|
fs::path normalized_path = path.lexically_normal();
|
||||||
|
if (!pref_path.empty()) {
|
||||||
|
const fs::path relative = normalized_path.lexically_relative(pref_path);
|
||||||
|
if (!relative.empty() && relative != normalized_path)
|
||||||
|
normalized_path = relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalized = fs_utils::path_to_utf8(normalized_path);
|
||||||
|
std::replace(normalized.begin(), normalized.end(), '\\', '/');
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string join_stylesheet_path_urls(const std::vector<fs::path> &paths, const fs::path &pref_path) {
|
||||||
|
std::string joined;
|
||||||
|
bool first = true;
|
||||||
|
for (const fs::path &path : paths) {
|
||||||
|
if (path.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!first)
|
||||||
|
joined += ", ";
|
||||||
|
joined += stylesheet_path_url(path, pref_path);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return joined;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ThemeTemplateContext {
|
||||||
|
std::unordered_map<std::string, std::string> strings;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<std::string> load_vita_theme_stylesheet_template() {
|
||||||
|
static const std::optional<std::string> stylesheet_template = []() -> std::optional<std::string> {
|
||||||
|
QFile file(QStringLiteral(":/themes/vita/generated.qss.in"));
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
LOG_ERROR("Failed to load Vita theme stylesheet template from resources");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(file.readAll().constData(), static_cast<std::size_t>(file.size()));
|
||||||
|
}();
|
||||||
|
|
||||||
|
return stylesheet_template;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> render_template_token(
|
||||||
|
const ThemeTemplateContext &context,
|
||||||
|
const std::string &token) {
|
||||||
|
const auto it = context.strings.find(token);
|
||||||
|
return (it != context.strings.end()) ? std::optional<std::string>(it->second) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> render_stylesheet_template(
|
||||||
|
const ThemeTemplateContext &context) {
|
||||||
|
auto stylesheet_template = load_vita_theme_stylesheet_template();
|
||||||
|
if (!stylesheet_template)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string rendered;
|
||||||
|
rendered.reserve(stylesheet_template->size());
|
||||||
|
|
||||||
|
std::size_t cursor = 0;
|
||||||
|
while (cursor < stylesheet_template->size()) {
|
||||||
|
const std::size_t start = stylesheet_template->find("{{", cursor);
|
||||||
|
if (start == std::string::npos) {
|
||||||
|
rendered.append(*stylesheet_template, cursor, std::string::npos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered.append(*stylesheet_template, cursor, start - cursor);
|
||||||
|
const std::size_t finish = stylesheet_template->find("}}", start + 2);
|
||||||
|
if (finish == std::string::npos) {
|
||||||
|
rendered.append(*stylesheet_template, start, std::string::npos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string token = stylesheet_template->substr(start + 2, finish - (start + 2));
|
||||||
|
const auto value = render_template_token(context, token);
|
||||||
|
if (!value) {
|
||||||
|
LOG_ERROR("Unknown Vita theme stylesheet template token '{}'", token);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered.append(*value);
|
||||||
|
cursor = finish + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<fs::path> existing_asset_path(const fs::path &theme_dir, const std::string &raw_path) {
|
||||||
|
const fs::path resolved = resolve_asset_path(theme_dir, raw_path);
|
||||||
|
return (!resolved.empty() && fs::exists(resolved)) ? std::optional<fs::path>(resolved) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<pugi::xml_node> collect_background_nodes(const pugi::xml_node &home_node) {
|
||||||
|
std::vector<pugi::xml_node> nodes;
|
||||||
|
const auto bg_parent = home_node.child("m_bgParam");
|
||||||
|
if (!bg_parent)
|
||||||
|
return nodes;
|
||||||
|
|
||||||
|
for (const auto &child : bg_parent.children()) {
|
||||||
|
if (child.type() != pugi::node_element)
|
||||||
|
continue;
|
||||||
|
if ((std::string_view(child.name()) == "BackgroundParam") || child.child("m_imageFilePath"))
|
||||||
|
nodes.push_back(child);
|
||||||
|
else if (const auto nested = child.child("BackgroundParam"))
|
||||||
|
nodes.push_back(nested);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect_directory_metrics(const fs::path &root, std::uintmax_t &total_size, std::time_t &latest_write_time) {
|
||||||
|
if (!fs::exists(root))
|
||||||
|
return;
|
||||||
|
|
||||||
|
boost::system::error_code error_code;
|
||||||
|
if (const auto root_time = fs::last_write_time(root, error_code); !error_code)
|
||||||
|
latest_write_time = std::max(latest_write_time, root_time);
|
||||||
|
|
||||||
|
fs::recursive_directory_iterator end;
|
||||||
|
for (fs::recursive_directory_iterator it(root, error_code); !error_code && it != end; it.increment(error_code)) {
|
||||||
|
const fs::path current = it->path();
|
||||||
|
|
||||||
|
if (fs::is_regular_file(current, error_code)) {
|
||||||
|
total_size += fs::file_size(current, error_code);
|
||||||
|
error_code.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto write_time = fs::last_write_time(current, error_code); !error_code)
|
||||||
|
latest_write_time = std::max(latest_write_time, write_time);
|
||||||
|
error_code.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VitaThemeBackgroundOption *find_background_option(
|
||||||
|
const VitaThemeInfo &theme,
|
||||||
|
const std::string &background_option_id) {
|
||||||
|
if (theme.background_options.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (background_option_id.empty())
|
||||||
|
return &theme.background_options.front();
|
||||||
|
|
||||||
|
const auto it = std::find_if(theme.background_options.begin(), theme.background_options.end(),
|
||||||
|
[&background_option_id](const VitaThemeBackgroundOption &option) {
|
||||||
|
return option.id == background_option_id;
|
||||||
|
});
|
||||||
|
return (it != theme.background_options.end()) ? &(*it) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_stylesheet(
|
||||||
|
const VitaThemeInfo &theme,
|
||||||
|
const VitaThemeBackgroundOption &background_option,
|
||||||
|
const int readability_percent,
|
||||||
|
const bool normalize_font_colors,
|
||||||
|
const std::vector<fs::path> &background_image_paths,
|
||||||
|
const int cycle_interval_seconds,
|
||||||
|
const bool cycle_enabled,
|
||||||
|
const fs::path &pref_path) {
|
||||||
|
const VitaThemeColor window = theme.palette.information_bar_color.value_or(VitaThemeColor{ 18, 26, 34, 255 });
|
||||||
|
const bool dark_theme = luminance(window) < 0.5;
|
||||||
|
const VitaThemeColor base = darken(window, 0.18);
|
||||||
|
const VitaThemeColor alternate_base = lighten(base, 0.08);
|
||||||
|
const VitaThemeColor accent_hint = dark_theme
|
||||||
|
? VitaThemeColor{ 140, 212, 255, 255 }
|
||||||
|
: VitaThemeColor{ 20, 138, 255, 255 };
|
||||||
|
VitaThemeColor accent = theme.palette.information_indicator_color.value_or(accent_hint);
|
||||||
|
const VitaThemeColor normalized_text = contrasting_text(window);
|
||||||
|
const VitaThemeColor neutral_border = mix(normalized_text, window, dark_theme ? 0.34 : 0.56);
|
||||||
|
VitaThemeColor border = theme.palette.notify_border_color
|
||||||
|
? mix(*theme.palette.notify_border_color, neutral_border, dark_theme ? 0.42 : 0.58)
|
||||||
|
: neutral_border;
|
||||||
|
const VitaThemeColor theme_text = normalize_font_colors
|
||||||
|
? normalized_text
|
||||||
|
: ensure_readable(
|
||||||
|
background_option.primary_text_color.value_or(
|
||||||
|
theme.palette.notify_font_color.value_or(normalized_text)),
|
||||||
|
window);
|
||||||
|
const VitaThemeColor dialog_text = dark_theme ? VitaThemeColor{ 224, 224, 224, 255 } : VitaThemeColor{ 32, 32, 32, 255 };
|
||||||
|
const VitaThemeColor dialog_disabled_text = dark_theme ? VitaThemeColor{ 119, 119, 119, 255 } : VitaThemeColor{ 138, 138, 138, 255 };
|
||||||
|
const VitaThemeColor dialog_muted_text = dark_theme ? VitaThemeColor{ 168, 168, 168, 255 } : VitaThemeColor{ 91, 91, 91, 255 };
|
||||||
|
const VitaThemeColor overlay_text = (normalize_font_colors && !dark_theme)
|
||||||
|
? VitaThemeColor{ 245, 247, 250, 255 }
|
||||||
|
: theme_text;
|
||||||
|
const VitaThemeColor text = overlay_text;
|
||||||
|
VitaThemeColor muted_text = ensure_readable(
|
||||||
|
normalize_font_colors
|
||||||
|
? mix(dialog_text, window, dark_theme ? 0.32 : 0.48)
|
||||||
|
: theme.palette.date_color.value_or(mix(theme_text, window, dark_theme ? 0.42 : 0.52)),
|
||||||
|
window);
|
||||||
|
const VitaThemeColor overlay_muted_text = normalize_font_colors
|
||||||
|
? (dark_theme ? VitaThemeColor{ 168, 168, 168, 255 } : VitaThemeColor{ 208, 220, 230, 255 })
|
||||||
|
: muted_text;
|
||||||
|
if ((std::abs(luminance(accent) - luminance(window)) < 0.16)
|
||||||
|
&& (std::abs(luminance(accent) - luminance(theme_text)) < 0.10)) {
|
||||||
|
accent = accent_hint;
|
||||||
|
}
|
||||||
|
accent = ensure_readable(accent, window);
|
||||||
|
|
||||||
|
const VitaThemeColor button = theme.palette.notify_background_color.value_or(mix(window, accent, 0.10));
|
||||||
|
const VitaThemeColor button_text = normalize_font_colors
|
||||||
|
? ensure_readable(dialog_text, button)
|
||||||
|
: ensure_readable(
|
||||||
|
theme.palette.notify_font_color.value_or(text),
|
||||||
|
button);
|
||||||
|
const VitaThemeColor highlight_text = contrasting_text(accent);
|
||||||
|
const VitaThemeColor checkbox_fill = mix(base, window, dark_theme ? 0.22 : 0.14);
|
||||||
|
const VitaThemeColor disabled_text = mix(muted_text, window, 0.32);
|
||||||
|
const VitaThemeColor overlay_disabled_text = normalize_font_colors
|
||||||
|
? (dark_theme ? VitaThemeColor{ 119, 119, 119, 255 } : VitaThemeColor{ 166, 180, 194, 255 })
|
||||||
|
: disabled_text;
|
||||||
|
const VitaThemeColor line_edit_fill = mix(base, window, 0.35);
|
||||||
|
const VitaThemeColor subtle_surface = mix(base, window, 0.40);
|
||||||
|
const VitaThemeColor overlay_selection_fill = dark_theme
|
||||||
|
? VitaThemeColor{ 255, 255, 255, 255 }
|
||||||
|
: VitaThemeColor{ 10, 18, 24, 255 };
|
||||||
|
const VitaThemeColor overlay_selection_text = VitaThemeColor{ 255, 255, 255, 255 };
|
||||||
|
const VitaThemeColor hover_surface = overlay_selection_fill;
|
||||||
|
const VitaThemeColor selected_text = overlay_selection_text;
|
||||||
|
const VitaThemeColor toolbar_icon_tint = normalize_font_colors
|
||||||
|
? overlay_text
|
||||||
|
: (dark_theme ? text : mix(text, window, 0.48));
|
||||||
|
const VitaThemeColor dialog_button_fill = dark_theme ? VitaThemeColor{ 48, 48, 48, 255 } : VitaThemeColor{ 236, 236, 236, 255 };
|
||||||
|
const VitaThemeColor dialog_bind_fill = dark_theme ? VitaThemeColor{ 47, 47, 47, 255 } : VitaThemeColor{ 230, 230, 230, 255 };
|
||||||
|
const VitaThemeColor dialog_sidebar_fill = dark_theme ? VitaThemeColor{ 42, 42, 42, 255 } : VitaThemeColor{ 255, 255, 255, 255 };
|
||||||
|
const VitaThemeColor dialog_help_fill = dark_theme ? VitaThemeColor{ 42, 42, 42, 255 } : VitaThemeColor{ 255, 255, 255, 255 };
|
||||||
|
const VitaThemeColor dialog_hover_fill = dark_theme ? VitaThemeColor{ 58, 58, 58, 255 } : VitaThemeColor{ 242, 242, 242, 255 };
|
||||||
|
const VitaThemeColor dialog_selected_fill = dark_theme ? VitaThemeColor{ 255, 255, 255, 255 } : VitaThemeColor{ 20, 138, 255, 255 };
|
||||||
|
const VitaThemeColor dialog_selected_text = VitaThemeColor{ 255, 255, 255, 255 };
|
||||||
|
const VitaThemeColor dialog_mode_checked_text = dark_theme ? dialog_text : dialog_selected_text;
|
||||||
|
const VitaThemeColor renderer_vulkan = VitaThemeColor{ 255, 68, 68, 255 };
|
||||||
|
const VitaThemeColor renderer_opengl = VitaThemeColor{ 68, 136, 255, 255 };
|
||||||
|
const VitaThemeColor status_high = VitaThemeColor{ 255, 136, 0, 255 };
|
||||||
|
const VitaThemeColor status_standard = text;
|
||||||
|
const VitaThemeColor status_neutral = text;
|
||||||
|
const VitaThemeColor status_audio = dark_theme ? VitaThemeColor{ 136, 221, 68, 255 } : VitaThemeColor{ 75, 155, 47, 255 };
|
||||||
|
const VitaThemeColor status_muted = text;
|
||||||
|
const VitaThemeColor status_update = dark_theme ? VitaThemeColor{ 109, 224, 137, 255 } : VitaThemeColor{ 31, 143, 78, 255 };
|
||||||
|
const VitaThemeColor log_fatal = ensure_readable(VitaThemeColor{ 255, 159, 243, 255 }, window);
|
||||||
|
const VitaThemeColor log_error = ensure_readable(VitaThemeColor{ 255, 154, 127, 255 }, window);
|
||||||
|
const VitaThemeColor log_warning = ensure_readable(VitaThemeColor{ 255, 211, 127, 255 }, window);
|
||||||
|
const VitaThemeColor log_debug = ensure_readable(VitaThemeColor{ 140, 212, 255, 255 }, window);
|
||||||
|
|
||||||
|
if (std::abs(luminance(muted_text) - luminance(text)) < 0.08)
|
||||||
|
muted_text = ensure_readable(mix(text, window, dark_theme ? 0.32 : 0.48), window);
|
||||||
|
|
||||||
|
if ((std::abs(luminance(border) - luminance(window)) < 0.11)
|
||||||
|
|| (color_distance(border, window) < 0.22)) {
|
||||||
|
border = neutral_border;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t dialog_alpha = readability_alpha(readability_percent, 188, 252);
|
||||||
|
const std::uint8_t chrome_alpha = readability_alpha(readability_percent, 58, 196);
|
||||||
|
const std::uint8_t chrome_border_alpha = readability_alpha(readability_percent, 18, 64);
|
||||||
|
const std::uint8_t control_alpha = readability_alpha(readability_percent, 26, 176);
|
||||||
|
const std::uint8_t input_alpha = readability_alpha(readability_percent, 32, 188);
|
||||||
|
const std::uint8_t sidebar_alpha = readability_alpha(readability_percent, 28, 210);
|
||||||
|
const std::uint8_t surface_alpha = readability_alpha(readability_percent, 10, 156);
|
||||||
|
const std::uint8_t pane_alpha = readability_alpha(readability_percent, 12, 190);
|
||||||
|
const std::uint8_t search_alpha = readability_alpha(readability_percent, 20, 210);
|
||||||
|
const std::uint8_t progress_alpha = readability_alpha(readability_percent, 28, 140);
|
||||||
|
const std::uint8_t scroll_alpha = readability_alpha(readability_percent, 18, 110);
|
||||||
|
const std::uint8_t hover_alpha = readability_alpha(readability_percent, 44, 96);
|
||||||
|
const std::uint8_t overlay_selected_alpha = dark_theme
|
||||||
|
? readability_alpha(readability_percent, 28, 82)
|
||||||
|
: readability_alpha(readability_percent, 42, 132);
|
||||||
|
const std::uint8_t overlay_selected_soft_alpha = dark_theme
|
||||||
|
? readability_alpha(readability_percent, 18, 54)
|
||||||
|
: readability_alpha(readability_percent, 28, 94);
|
||||||
|
const std::uint8_t overlay_hover_alpha = dark_theme
|
||||||
|
? readability_alpha(readability_percent, 16, 42)
|
||||||
|
: readability_alpha(readability_percent, 12, 34);
|
||||||
|
const std::uint8_t dialog_selected_alpha = dark_theme
|
||||||
|
? readability_alpha(readability_percent, 28, 78)
|
||||||
|
: readability_alpha(readability_percent, 96, 220);
|
||||||
|
|
||||||
|
const std::string icon_suffix = dark_theme ? "light" : "dark";
|
||||||
|
const std::string check_icon_suffix = luminance(highlight_text) > 0.5 ? "light" : "dark";
|
||||||
|
const std::string background_url = stylesheet_path_url(background_option.asset_path, pref_path);
|
||||||
|
ThemeTemplateContext context;
|
||||||
|
const auto put = [&context](const std::string &key, std::string value) {
|
||||||
|
context.strings[key] = std::move(value);
|
||||||
|
};
|
||||||
|
const auto put_hex = [&put](const std::string &key, const VitaThemeColor &color) {
|
||||||
|
put(key, css_hex(color));
|
||||||
|
};
|
||||||
|
const auto put_rgba = [&put](const std::string &key, const VitaThemeColor &color, const int alpha) {
|
||||||
|
put(key, css_rgba(color, static_cast<std::uint8_t>(std::clamp(alpha, 0, 255))));
|
||||||
|
};
|
||||||
|
const auto put_argb = [&put](const std::string &key, const VitaThemeColor &color, const int alpha) {
|
||||||
|
put(key, css_argb(color, static_cast<std::uint8_t>(std::clamp(alpha, 0, 255))));
|
||||||
|
};
|
||||||
|
|
||||||
|
put("generated_background_images", join_stylesheet_path_urls(background_image_paths, pref_path));
|
||||||
|
put("generated_background_interval_seconds", std::to_string(std::clamp(cycle_interval_seconds, 5, 120)));
|
||||||
|
put("generated_background_animated", cycle_enabled ? "true" : "false");
|
||||||
|
|
||||||
|
put("palette_dark", dark_theme ? "dark" : "light");
|
||||||
|
put_hex("window_hex", window);
|
||||||
|
put_hex("text_hex", text);
|
||||||
|
put_hex("base_hex", base);
|
||||||
|
put_hex("alternate_base_hex", alternate_base);
|
||||||
|
put_hex("button_hex", button);
|
||||||
|
put_hex("button_text_hex", button_text);
|
||||||
|
put_hex("button_bright_text_hex", contrasting_text(button));
|
||||||
|
put_hex("accent_hex", accent);
|
||||||
|
put_argb("highlight_argb", overlay_selection_fill, overlay_selected_alpha);
|
||||||
|
put_argb("inactive_highlight_argb", overlay_selection_fill, overlay_selected_soft_alpha);
|
||||||
|
put_argb("disabled_highlight_argb", overlay_selection_fill, overlay_hover_alpha);
|
||||||
|
put_hex("highlight_text_hex", overlay_selection_text);
|
||||||
|
put_hex("disabled_text_hex", disabled_text);
|
||||||
|
put_hex("muted_text_hex", muted_text);
|
||||||
|
put_hex("overlay_disabled_text_hex", overlay_disabled_text);
|
||||||
|
put_hex("dialog_text_hex", dialog_text);
|
||||||
|
put_hex("dialog_muted_text_hex", dialog_muted_text);
|
||||||
|
put_hex("dialog_disabled_text_hex", dialog_disabled_text);
|
||||||
|
put_hex("selected_text_hex", selected_text);
|
||||||
|
put_hex("push_button_pressed_text_hex", highlight_text);
|
||||||
|
put_hex("dialog_selected_text_hex", dialog_selected_text);
|
||||||
|
put_hex("dialog_mode_checked_text_hex", dialog_mode_checked_text);
|
||||||
|
put_hex("toolbar_icon_tint_hex", toolbar_icon_tint);
|
||||||
|
put_hex("renderer_vulkan_hex", renderer_vulkan);
|
||||||
|
put_hex("renderer_opengl_hex", renderer_opengl);
|
||||||
|
put_hex("status_high_hex", status_high);
|
||||||
|
put_hex("status_standard_hex", status_standard);
|
||||||
|
put_hex("status_neutral_hex", status_neutral);
|
||||||
|
put_hex("status_audio_hex", status_audio);
|
||||||
|
put_hex("status_muted_hex", status_muted);
|
||||||
|
put_hex("status_update_hex", status_update);
|
||||||
|
put_hex("log_fatal_hex", log_fatal);
|
||||||
|
put_hex("log_error_hex", log_error);
|
||||||
|
put_hex("log_warning_hex", log_warning);
|
||||||
|
put_hex("log_debug_hex", log_debug);
|
||||||
|
put_hex("severity_success_text_hex", contrasting_text(status_audio));
|
||||||
|
put_hex("severity_error_text_hex", contrasting_text(log_error));
|
||||||
|
|
||||||
|
put("background_url", background_url);
|
||||||
|
put("icon_suffix", icon_suffix);
|
||||||
|
put("check_icon_suffix", check_icon_suffix);
|
||||||
|
|
||||||
|
put_rgba("dialog_bg", base, dialog_alpha);
|
||||||
|
put_rgba("menu_bar_bg", base, chrome_alpha);
|
||||||
|
put_rgba("menu_bar_border", border, chrome_border_alpha);
|
||||||
|
put_rgba("menu_bar_item_hover_bg", hover_surface, hover_alpha);
|
||||||
|
put_rgba("menu_bg", base, 245);
|
||||||
|
put_rgba("menu_border", border, 40);
|
||||||
|
put_rgba("menu_selected_bg", overlay_selection_fill, overlay_selected_soft_alpha);
|
||||||
|
put_rgba("menu_separator_bg", border, 30);
|
||||||
|
put_rgba("toolbar_bg", base, chrome_alpha);
|
||||||
|
put_rgba("toolbar_border_top", border, chrome_border_alpha + 8);
|
||||||
|
put_rgba("toolbar_border_bottom", border, chrome_border_alpha);
|
||||||
|
put_rgba("toolbar_separator_bg", border, 40);
|
||||||
|
put_rgba("tool_button_hover_bg", hover_surface, hover_alpha);
|
||||||
|
put_rgba("tool_button_hover_border", border, chrome_border_alpha + 6);
|
||||||
|
put_rgba("tool_button_pressed_bg", accent, 54);
|
||||||
|
put_rgba("welcome_button_bg", button, 88);
|
||||||
|
put_rgba("welcome_button_border", border, 36);
|
||||||
|
put_rgba("welcome_button_hover_bg", hover_surface, 88);
|
||||||
|
put_rgba("welcome_button_hover_border", border, 48);
|
||||||
|
put_rgba("welcome_button_pressed_bg", accent, 56);
|
||||||
|
put_rgba("welcome_menu_button_border", border, 36);
|
||||||
|
put_rgba("welcome_menu_button_bg", base, 28);
|
||||||
|
put_rgba("welcome_menu_button_hover_bg", hover_surface, 74);
|
||||||
|
put_rgba("welcome_menu_button_hover_border", border, 48);
|
||||||
|
put_rgba("welcome_menu_button_pressed_bg", accent, 48);
|
||||||
|
put_rgba("push_button_bg", button, control_alpha);
|
||||||
|
put_rgba("push_button_border", border, 36);
|
||||||
|
put_rgba("push_button_hover_bg", hover_surface, hover_alpha + 12);
|
||||||
|
put_rgba("push_button_hover_border", border, chrome_border_alpha + 18);
|
||||||
|
put_rgba("push_button_pressed_bg", accent, 62);
|
||||||
|
put_rgba("push_button_disabled_bg", base, 72);
|
||||||
|
put_rgba("push_button_disabled_border", border, 20);
|
||||||
|
put_rgba("push_button_default_border", accent, 110);
|
||||||
|
put_rgba("vita_list_bg", dialog_sidebar_fill, sidebar_alpha);
|
||||||
|
put_rgba("vita_list_selected_bg", dialog_selected_fill, dialog_selected_alpha);
|
||||||
|
put_rgba("vita_list_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha + 8 : hover_alpha);
|
||||||
|
put_rgba("background_list_bg", line_edit_fill, pane_alpha);
|
||||||
|
put_rgba("background_list_border", border, 26);
|
||||||
|
put_rgba("background_list_selected_bg", dialog_selected_fill, dialog_selected_alpha);
|
||||||
|
put_rgba("background_list_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha + 6 : hover_alpha + 4);
|
||||||
|
put_rgba("vita_help_bg", dialog_help_fill, pane_alpha);
|
||||||
|
put_rgba("controls_mode_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha : hover_alpha + 10);
|
||||||
|
put_rgba("controls_mode_checked_bg", dialog_selected_fill, dialog_selected_alpha);
|
||||||
|
put_rgba("controls_mode_pressed_bg", dialog_selected_fill, dark_theme ? dialog_selected_alpha + 16 : std::min(255, dialog_selected_alpha + 24));
|
||||||
|
put_rgba("controls_button_bg", dialog_button_fill, control_alpha);
|
||||||
|
put_rgba("controls_button_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha + 10 : hover_alpha + 6);
|
||||||
|
put_rgba("controls_button_pressed_bg", dialog_hover_fill, dark_theme ? hover_alpha + 20 : hover_alpha + 18);
|
||||||
|
put_rgba("controls_button_disabled_bg", dialog_button_fill, std::max(40, control_alpha - 28));
|
||||||
|
put_rgba("controls_bind_bg", dialog_bind_fill, input_alpha);
|
||||||
|
put_rgba("controls_bind_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha + 6 : hover_alpha + 4);
|
||||||
|
put_rgba("controls_bind_pressed_bg", dialog_hover_fill, dark_theme ? hover_alpha + 16 : hover_alpha + 14);
|
||||||
|
put_rgba("controls_capture_bg", dialog_selected_fill, dark_theme ? dialog_selected_alpha + 18 : dialog_selected_alpha);
|
||||||
|
put_rgba("controls_capture_hover_bg", dialog_selected_fill, dark_theme ? dialog_selected_alpha + 28 : std::min(255, dialog_selected_alpha + 18));
|
||||||
|
put_rgba("controls_capture_pressed_bg", dialog_selected_fill, dark_theme ? dialog_selected_alpha + 38 : std::min(255, dialog_selected_alpha + 28));
|
||||||
|
put_rgba("status_bar_bg", base, chrome_alpha + 18);
|
||||||
|
put_rgba("status_bar_border", border, 26);
|
||||||
|
put_rgba("status_button_hover_border", border, 32);
|
||||||
|
put_rgba("status_button_hover_bg", hover_surface, overlay_hover_alpha);
|
||||||
|
put_rgba("status_button_pressed_border", border, 40);
|
||||||
|
put_rgba("status_button_pressed_bg", overlay_selection_fill, overlay_selected_soft_alpha);
|
||||||
|
put_rgba("line_edit_bg", line_edit_fill, input_alpha);
|
||||||
|
put_rgba("line_edit_border", border, 36);
|
||||||
|
put_rgba("line_edit_selection_bg", overlay_selection_fill, overlay_selected_alpha);
|
||||||
|
put_rgba("line_edit_focus_border", accent, 96);
|
||||||
|
put_rgba("line_edit_disabled_bg", base, 76);
|
||||||
|
put_rgba("line_edit_disabled_border", border, 20);
|
||||||
|
put_rgba("main_search_border", border, 36);
|
||||||
|
put_rgba("main_search_selection_bg", overlay_selection_fill, overlay_selected_soft_alpha);
|
||||||
|
put_rgba("main_search_focus_bg", base, 18);
|
||||||
|
put_rgba("main_search_focus_border", accent, 72);
|
||||||
|
put_rgba("combo_bg", line_edit_fill, input_alpha);
|
||||||
|
put_rgba("combo_border", border, 36);
|
||||||
|
put_rgba("combo_hover_border", border, 54);
|
||||||
|
put_rgba("combo_disabled_bg", base, 76);
|
||||||
|
put_rgba("combo_disabled_border", border, 20);
|
||||||
|
put_rgba("combo_popup_bg", base, 244);
|
||||||
|
put_rgba("combo_popup_border", border, 40);
|
||||||
|
put_rgba("combo_popup_selection_bg", overlay_selection_fill, overlay_selected_soft_alpha);
|
||||||
|
put_rgba("spin_bg", line_edit_fill, input_alpha);
|
||||||
|
put_rgba("spin_border", border, 36);
|
||||||
|
put_rgba("spin_selection_bg", overlay_selection_fill, overlay_selected_alpha);
|
||||||
|
put_rgba("spin_disabled_bg", base, 76);
|
||||||
|
put_rgba("spin_disabled_border", border, 20);
|
||||||
|
put_rgba("indicator_border", border, 92);
|
||||||
|
put_rgba("indicator_bg", checkbox_fill, std::max(62, static_cast<int>(input_alpha) - 12));
|
||||||
|
put_rgba("indicator_hover_border", accent, 138);
|
||||||
|
put_rgba("indicator_hover_bg", checkbox_fill, std::max(82, static_cast<int>(input_alpha) + 6));
|
||||||
|
put_rgba("indicator_checked_bg", accent, 196);
|
||||||
|
put_rgba("indicator_checked_border", accent, 204);
|
||||||
|
put_rgba("indicator_disabled_border", border, 54);
|
||||||
|
put_rgba("indicator_disabled_bg", base, 88);
|
||||||
|
put_rgba("indicator_checked_disabled_bg", accent, 96);
|
||||||
|
put_rgba("indicator_checked_disabled_border", accent, 108);
|
||||||
|
put_rgba("radio_indicator_border", border, 92);
|
||||||
|
put_rgba("radio_indicator_bg", checkbox_fill, std::max(62, static_cast<int>(input_alpha) - 12));
|
||||||
|
put_rgba("radio_indicator_hover_border", accent, 138);
|
||||||
|
put_rgba("radio_indicator_hover_bg", checkbox_fill, std::max(82, static_cast<int>(input_alpha) + 6));
|
||||||
|
put_rgba("radio_checked_bg", accent, 196);
|
||||||
|
put_rgba("radio_checked_border", accent, 204);
|
||||||
|
put_rgba("radio_checked_disabled_bg", accent, 96);
|
||||||
|
put_rgba("radio_checked_disabled_border", accent, 108);
|
||||||
|
put_rgba("group_border", border, 32);
|
||||||
|
put_rgba("group_disabled_border", border, 18);
|
||||||
|
put_rgba("dock_title_bg", base, chrome_alpha);
|
||||||
|
put_rgba("dock_button_hover_bg", hover_surface, hover_alpha + 8);
|
||||||
|
put_rgba("apps_surface_bg", base, surface_alpha);
|
||||||
|
put_rgba("alt_row_bg", text, std::max<std::uint8_t>(8, surface_alpha / 2));
|
||||||
|
put_rgba("table_gridline", border, 18);
|
||||||
|
put_rgba("table_disabled_bg", base, 54);
|
||||||
|
put_rgba("table_corner_bg", darken(base, 0.08), 80);
|
||||||
|
put_rgba("table_corner_border", border, 24);
|
||||||
|
put_rgba("table_selected_bg", overlay_selection_fill, overlay_selected_alpha);
|
||||||
|
put_rgba("header_bg", darken(base, 0.08), surface_alpha);
|
||||||
|
put_rgba("header_border", border, 18);
|
||||||
|
put_rgba("header_hover_bg", hover_surface, 84);
|
||||||
|
put_rgba("log_search_bg", subtle_surface, search_alpha);
|
||||||
|
put_rgba("log_search_border", border, 30);
|
||||||
|
put_rgba("toolbar_slider_groove_bg", text, 24);
|
||||||
|
put_rgba("toolbar_slider_subpage_bg", text, 228);
|
||||||
|
put_rgba("toolbar_slider_addpage_bg", text, 20);
|
||||||
|
put_hex("toolbar_slider_handle_bg", lighten(text, dark_theme ? 0.0 : 0.12));
|
||||||
|
put_hex("toolbar_slider_handle_hover_bg", lighten(text, 0.12));
|
||||||
|
put_rgba("severity_success_bg", status_audio, 124);
|
||||||
|
put_rgba("severity_error_bg", log_error, 124);
|
||||||
|
put_rgba("tab_pane_border", border, 24);
|
||||||
|
put_rgba("tab_pane_bg", base, pane_alpha);
|
||||||
|
put_rgba("tab_bg", subtle_surface, sidebar_alpha);
|
||||||
|
put_rgba("tab_border", border, 24);
|
||||||
|
put_rgba("tab_selected_bg", base, pane_alpha);
|
||||||
|
put_rgba("tab_hover_bg", hover_surface, hover_alpha);
|
||||||
|
put_rgba("list_bg", base, pane_alpha);
|
||||||
|
put_rgba("list_border", border, 22);
|
||||||
|
put_rgba("list_disabled_bg", base, 54);
|
||||||
|
put_rgba("list_disabled_border", border, 16);
|
||||||
|
put_rgba("list_selected_bg", overlay_selection_fill, overlay_selected_alpha);
|
||||||
|
put_rgba("list_hover_bg", hover_surface, hover_alpha);
|
||||||
|
put_rgba("settings_category_bg", dialog_sidebar_fill, sidebar_alpha);
|
||||||
|
put_rgba("settings_category_selected_bg", dialog_selected_fill, dialog_selected_alpha);
|
||||||
|
put_rgba("settings_category_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha + 8 : hover_alpha);
|
||||||
|
put_rgba("modules_list_bg", line_edit_fill, pane_alpha);
|
||||||
|
put_rgba("modules_list_border", border, 30);
|
||||||
|
put_rgba("modules_list_hover_bg", dialog_hover_fill, dark_theme ? hover_alpha + 4 : hover_alpha + 8);
|
||||||
|
put_rgba("settings_help_bg", dialog_help_fill, pane_alpha);
|
||||||
|
put_rgba("scrollbar_bg", base, scroll_alpha);
|
||||||
|
put_rgba("scrollbar_handle_bg", text, scroll_alpha + 22);
|
||||||
|
put_rgba("scrollbar_handle_hover_bg", text, scroll_alpha + 40);
|
||||||
|
put_rgba("slider_subpage_bg", text, 224);
|
||||||
|
put_rgba("slider_addpage_bg", text, 28);
|
||||||
|
put_hex("slider_handle_bg", lighten(text, dark_theme ? 0.0 : 0.12));
|
||||||
|
put_hex("slider_handle_hover_bg", lighten(text, 0.12));
|
||||||
|
put_rgba("progress_bg", base, progress_alpha);
|
||||||
|
put_rgba("progress_border", border, 22);
|
||||||
|
put_hex("progress_chunk_bg", accent);
|
||||||
|
put_rgba("splitter_handle_bg", border, 34);
|
||||||
|
put_rgba("tooltip_bg", base, 245);
|
||||||
|
put_rgba("tooltip_border", border, 40);
|
||||||
|
put_rgba("text_edit_bg", base, pane_alpha);
|
||||||
|
put_rgba("text_edit_border", border, 22);
|
||||||
|
put_rgba("text_edit_disabled_bg", base, 54);
|
||||||
|
put_rgba("text_edit_disabled_border", border, 16);
|
||||||
|
|
||||||
|
const auto rendered = render_stylesheet_template(context);
|
||||||
|
return rendered.value_or(std::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::optional<VitaThemeInfo> load_vita_theme(
|
||||||
|
const fs::path &theme_dir,
|
||||||
|
const std::string &preferred_locale) {
|
||||||
|
const fs::path theme_xml_path = theme_dir / "theme.xml";
|
||||||
|
if (!fs::exists(theme_xml_path))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::vector<char> xml_buffer;
|
||||||
|
if (!fs_utils::read_data(theme_xml_path, xml_buffer)) {
|
||||||
|
LOG_WARN("Failed to read Vita theme metadata from {}", theme_xml_path);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pugi::xml_document document;
|
||||||
|
const auto parse_result = document.load_buffer(xml_buffer.data(), xml_buffer.size());
|
||||||
|
if (!parse_result) {
|
||||||
|
LOG_WARN("Failed to parse Vita theme metadata from {}: {}", theme_xml_path, parse_result.description());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto theme_root = document.child("theme");
|
||||||
|
if (!theme_root) {
|
||||||
|
LOG_WARN("Theme metadata at {} is missing the root <theme> node", theme_xml_path);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeInfo theme;
|
||||||
|
theme.installed_path = theme_dir;
|
||||||
|
const auto info_node = theme_root.child("InfomationProperty");
|
||||||
|
const auto info_bar_node = theme_root.child("InfomationBarProperty");
|
||||||
|
const auto home_node = theme_root.child("HomeProperty");
|
||||||
|
const auto start_node = theme_root.child("StartScreenProperty");
|
||||||
|
|
||||||
|
theme.content_id = child_text(info_node, "m_contentId");
|
||||||
|
theme.title = localized_text(info_node.child("m_title"), preferred_locale);
|
||||||
|
theme.provider = localized_text(info_node.child("m_provider"), preferred_locale);
|
||||||
|
theme.content_version = child_text(info_node, "m_contentVer");
|
||||||
|
theme.theme_id = vita_theme_utils::resolve_theme_id(theme.content_id, path_leaf_string(theme_dir), theme.title);
|
||||||
|
|
||||||
|
if (theme.title.empty())
|
||||||
|
theme.title = theme.theme_id;
|
||||||
|
|
||||||
|
const std::string package_thumbnail_raw = [&]() {
|
||||||
|
if (const std::string package_image = child_text(info_node, "m_packageImageFilePath"); !package_image.empty())
|
||||||
|
return package_image;
|
||||||
|
return child_text(info_node, "m_previewFilePath");
|
||||||
|
}();
|
||||||
|
if (const auto package_thumbnail = existing_asset_path(theme_dir, package_thumbnail_raw))
|
||||||
|
theme.package_thumbnail_path = *package_thumbnail;
|
||||||
|
|
||||||
|
const std::string bgm_raw_path = child_text(home_node, "m_bgmFilePath");
|
||||||
|
if (const auto bgm_path = existing_asset_path(theme_dir, bgm_raw_path)) {
|
||||||
|
theme.bgm_path = *bgm_path;
|
||||||
|
} else if (!bgm_raw_path.empty()) {
|
||||||
|
LOG_WARN("Skipping missing Vita theme BGM '{}' for theme '{}'", bgm_raw_path, theme.theme_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
theme.palette.information_bar_color = parse_theme_color(child_text(info_bar_node, "m_barColor"));
|
||||||
|
theme.palette.information_indicator_color = parse_theme_color(child_text(info_bar_node, "m_indicatorColor"));
|
||||||
|
theme.palette.notify_background_color = parse_theme_color(child_text(start_node, "m_notifyBgColor"));
|
||||||
|
theme.palette.notify_border_color = parse_theme_color(child_text(start_node, "m_notifyBorderColor"));
|
||||||
|
theme.palette.notify_font_color = parse_theme_color(child_text(start_node, "m_notifyFontColor"));
|
||||||
|
theme.palette.date_color = parse_theme_color(child_text(start_node, "m_dateColor"));
|
||||||
|
|
||||||
|
int home_index = 0;
|
||||||
|
for (const auto &background_node : collect_background_nodes(home_node)) {
|
||||||
|
const std::string raw_path = child_text(background_node, "m_imageFilePath");
|
||||||
|
if (raw_path.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto asset_path = existing_asset_path(theme_dir, raw_path);
|
||||||
|
if (!asset_path) {
|
||||||
|
LOG_WARN("Skipping missing Vita home background '{}' for theme '{}'", raw_path, theme.theme_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VitaThemeBackgroundOption option;
|
||||||
|
option.id = fmt::format("home-{}", home_index);
|
||||||
|
option.label = fmt::format("Home Background {}", home_index + 1);
|
||||||
|
option.asset_path = *asset_path;
|
||||||
|
option.source = VitaThemeBackgroundSource::Home;
|
||||||
|
option.primary_text_color = parse_theme_color(child_text(background_node, "m_fontColor"));
|
||||||
|
theme.background_options.push_back(std::move(option));
|
||||||
|
++home_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string raw_lockscreen_path = child_text(start_node, "m_filePath");
|
||||||
|
if (const auto lockscreen_path = existing_asset_path(theme_dir, raw_lockscreen_path)) {
|
||||||
|
VitaThemeBackgroundOption option;
|
||||||
|
option.id = "lockscreen";
|
||||||
|
option.label = "Lockscreen";
|
||||||
|
option.asset_path = *lockscreen_path;
|
||||||
|
option.source = VitaThemeBackgroundSource::Lockscreen;
|
||||||
|
option.primary_text_color = theme.palette.notify_font_color;
|
||||||
|
theme.background_options.push_back(std::move(option));
|
||||||
|
} else if (!raw_lockscreen_path.empty()) {
|
||||||
|
LOG_WARN("Skipping missing Vita lockscreen background '{}' for theme '{}'", raw_lockscreen_path, theme.theme_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_directory_metrics(theme_dir, theme.installed_size, theme.updated_time);
|
||||||
|
|
||||||
|
if (theme.background_options.empty()) {
|
||||||
|
LOG_WARN("Theme '{}' has no usable background assets, skipping", theme.theme_id);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VitaThemeInfo> enumerate_installed_vita_themes(
|
||||||
|
const fs::path &themes_root,
|
||||||
|
const std::string &preferred_locale) {
|
||||||
|
std::vector<VitaThemeInfo> themes;
|
||||||
|
if (!fs::exists(themes_root) || !fs::is_directory(themes_root))
|
||||||
|
return themes;
|
||||||
|
|
||||||
|
std::vector<fs::path> theme_paths;
|
||||||
|
for (fs::directory_iterator it(themes_root), end; it != end; ++it) {
|
||||||
|
if (fs::is_directory(it->path()))
|
||||||
|
theme_paths.push_back(it->path());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(theme_paths.begin(), theme_paths.end(), [](const fs::path &lhs, const fs::path &rhs) {
|
||||||
|
return fs_utils::path_to_utf8(lhs.filename()) < fs_utils::path_to_utf8(rhs.filename());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto &theme_path : theme_paths) {
|
||||||
|
if (auto theme = load_vita_theme(theme_path, preferred_locale))
|
||||||
|
themes.push_back(std::move(*theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
return themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<fs::path> synthesize_vita_theme_qss(
|
||||||
|
const VitaThemeInfo &theme,
|
||||||
|
const std::string &background_option_id,
|
||||||
|
const int readability_percent,
|
||||||
|
const bool normalize_font_colors,
|
||||||
|
const std::vector<fs::path> &background_image_paths,
|
||||||
|
const int cycle_interval_seconds,
|
||||||
|
const bool cycle_enabled,
|
||||||
|
const fs::path &pref_path,
|
||||||
|
const fs::path &gui_settings_dir) {
|
||||||
|
const auto *selected_background = find_background_option(theme, background_option_id);
|
||||||
|
if (!selected_background) {
|
||||||
|
LOG_ERROR("Unable to find Vita theme background option '{}' for theme '{}'",
|
||||||
|
background_option_id,
|
||||||
|
theme.theme_id);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs::exists(selected_background->asset_path)) {
|
||||||
|
LOG_ERROR("Vita theme asset is missing: {}", selected_background->asset_path);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs::path output_directory = gui_settings_dir / "vita-themes" / theme.theme_id;
|
||||||
|
const fs::path qss_path = output_directory / "generated.qss";
|
||||||
|
const std::string stylesheet = build_stylesheet(
|
||||||
|
theme,
|
||||||
|
*selected_background,
|
||||||
|
std::clamp(readability_percent, 0, 100),
|
||||||
|
normalize_font_colors,
|
||||||
|
background_image_paths.empty() ? std::vector<fs::path>{ selected_background->asset_path } : background_image_paths,
|
||||||
|
cycle_interval_seconds,
|
||||||
|
cycle_enabled,
|
||||||
|
pref_path);
|
||||||
|
|
||||||
|
boost::system::error_code error_code;
|
||||||
|
fs::create_directories(output_directory, error_code);
|
||||||
|
if (error_code) {
|
||||||
|
LOG_ERROR("Failed to create Vita theme output directory {}: {}",
|
||||||
|
output_directory,
|
||||||
|
error_code.message());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::ofstream output(qss_path, fs::ofstream::binary | fs::ofstream::trunc);
|
||||||
|
if (!output) {
|
||||||
|
LOG_ERROR("Failed to open generated Vita theme stylesheet path {}", qss_path);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write(stylesheet.data(), static_cast<std::streamsize>(stylesheet.size()));
|
||||||
|
if (!output.good()) {
|
||||||
|
LOG_ERROR("Failed to write generated Vita theme stylesheet {}", qss_path);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qss_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M3.2 8.4 6.3 11.5 12.8 5" fill="none" stroke="#09131A" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 202 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M3.2 8.4 6.3 11.5 12.8 5" fill="none" stroke="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 202 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<circle cx="8" cy="8" r="3" fill="#09131A"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 115 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<circle cx="8" cy="8" r="3" fill="#FFFFFF"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 115 B |
+40
-12
@@ -44,6 +44,7 @@
|
|||||||
#include <util/log.h>
|
#include <util/log.h>
|
||||||
#include <util/string_utils.h>
|
#include <util/string_utils.h>
|
||||||
#include <util/vector_utils.h>
|
#include <util/vector_utils.h>
|
||||||
|
#include <util/vita_theme_utils.h>
|
||||||
|
|
||||||
#include <gdbstub/functions.h>
|
#include <gdbstub/functions.h>
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
@@ -76,6 +77,18 @@ static const char *miniz_get_error(const ZipPtr &zip) {
|
|||||||
return mz_zip_get_error_string(mz_zip_get_last_error(zip.get()));
|
return mz_zip_get_error_string(mz_zip_get_last_error(zip.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string fallback_theme_root_name(const std::string &content_path) {
|
||||||
|
std::string trimmed = content_path;
|
||||||
|
while (!trimmed.empty() && ((trimmed.back() == '/') || (trimmed.back() == '\\')))
|
||||||
|
trimmed.pop_back();
|
||||||
|
|
||||||
|
if (trimmed.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto separator = trimmed.find_last_of("/\\");
|
||||||
|
return (separator == std::string::npos) ? trimmed : trimmed.substr(separator + 1);
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_nonpdrm(EmuEnvState &emuenv, const fs::path &output_path) {
|
static bool is_nonpdrm(EmuEnvState &emuenv, const fs::path &output_path) {
|
||||||
const auto app_license_path{ emuenv.pref_path / "ux0/license" / emuenv.app_info.app_title_id / fmt::format("{}.rif", emuenv.app_info.app_content_id) };
|
const auto app_license_path{ emuenv.pref_path / "ux0/license" / emuenv.app_info.app_title_id / fmt::format("{}.rif", emuenv.app_info.app_content_id) };
|
||||||
const auto is_patch_found_app_license = (emuenv.app_info.app_category == "gp") && fs::exists(app_license_path);
|
const auto is_patch_found_app_license = (emuenv.app_info.app_category == "gp") && fs::exists(app_license_path);
|
||||||
@@ -120,18 +133,32 @@ static bool set_content_path(EmuEnvState &emuenv, const bool is_theme, fs::path
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_theme_name(EmuEnvState &emuenv, const vfs::FileBuffer &buffer) {
|
static void set_theme_name(EmuEnvState &emuenv, const vfs::FileBuffer &buffer, const std::string &fallback_id_hint = {}) {
|
||||||
|
std::string content_id;
|
||||||
|
std::string title;
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
if (doc.load_buffer(buffer.data(), buffer.size())) {
|
if (doc.load_buffer(buffer.data(), buffer.size())) {
|
||||||
const auto info = doc.child("theme").child("InfomationProperty");
|
const auto info = doc.child("theme").child("InfomationProperty");
|
||||||
const char *content_id = info.child("m_contentId").text().as_string();
|
content_id = info.child("m_contentId").text().as_string();
|
||||||
const char *title = info.child("m_title").child("m_default").text().as_string();
|
title = info.child("m_title").child("m_default").text().as_string();
|
||||||
if (content_id[0] != '\0') {
|
} else {
|
||||||
emuenv.app_info.app_content_id = content_id;
|
LOG_WARN("Unable to parse theme.xml metadata during install, falling back to folder/title-derived theme identity");
|
||||||
emuenv.app_info.app_title_id = content_id;
|
}
|
||||||
}
|
|
||||||
if (title[0] != '\0')
|
const std::string resolved_id = vita_theme_utils::resolve_theme_id(
|
||||||
emuenv.app_info.app_title = title;
|
content_id,
|
||||||
|
fallback_id_hint,
|
||||||
|
title,
|
||||||
|
buffer.empty() ? nullptr : buffer.data(),
|
||||||
|
buffer.size());
|
||||||
|
emuenv.app_info.app_content_id = resolved_id;
|
||||||
|
emuenv.app_info.app_title_id = resolved_id;
|
||||||
|
|
||||||
|
if (!title.empty()) {
|
||||||
|
emuenv.app_info.app_title = title;
|
||||||
|
} else if (emuenv.app_info.app_title.empty()) {
|
||||||
|
emuenv.app_info.app_title = resolved_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +168,7 @@ static bool install_archive_content(EmuEnvState &emuenv, const ZipPtr &zip, cons
|
|||||||
vfs::FileBuffer buffer, theme;
|
vfs::FileBuffer buffer, theme;
|
||||||
|
|
||||||
const auto is_theme = mz_zip_reader_extract_file_to_callback(zip.get(), (content_path + theme_path).c_str(), &write_to_buffer, &theme, 0);
|
const auto is_theme = mz_zip_reader_extract_file_to_callback(zip.get(), (content_path + theme_path).c_str(), &write_to_buffer, &theme, 0);
|
||||||
|
const std::string theme_root_name = fallback_theme_root_name(content_path);
|
||||||
|
|
||||||
auto output_path{ emuenv.pref_path / "ux0" };
|
auto output_path{ emuenv.pref_path / "ux0" };
|
||||||
if (mz_zip_reader_extract_file_to_callback(zip.get(), (content_path + sfo_path).c_str(), &write_to_buffer, &buffer, 0)) {
|
if (mz_zip_reader_extract_file_to_callback(zip.get(), (content_path + sfo_path).c_str(), &write_to_buffer, &buffer, 0)) {
|
||||||
@@ -148,7 +176,7 @@ static bool install_archive_content(EmuEnvState &emuenv, const ZipPtr &zip, cons
|
|||||||
if (!set_content_path(emuenv, is_theme, output_path))
|
if (!set_content_path(emuenv, is_theme, output_path))
|
||||||
return false;
|
return false;
|
||||||
} else if (is_theme) {
|
} else if (is_theme) {
|
||||||
set_theme_name(emuenv, theme);
|
set_theme_name(emuenv, theme, theme_root_name);
|
||||||
output_path /= fs::path("theme") / emuenv.app_info.app_content_id;
|
output_path /= fs::path("theme") / emuenv.app_info.app_content_id;
|
||||||
} else {
|
} else {
|
||||||
LOG_CRITICAL("miniz error: {} extracting file: {}", miniz_get_error(zip), sfo_path);
|
LOG_CRITICAL("miniz error: {} extracting file: {}", miniz_get_error(zip), sfo_path);
|
||||||
@@ -321,7 +349,7 @@ static bool install_content(EmuEnvState &emuenv, const fs::path &content_path) {
|
|||||||
fs::remove_all(dst_path);
|
fs::remove_all(dst_path);
|
||||||
|
|
||||||
} else if (fs_utils::read_data(theme_path, buffer)) {
|
} else if (fs_utils::read_data(theme_path, buffer)) {
|
||||||
set_theme_name(emuenv, buffer);
|
set_theme_name(emuenv, buffer, fs_utils::path_to_utf8(content_path.filename()));
|
||||||
dst_path /= fs::path("theme") / fs_utils::utf8_to_path(emuenv.app_info.app_title_id);
|
dst_path /= fs::path("theme") / fs_utils::utf8_to_path(emuenv.app_info.app_title_id);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Param.sfo file is missing in path", sfo_path);
|
LOG_ERROR("Param.sfo file is missing in path", sfo_path);
|
||||||
@@ -434,7 +462,7 @@ static ExitCode load_app_impl(SceUID &main_module_id, EmuEnvState &emuenv, const
|
|||||||
} else
|
} else
|
||||||
return FileNotFound;
|
return FileNotFound;
|
||||||
// Set self name from self path, can contain folder, get file name only
|
// Set self name from self path, can contain folder, get file name only
|
||||||
emuenv.self_name = fs::path(emuenv.self_path).filename().string();
|
emuenv.self_name = fs_utils::path_to_utf8(fs::path(emuenv.self_path).filename());
|
||||||
|
|
||||||
// get list of preload modules
|
// get list of preload modules
|
||||||
SceUInt32 process_preload_disabled = 0;
|
SceUInt32 process_preload_disabled = 0;
|
||||||
|
|||||||
@@ -7,10 +7,21 @@
|
|||||||
<file>icons/silver.png</file>
|
<file>icons/silver.png</file>
|
||||||
<file>icons/gold.png</file>
|
<file>icons/gold.png</file>
|
||||||
<file>icons/platinum.png</file>
|
<file>icons/platinum.png</file>
|
||||||
|
<file alias="themes/light/light.qss">gui-qt/resources/themes/light/light.qss</file>
|
||||||
|
<file alias="themes/light/chevron_down_dark.svg">icons/chevron_down_dark.svg</file>
|
||||||
|
<file alias="themes/light/chevron_up_dark.svg">icons/chevron_up_dark.svg</file>
|
||||||
|
<file alias="themes/dark/dark.qss">gui-qt/resources/themes/dark/dark.qss</file>
|
||||||
|
<file alias="themes/dark/chevron_down_light.svg">icons/chevron_down_light.svg</file>
|
||||||
|
<file alias="themes/dark/chevron_up_light.svg">icons/chevron_up_light.svg</file>
|
||||||
|
<file alias="themes/vita/generated.qss.in">gui-qt/resources/themes/vita/generated.qss.in</file>
|
||||||
<file>icons/configure.png</file>
|
<file>icons/configure.png</file>
|
||||||
<file>icons/controllers.png</file>
|
<file>icons/controllers.png</file>
|
||||||
<file>icons/chevron_down_dark.svg</file>
|
<file>icons/chevron_down_dark.svg</file>
|
||||||
<file>icons/chevron_down_light.svg</file>
|
<file>icons/chevron_down_light.svg</file>
|
||||||
|
<file>icons/check_dark.svg</file>
|
||||||
|
<file>icons/check_light.svg</file>
|
||||||
|
<file>icons/radio_dot_dark.svg</file>
|
||||||
|
<file>icons/radio_dot_light.svg</file>
|
||||||
<file>icons/cross_dark.svg</file>
|
<file>icons/cross_dark.svg</file>
|
||||||
<file>icons/cross_light.svg</file>
|
<file>icons/cross_light.svg</file>
|
||||||
<file>icons/chevron_up_dark.svg</file>
|
<file>icons/chevron_up_dark.svg</file>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ add_library(
|
|||||||
src/net_utils.cpp
|
src/net_utils.cpp
|
||||||
src/string_utils.cpp
|
src/string_utils.cpp
|
||||||
src/tracy.cpp
|
src/tracy.cpp
|
||||||
|
src/vita_theme_utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# vc_runtime_checker.cpp is directly added from the main CMakeList (for some reason adding it here doesn't work)
|
# vc_runtime_checker.cpp is directly added from the main CMakeList (for some reason adding it here doesn't work)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ std::wstring utf_to_wide(const std::string &str);
|
|||||||
std::string wide_to_utf(const std::wstring &str);
|
std::string wide_to_utf(const std::wstring &str);
|
||||||
std::string utf16_to_utf8(const std::u16string &str);
|
std::string utf16_to_utf8(const std::u16string &str);
|
||||||
std::u16string utf8_to_utf16(const std::string &str);
|
std::u16string utf8_to_utf16(const std::string &str);
|
||||||
|
std::string trim_copy(const std::string &str);
|
||||||
std::string remove_special_chars(std::string str);
|
std::string remove_special_chars(std::string str);
|
||||||
void replace(std::string &str, const std::string &in, const std::string &out);
|
void replace(std::string &str, const std::string &in, const std::string &out);
|
||||||
std::vector<uint8_t> string_to_byte_array(const std::string &string);
|
std::vector<uint8_t> string_to_byte_array(const std::string &string);
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace vita_theme_utils {
|
||||||
|
|
||||||
|
std::string sanitize_theme_id(std::string value);
|
||||||
|
std::string resolve_theme_id(
|
||||||
|
const std::string &content_id,
|
||||||
|
const std::string &folder_name,
|
||||||
|
const std::string &title,
|
||||||
|
const std::uint8_t *fallback_hash_data = nullptr,
|
||||||
|
std::size_t fallback_hash_size = 0);
|
||||||
|
|
||||||
|
} // namespace vita_theme_utils
|
||||||
@@ -53,6 +53,20 @@ std::string wide_to_utf(const std::wstring &str) {
|
|||||||
return myconv.to_bytes(str);
|
return myconv.to_bytes(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string trim_copy(const std::string &str) {
|
||||||
|
const auto begin = std::find_if_not(str.begin(), str.end(), [](const unsigned char ch) {
|
||||||
|
return std::isspace(ch) != 0;
|
||||||
|
});
|
||||||
|
const auto end = std::find_if_not(str.rbegin(), str.rend(), [](const unsigned char ch) {
|
||||||
|
return std::isspace(ch) != 0;
|
||||||
|
}).base();
|
||||||
|
|
||||||
|
if (begin >= end)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return std::string(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
std::string remove_special_chars(std::string str) {
|
std::string remove_special_chars(std::string str) {
|
||||||
for (char &c : str) {
|
for (char &c : str) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Vita3K emulator project
|
||||||
|
// Copyright (C) 2026 Vita3K team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#include <util/vita_theme_utils.h>
|
||||||
|
|
||||||
|
#include <util/string_utils.h>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace vita_theme_utils {
|
||||||
|
|
||||||
|
std::string sanitize_theme_id(std::string value) {
|
||||||
|
value = string_utils::trim_copy(string_utils::remove_special_chars(std::move(value)));
|
||||||
|
|
||||||
|
std::string sanitized;
|
||||||
|
sanitized.reserve(value.size());
|
||||||
|
|
||||||
|
bool last_was_separator = false;
|
||||||
|
for (const unsigned char raw_char : value) {
|
||||||
|
const char ch = static_cast<char>(raw_char);
|
||||||
|
if (std::isalnum(raw_char) || (ch == '-') || (ch == '_')) {
|
||||||
|
sanitized.push_back(ch);
|
||||||
|
last_was_separator = false;
|
||||||
|
} else if (!last_was_separator) {
|
||||||
|
sanitized.push_back('_');
|
||||||
|
last_was_separator = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!sanitized.empty() && ((sanitized.front() == '_') || (sanitized.front() == '-')))
|
||||||
|
sanitized.erase(sanitized.begin());
|
||||||
|
while (!sanitized.empty() && ((sanitized.back() == '_') || (sanitized.back() == '-')))
|
||||||
|
sanitized.pop_back();
|
||||||
|
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string resolve_theme_id(
|
||||||
|
const std::string &content_id,
|
||||||
|
const std::string &folder_name,
|
||||||
|
const std::string &title,
|
||||||
|
const std::uint8_t *fallback_hash_data,
|
||||||
|
const std::size_t fallback_hash_size) {
|
||||||
|
if (const std::string trimmed_content_id = string_utils::trim_copy(content_id); !trimmed_content_id.empty())
|
||||||
|
return trimmed_content_id;
|
||||||
|
|
||||||
|
if (const std::string sanitized_folder = sanitize_theme_id(folder_name); !sanitized_folder.empty())
|
||||||
|
return sanitized_folder;
|
||||||
|
|
||||||
|
if (const std::string sanitized_title = sanitize_theme_id(title); !sanitized_title.empty())
|
||||||
|
return sanitized_title;
|
||||||
|
|
||||||
|
if (fallback_hash_data && fallback_hash_size > 0) {
|
||||||
|
uint32_t hash = 2166136261u;
|
||||||
|
for (std::size_t index = 0; index < fallback_hash_size; ++index) {
|
||||||
|
hash ^= fallback_hash_data[index];
|
||||||
|
hash *= 16777619u;
|
||||||
|
}
|
||||||
|
return fmt::format("theme-{:08X}", hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "theme";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vita_theme_utils
|
||||||
Reference in New Issue
Block a user