diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03c87574..deb19b20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,18 +19,21 @@ jobs: - os: ubuntu-20.04 qt_host: linux - qt_version: '6.6.0' + qt_version: '6.8.0' qt_modules: 'qtmultimedia qtcharts qtwaylandcompositor' + qt_arch: 'linux_gcc_64' - os: windows-2022 qt_host: windows - qt_version: '6.6.0' + qt_version: '6.8.0' qt_modules: 'qtmultimedia qtcharts' + qt_arch: 'win64_msvc2022_64' - - os: macOS-12 + - os: macos-latest qt_host: mac - qt_version: '6.6.0' + qt_version: '6.7.2' qt_modules: 'qtmultimedia qtcharts' + qt_arch: 'clang_64' runs-on: ${{ matrix.os }} @@ -69,16 +72,18 @@ jobs: - uses: ilammy/msvc-dev-cmd@v1 if: runner.os == 'Windows' - - uses: repolevedavaj/install-nsis@v1.0.1 + - uses: repolevedavaj/install-nsis@v1.0.2 if: runner.os == 'Windows' with: - nsis-version: 3.08 + nsis-version: 3.09 - name: Install Qt uses: jurplel/install-qt-action@v3.3.0 with: - version: 6.6.0 + version: ${{ matrix.qt_version }} modules: ${{ matrix.qt_modules }} + arch: ${{ matrix.qt_arch }} + aqtversion: ==3.1.* cache: true - name: Set up Python 3.8 (macOS) @@ -121,14 +126,6 @@ jobs: run: | cmake --build ${{ env.BUILD_DIR }} --parallel $(nproc) --config ${{ inputs.build_type }} - - name: Test - if: inputs.build_type == 'Debug' - working-directory: ${{ env.BUILD_DIR }}/tests - shell: bash - run: | - export QT_QPA_PLATFORM=minimal - ctest --build-config ${{ inputs.build_type }} --verbose - - name: Package (Linux) if: runner.os == 'Linux' run: | @@ -147,12 +144,13 @@ jobs: cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr export OUTPUT="QFRCDashboard-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" + export QML_SOURCES_PATHS="${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/qml:${{ github.workspace }}" chmod +x linuxdeploy-*.AppImage - mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/ - cp -r /home/runner/work/QFRCDashboard/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines + cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ @@ -162,16 +160,18 @@ jobs: - name: Package (macOS) if: runner.os == 'macOS' run: | - export PATH="$PATH;${{ runner.workspace }}/Qt/6.6.0/*/bin" + export PATH="$PATH;${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/*/bin" + export QML_DIR_PATH="${{ github.workspace }}" + export QML_SOURCES_PATHS="${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/macos/qml" cd ${{ env.BUILD_DIR }} - macdeployqt QFRCDashboard.app + macdeployqt Dashboard.app -qmldir=$QML_DIR_PATH -qmlimport=$QML_SOURCES_PATHS ls - sudo codesign --sign - --deep --force --entitlements ../program_info/App.entitlements --options runtime QFRCDashboard.app/Contents/MacOS/QFRCDashboard - chmod a+x QFRCDashboard.app/Contents/MacOS/QFRCDashboard - cp ../program_info/QFRCDashboard.icns QFRCDashboard.app/Contents/Resources - tar czf ../QFRCDashboard.tar.gz QFRCDashboard.app + sudo codesign --sign - --deep --force --entitlements ../program_info/App.entitlements --options runtime Dashboard.app/Contents/MacOS/Dashboard + chmod a+x Dashboard.app/Contents/MacOS/Dashboard + cp ../program_info/QFRCDashboard.icns Dashboard.app/Contents/Resources + tar czf ../QFRCDashboard.tar.gz Dashboard.app - name: Package (Windows, Portable) if: runner.os == 'Windows' @@ -181,10 +181,11 @@ jobs: cmake --install ${{ env.BUILD_DIR }}\_deps\protobuf-build --prefix ${{ env.PROTOBUF_INSTALL_DIR }} mkdir ${{ env.INSTALL_DIR }} - set PATH=%PATH%;"${{ runner.workspace }}\Qt\6.6.0\msvc2019_64\bin" + set PATH=%PATH%;"${{ runner.workspace }}\Qt\${{ matrix.qt_version }}\${{ matrix.qt_arch }}\bin" - windeployqt ${{ env.BUILD_DIR }}\QFRCDashboard.exe --dir ${{ env.INSTALL_DIR }} - cp ${{ env.BUILD_DIR }}\QFRCDashboard.exe ${{ env.INSTALL_DIR }} + windeployqt ${{ env.BUILD_DIR }}\Dashboard.exe --dir ${{ env.INSTALL_DIR }} --qmldir ${{ github.workspace }} --qmlimport %QML2_IMPORT_PATH% + cp ${{ env.BUILD_DIR }}\Dashboard.exe ${{ env.INSTALL_DIR }} + cp -r ${{ runner.workspace }}\Qt\${{ matrix.qt_version }}\msvc2022_64\qml ${{ env.INSTALL_DIR }} if "${{ inputs.build_type }}" == "Debug" ( cp C:\Windows\System32\ucrtbased.dll ${{ env.INSTALL_DIR }} @@ -242,4 +243,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: QFRCDashboard-${{ runner.os }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }} - path: QFRCDashboard-Setup.exe + path: Dashboard-Setup.exe diff --git a/.gitignore b/.gitignore index 1417257c..742794b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,289 +1,78 @@ -# Created by https://www.gitignore.io/api/macos,windows,linux,java,gradle,intellij,eclipse -# Edit at https://www.gitignore.io/?templates=macos,windows,linux,java,gradle,intellij,eclipse +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -### Linux ### *~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store .directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Intellij+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### Intellij+all Patch ### -# Ignores the whole .idea folder and all .iml files -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 - -*.iml -modules.xml -.idea/misc.xml -*.ipr - -### Java ### -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -### Eclipse ### - -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.settings/ -.loadpath -.recommenders - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# PyDev specific (Python IDE for Eclipse) -*.pydevproject - -# CDT-specific (C/C++ Development Tooling) -.cproject - -# CDT- autotools -.autotools - -# Java annotation processor (APT) -.factorypath - -# PDT-specific (PHP Development Tools) -.buildpath - -# sbteclipse plugin -.target - -# Tern plugin -.tern-project - -# TeXlipse plugin -.texlipse - -# STS (Spring Tool Suite) -.springBeans - -# Code Recommenders -.recommenders/ - -# Annotation Processing -.apt_generated/ - -# Scala IDE specific (Scala & Java development for Eclipse) -.cache-main -.scala_dependencies -.worksheet - -### Eclipse Patch ### -# Eclipse Core -.project - -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# Annotation Processing -.apt_generated - -.sts4-cache/ - -### Java ### -*.class - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - - -### Gradle ### -.gradle -build/ - -# Ignore Gradle GUI config -gradle-app.setting - -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar - -# Cache of project -.gradletasknamecache - -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties - -# VS Code settings file -.vscode/ +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe CMakeLists.txt.user appdir *.AppImage -build* +build diff --git a/lib/3rd_party/CMakeLists.txt b/3rd_party/CMakeLists.txt similarity index 100% rename from lib/3rd_party/CMakeLists.txt rename to 3rd_party/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 36726b43..17665a52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,30 +1,19 @@ -cmake_minimum_required(VERSION 3.25) +cmake_minimum_required(VERSION 3.16) -project(QFRCDashboard VERSION 1.0.0 LANGUAGES CXX) +set(Dashboard_APP_NAME QFRCDashboard) +set(Dashboard_EXEC_NAME Dashboard) +set(Dashboard_ORG_NAME "Q-FRC") +set(Dashboard_GIT_REPO https://github.com/${Dashboard_ORG_NAME}/${Dashboard_EXEC_NAME}) -set(CMAKE_AUTOUIC_SEARCH_PATHS "lib/ui/") +project(QFRCDashboard VERSION 0.1 LANGUAGES CXX) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTORCC ON) -option(WITH_TESTS "Build tests" ON) - -set(QTCOMPONENTS Widgets MultimediaWidgets Charts) - -if(WITH_TESTS) - set(QTCOMPONENTS ${QTCOMPONENTS} Test) -endif() - -find_package(QT NAMES Qt6 REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${QTCOMPONENTS}) +find_package(Qt6 6.7 REQUIRED COMPONENTS Quick Multimedia) +add_subdirectory(3rd_party EXCLUDE_FROM_ALL) -set(Dashboard_ORG_NAME "binex-dsk") -set(Dashboard_APP_NAME "QFRCDashboard") -set(Dashboard_GIT_REPO "https://github.com/binex-dsk/QFRCDashboard") +qt_standard_project_setup(REQUIRES 6.7) if(WIN32) set(RCS_FILE ${CMAKE_CURRENT_BINARY_DIR}/program_info/${Dashboard_APP_NAME}.rc) @@ -61,69 +50,129 @@ string(TIMESTAMP TODAY "%Y-%m-%d") set(Dashboard_BUILD_TIMESTAMP "${TODAY}") add_subdirectory("program_info") -add_subdirectory(lib) find_package(ntcore) find_package(Protobuf) -set(QAPPLICATION_CLASS QApplication) -add_subdirectory("SingleApplication") - -if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) - qt_add_executable(QFRCDashboard - MANUAL_FINALIZATION - main.cpp - ${RCS_FILE} - lib/fields.qrc - lib/theme/qrc/breeze.qrc - lib/icons.qrc - ) -endif() +set(QRC_FILES fields.qrc) -target_link_libraries(QFRCDashboard PRIVATE QFRCDashboardLib SingleApplication::SingleApplication Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::MultimediaWidgets Qt${QT_VERSION_MAJOR}::Charts ntcore BuildConfig) - -target_include_directories(QFRCDashboard PRIVATE "$") -target_include_directories(QFRCDashboard PRIVATE lib/ui/) -target_include_directories(QFRCDashboard PRIVATE lib/include) -target_include_directories(QFRCDashboard PRIVATE ${_Dashboard_AUTOGEN}/include) -target_include_directories(QFRCDashboard PRIVATE lib/) - -# courtesy of Cristian Adam -# enable CMake support for all targets (wpilib, protobuf, lib, tests...) -function(ccache_handle_debug_msvc) - if (MSVC) - foreach(config DEBUG RELWITHDEBINFO) - foreach(lang C CXX) - set(flags_var "CMAKE_${lang}_FLAGS_${config}") - string(REPLACE "/Zi" "/Z7" ${flags_var} "${${flags_var}}") - set(${flags_var} "${${flags_var}}" PARENT_SCOPE) - endforeach() - endforeach() - endif() -endfunction() - -ccache_handle_debug_msvc() - -if(CMAKE_BUILD_TYPE STREQUAL "Release") - set_property(TARGET QFRCDashboard PROPERTY WIN32_EXECUTABLE true) -endif() +qt_add_executable(${Dashboard_EXEC_NAME} + main.cpp + ${QRC_FILES} +) + +set_source_files_properties(Constants.qml + PROPERTIES + QT_QML_SINGLETON_TYPE true +) + +set(QML_ELEMENTS + Main + Constants + + items/MainScreen + items/Tab + items/GridHandler + + widgets/BaseWidget + widgets/ResizeAnchor + widgets/DoubleSpinBox + + widgets/primitive/TextWidget + widgets/primitive/IntWidget + widgets/primitive/IntDialWidget + widgets/primitive/DoubleWidget + widgets/primitive/DoubleDialWidget + widgets/primitive/BoolWidget + widgets/primitive/EnumWidget + widgets/primitive/ColorWidget + + widgets/primitive/ShapeHandler + + widgets/sendable/FMSInfo + widgets/sendable/Field2d + widgets/sendable/Command + widgets/sendable/StringChooser + + widgets/misc/CameraView + + dialogs/TopicView + dialogs/TabNameDialog + dialogs/TabSizeDialog + dialogs/WidgetConfig + dialogs/ServerDialog +) + +set(ELEMENTS + Globals + Constants + SettingsManager + TitleManager + + models/TabWidgetsModel + models/TabListModel + models/TopicListModel + models/CameraListModel + models/MapModel + + stores/TopicStore +) + +foreach(ELEMENT ${ELEMENTS}) + set(HEADERS ${HEADERS} include/${ELEMENT}.h) + set(SOURCES ${SOURCES} src/${ELEMENT}.cpp) +endforeach() + +foreach(QML ${QML_ELEMENTS}) + set(QML_FILES ${QML_FILES} ${QML}.qml) +endforeach() + +set(HEADERS + ${HEADERS} + include/Flags.h + include/MetaObjectHelper.h +) + +add_subdirectory(buildconfig) + +qt_add_qml_module(${Dashboard_EXEC_NAME} + URI QFRCDashboard + VERSION 1.0 + QML_FILES ${QML_FILES} + + SOURCES ${SOURCES} ${HEADERS} +) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +set_target_properties(${Dashboard_EXEC_NAME} PROPERTIES + # MACOSX_BUNDLE_GUI_IDENTIFIER com.example.${Dashboard_EXEC_NAME} + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +target_link_libraries(${Dashboard_EXEC_NAME} + PRIVATE Qt6::Quick + Qt6::Multimedia + BuildConfig + ntcore +) + +target_include_directories(${Dashboard_EXEC_NAME} PRIVATE include/) +target_include_directories(${Dashboard_EXEC_NAME} PRIVATE include/models) +target_include_directories(${Dashboard_EXEC_NAME} PRIVATE include/stores) +target_include_directories(${Dashboard_EXEC_NAME} PRIVATE "$") include(GNUInstallDirs) -install(TARGETS QFRCDashboard +install(TARGETS ${Dashboard_EXEC_NAME} BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) -install( - CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/qt.conf\" \" \")" - COMPONENT Runtime -) - -if(WITH_TESTS) - add_subdirectory(tests) -endif() - -set_target_properties(QFRCDashboard PROPERTIES +set_target_properties(${Dashboard_EXEC_NAME} PROPERTIES MACOSX_BUNDLE TRUE ) @@ -141,7 +190,3 @@ if(UNIX AND APPLE) # install as bundle set(INSTALL_BUNDLE "full") endif() - -if(QT_VERSION_MAJOR EQUAL 6) - qt_finalize_executable(QFRCDashboard) -endif() diff --git a/Constants.qml b/Constants.qml new file mode 100644 index 00000000..6efea0d4 --- /dev/null +++ b/Constants.qml @@ -0,0 +1,92 @@ +pragma Singleton +import QtQuick + +QtObject { + readonly property int width: 900; + readonly property int height: 600; + + property var palette: dark + + property color accent: "#ec202a" + property color tab: "#ec5a5c" + + function setTheme(newTheme) { + settings.theme = newTheme + switch (newTheme) { + case "light": + palette = light + break + case "dark": + palette = dark + break + case "midnight": + palette = midnight + break + default: + break + } + } + + function setAccent(newAccent) { + settings.accent = newAccent + switch (newAccent) { + case "red": + accent = "#ec202a" + tab = "#ec5a5c" + break + case "blue": + accent = "#0024c2" + tab = "#4520ff" + break + case "purple": + accent = "#a100a1" + tab = "#b400e1" + break + default: + break; + } + } + + property QtObject midnight: QtObject { + id: midnight + + readonly property color bg: "#000000"; + readonly property color widgetBg: "#1B1B1B"; + readonly property color dialogBg: "#272727"; + + readonly property color text: "#EEEEEE" + + readonly property color menu: "#111111" + readonly property color menuBorder: "#AAAAAA" + readonly property real menuBaseOpacity: 0.3 + readonly property color menuItem: bg + } + + property QtObject dark: QtObject { + id: dark + + readonly property color bg: "#4d4d4d"; + readonly property color widgetBg: "#5d5d5d"; + readonly property color dialogBg: "#717171"; + + readonly property color text: "#FFFFFF" + + readonly property color menu: "#595959" + readonly property color menuBorder: "#AAAAAA" + readonly property real menuBaseOpacity: 0.5 + readonly property color menuItem: bg + } + + property QtObject light: QtObject { + readonly property color bg: "#FFFFFF"; + readonly property color widgetBg: "#EDEDED"; + readonly property color dialogBg: "#DCDCDC"; + + readonly property color text: "#000000" + + readonly property color menu: "#DDDDDD" + readonly property color menuBorder: "#222222" + readonly property real menuBaseOpacity: 0.8 + readonly property color menuItem: bg + } +} diff --git a/Main.qml b/Main.qml new file mode 100644 index 00000000..d6dec4fa --- /dev/null +++ b/Main.qml @@ -0,0 +1,139 @@ +import QtQuick +import QtQuick.Controls + +import QFRCDashboard + +ApplicationWindow { + id: window + width: Constants.width + height: Constants.height + visible: true + title: titleManager.title + + menuBar: MenuBar { + contentWidth: parent.width + + Menu { + title: qsTr("&Settings") + Action { + text: qsTr("&Server Settings...") + onTriggered: screen.serverSettings() + shortcut: "Ctrl+E" + } + MenuItem { + text: "&Load Most Recent File?" + checkable: true + checked: settings.loadRecent + onCheckedChanged: settings.loadRecent = checked + } + + Menu { + title: qsTr("&Theme") + Repeater { + model: ["Light", "Dark", "Midnight"] + + MenuItem { + text: "&" + modelData + checkable: true + checked: settings.theme === modelData.toLowerCase() + onCheckedChanged: { + if (checked) + Constants.setTheme(modelData.toLowerCase()) + } + } + } + } + + Menu { + title: qsTr("&Accent") + + Repeater { + model: ["Red", "Blue", "Purple"] + + MenuItem { + text: "&" + modelData + checkable: true + checked: settings.accent === modelData.toLowerCase() + onCheckedChanged: { + if (checked) + Constants.setAccent(modelData.toLowerCase()) + } + } + } + } + } + + Menu { + title: qsTr("&File") + Action { + text: qsTr("&Save") + onTriggered: screen.save() + shortcut: "Ctrl+S" + } + Action { + text: qsTr("Save &As...") + onTriggered: screen.saveAsAction() + shortcut: "Ctrl+Shift+S" + } + Action { + text: qsTr("&Open...") + onTriggered: screen.loadAction() + shortcut: "Ctrl+O" + } + Menu { + title: qsTr("&Recent Files...") + Repeater { + model: settings.recentFiles + + delegate: MenuItem { + text: qsTr("&" + index + ". " + modelData) + onTriggered: tlm.load(modelData) + } + } + } + } + + Menu { + title: qsTr("&Tab") + Action { + text: qsTr("&New Tab") + onTriggered: screen.newTab() + shortcut: "Ctrl+T" + } + + Action { + text: qsTr("&Close Tab") + onTriggered: screen.closeTab() + shortcut: "Ctrl+W" + } + + Action { + text: qsTr("Tab &Size") + onTriggered: screen.tabSize() + shortcut: "Ctrl+R" + } + + Action { + text: qsTr("&Rename Tab") + onTriggered: screen.renameTab() + shortcut: "Ctrl+N" + } + } + + Menu { + title: qsTr("&Widget") + Action { + text: qsTr("&Paste") + onTriggered: screen.paste() + shortcut: "Ctrl+V" + } + } + } + + MainScreen { + id: screen + anchors.fill: parent + } +} + + diff --git a/README.md b/README.md index c66bcc05..7fd1fc4e 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,6 @@ A reliable, high-performance, low-footprint dashboard for use with FRC. A mirror of this repository is available on my [Gitea](https://git.swurl.xyz/swirl/qfrcdashboard.git) instance. -# Key Features -- Small RAM/CPU footprint -- Customizable fonts for title & text (i.e. big numbers) -- Enum widget: different colors for different string values -- Powerful, customizable graphs -- Simple, semi-modern look & feel -- Support for swerve display, FMS information, field widget -- Different shapes for colored widgets (bool/enum) -- Run commands - ## Lightweight Dashboards don't have to be resource hogs. In fact, dashboards should be designed to take up as few resources as possible. Dashboards that use up resources like nobody's business will cause **packet loss** and **comms issues** when run on driver stations! @@ -20,19 +10,18 @@ Because of this, QFRCDashboard has been specifically designed to use up as few r | Metric | Shuffleboard | QFRCDashboard | | ----------------- | ------------- | ------------- | -| Memory (Base) | 530MB | <200MB | -| Memory (Heavy Use)| 600MB-1.2GB | 200-250MB | +| Memory (Base) | 530MB | 100MB | +| Memory (Heavy Use)| 600MB-1.2GB | 150MB | | CPU (Base) | 2-10% | 0-1% | -| CPU (Heavy Use) | 10-30% | 0-3% | +| CPU (Heavy Use) | 10-30% | 0-2% | Network activity hasn't been specifically measured, but expect better network performance out of QFRCDashboard. (Note that Shuffleboard's numbers may vary. Sometimes I've seen it eat the entire CPU on computers 5x as powerful as mine.) QFRCDashboard excels with its lightweight performance thanks to many factors: -- The efficiency of C++ and Qt vs. Java and JavaFX +- Qt & QML's high efficiency - No menu that subscribes to every topic at once - Shared subscriptions between duplicate topics - Widgets only update and repaint when they need to -- Minimal, deferred repaints ## Download Windows, Linux, and macOS builds are available via GitHub Actions. Currently, all use WPILib 2024.3.1. Release builds are available either through Actions or in the releases tab: @@ -106,6 +95,3 @@ cmake --build . C:\Qt6\6.6.1\msvc2019_64\bin\windeployqt.exe . ``` OR use [CLion](https://www.jetbrains.com/clion/) or Qt Creator from the online installer. - -## Miscellaneous Notes -QFRCDashboard is created solely for the purpose of A RELIABLE, LOW-OVERHEAD FRC dashboard. QFRCDashboard is NOT created with eye-candy or intense theming in mind. Contributions and suggestions to theming will be accepted and worked on, but without user request or contribution, QFRCDashboard will see very few updates to theming. diff --git a/lib/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in similarity index 100% rename from lib/buildconfig/BuildConfig.cpp.in rename to buildconfig/BuildConfig.cpp.in diff --git a/lib/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h similarity index 100% rename from lib/buildconfig/BuildConfig.h rename to buildconfig/BuildConfig.h diff --git a/lib/buildconfig/CMakeLists.txt b/buildconfig/CMakeLists.txt similarity index 100% rename from lib/buildconfig/CMakeLists.txt rename to buildconfig/CMakeLists.txt diff --git a/dialogs/ServerDialog.qml b/dialogs/ServerDialog.qml new file mode 100644 index 00000000..4d488d91 --- /dev/null +++ b/dialogs/ServerDialog.qml @@ -0,0 +1,95 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 6.6 +import QtQuick.Dialogs + +import QFRCDashboard + +Dialog { + id: serverDialog + + anchors.centerIn: parent + + onAccepted: { + settings.port = port.value + settings.useTeam = useTeam.checked + settings.ip = ip.text + } + + Column { + anchors.fill: parent + spacing: 5 + + CheckBox { + id: useTeam + text: "Use Team Number?" + + checked: settings.useTeam + + font.pixelSize: 17 + } + + Row { + Text { + text: "Port:" + font.pixelSize: 17 + color: Constants.palette.text + } + + SpinBox { + id: port + + value: settings.port + + from: 1 + to: 65535 + + font.pixelSize: 17 + } + } + + Row { + Text { + text: useTeam.checked ? "Team Number:" : "IP Address:" + font.pixelSize: 17 + color: Constants.palette.text + } + + TextField { + id: ip + + text: settings.ip + + validator: RegularExpressionValidator { + regularExpression: useTeam.checked ? /[0-9]{1,5}/ : /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/ + } + + font.pixelSize: 17 + } + } + + DialogButtonBox { + Shortcut { + context: Qt.WidgetWithChildrenShortcut + sequences: [Qt.Key_Escape] + + onActivated: serverDialog.reject() + } + + Button { + text: qsTr("Ok") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + Button { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole + } + + width: parent.width + font.pixelSize: 15 + + onAccepted: serverDialog.accept() + onRejected: serverDialog.reject() + } + } +} diff --git a/dialogs/TabNameDialog.qml b/dialogs/TabNameDialog.qml new file mode 100644 index 00000000..a4254a4e --- /dev/null +++ b/dialogs/TabNameDialog.qml @@ -0,0 +1,61 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 6.6 +import QtQuick.Dialogs + +import QFRCDashboard + +Dialog { + id: tabNameDialog + + anchors.centerIn: parent + + property alias tabName: tabName + + function openUp(txt) { + open() + tabName.focus = true + tabName.text = txt + } + + Column { + anchors.fill: parent + spacing: 5 + + Text { + text: "Input new tab name:" + font.pixelSize: 20 + color: Constants.palette.text + } + + TextField { + id: tabName + font.pixelSize: 20 + placeholderText: "New Tab" + } + + DialogButtonBox { + Shortcut { + context: Qt.WidgetWithChildrenShortcut + sequences: [Qt.Key_Escape] + + onActivated: tabNameDialog.reject() + } + + Button { + text: qsTr("Ok") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + Button { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole + } + + width: tabName.width + font.pixelSize: 15 + + onAccepted: tabNameDialog.accept() + onRejected: tabNameDialog.reject() + } + } +} diff --git a/dialogs/TabSizeDialog.qml b/dialogs/TabSizeDialog.qml new file mode 100644 index 00000000..12270cb6 --- /dev/null +++ b/dialogs/TabSizeDialog.qml @@ -0,0 +1,82 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 6.6 +import QtQuick.Dialogs + +import QFRCDashboard + +Dialog { + id: tabSizeDialog + + anchors.centerIn: parent + + property alias columnValue: columnValue + property alias rowValue: rowValue + + function openUp(rows, cols) { + open() + columnValue.value = cols + rowValue.value = rows + } + + Column { + anchors.fill: parent + spacing: 5 + + Row { + Text { + text: "Columns:" + font.pixelSize: 17 + color: Constants.palette.text + } + + SpinBox { + id: columnValue + from: 1 + to: 99 + + font.pixelSize: 17 + } + } + + Row { + Text { + text: "Rows:" + font.pixelSize: 17 + color: Constants.palette.text + } + + SpinBox { + id: rowValue + from: 1 + to: 99 + + font.pixelSize: 17 + } + } + + DialogButtonBox { + Shortcut { + context: Qt.WidgetWithChildrenShortcut + sequences: [Qt.Key_Escape] + + onActivated: tabSizeDialog.reject() + } + + Button { + text: qsTr("Ok") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + Button { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole + } + + width: parent.width + font.pixelSize: 15 + + onAccepted: tabSizeDialog.accept() + onRejected: tabSizeDialog.reject() + } + } +} diff --git a/dialogs/TopicView.qml b/dialogs/TopicView.qml new file mode 100644 index 00000000..55751684 --- /dev/null +++ b/dialogs/TopicView.qml @@ -0,0 +1,240 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import QFRCDashboard + +Row { + id: tv + z: 4 + + property alias menuAnim : menuAnim + + property bool isCamera: false + + property string closedText: isCamera ? "<<" : ">>" + property string openText: isCamera ? ">>" : "<<" + + width: (parent.width / 3) + tabs.height + height: parent.height + + SmoothedAnimation { + id: menuAnim + target: tv + property: "anchors." + (isCamera ? "right" : "left") + "Margin" + duration: 500 + } + + signal open + signal close + + signal addWidget(string name, string topic, string type) + signal addCamera(string name, string source, var urls) + signal dragging(point pos) + signal dropped(point pos) + + function widgetAdd(name, topic, type) { + button2.text = closedText + close() + addWidget(name, topic, type) + } + + function cameraAdd(name, source, urls) { + button.text = (closedText) + close() + addCamera(name, source, urls) + } + + Button { + id: button + text: closedText + + width: isCamera ? tabs.height : 0 + height: isCamera ? width : 0 + + onClicked: { + if (text === closedText) { + open() + text = openText + } else { + close() + text = closedText + } + } + } + + Rectangle { + id: topicView + radius: 10 + + width: parent.width - 40 + height: parent.height + + color: Constants.palette.menu + border { + color: Constants.palette.menuBorder + width: 3 + } + + // modified from Qt's example TreeView + TreeView { + id: treeView + anchors.fill: parent + anchors.margins: 10 + clip: true + + boundsBehavior: Flickable.StopAtBounds + + selectionModel: ItemSelectionModel {} + + model: isCamera ? cameras : topics + + delegate: Item { + DragHandler { + id: dh + target: null + + property bool ready: false + + onActiveChanged: if (!active && ready) { + ready = false + dropped(centroid.position) + } + + function drag() { + let global = mapToItem(topicView, centroid.position) + if (!topicView.contains(global)) { + if (!ready) { + if (isCamera) { + cameraAdd(model.name, model.source, model.urls) + } else { + widgetAdd(model.name, model.topic, model.type) + } + + ready = true + } + + let p = mapToItem(tv, centroid.position) + p.x += tv.x + dragging(p) + } + } + + yAxis.onActiveValueChanged: { + drag() + } + + xAxis.onActiveValueChanged: { + drag() + } + } + + // implicitWidth: padding + label.x + label.implicitWidth + padding + implicitHeight: label.implicitHeight * 1.5 + + implicitWidth: topicView.width - 20 + + readonly property real indentation: 20 + readonly property real padding: 5 + + // Assigned to by TreeView: + required property TreeView treeView + required property bool isTreeNode + required property bool expanded + required property int hasChildren + required property int depth + required property int row + required property int column + required property bool current + + // Rotate indicator when expanded by the user + // (requires TreeView to have a selectionModel) + property Animation indicatorAnimation: NumberAnimation { + target: indicator + property: "rotation" + from: expanded ? 0 : 90 + to: expanded ? 90 : 0 + duration: 100 + easing.type: Easing.OutQuart + } + TableView.onPooled: indicatorAnimation.complete() + TableView.onReused: if (current) + indicatorAnimation.start() + onExpandedChanged: indicator.rotation = expanded ? 90 : 0 + + Rectangle { + id: background + anchors.fill: parent + color: row === treeView.currentRow ? palette.highlight : Constants.palette.menuItem + opacity: (treeView.alternatingRows + && row % 2 !== 0) ? 0.3 : 0.1 + } + + Label { + id: indicator + x: padding + (depth * indentation) + anchors.verticalCenter: parent.verticalCenter + visible: isTreeNode && hasChildren + text: "▶" + color: Constants.palette.text + + TapHandler { + onSingleTapped: { + let index = treeView.index(row, column) + treeView.selectionModel.setCurrentIndex( + index, ItemSelectionModel.NoUpdate) + treeView.toggleExpanded(row) + } + } + + font.pixelSize: 17 + } + + Label { + id: label + x: padding + (isTreeNode ? (depth + 1) * indentation : 0) + anchors.verticalCenter: parent.verticalCenter + width: parent.width - padding - x - typeLabel.width + clip: true + text: model.name + + color: Constants.palette.text + + font.pixelSize: 17 + } + + Label { + id: typeLabel + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + clip: true + text: isCamera ? "" : model.type + + color: Constants.palette.text + + font.pixelSize: 17 + } + + } + } + } + + Button { + id: button2 + text: closedText + + width: !isCamera ? tabs.height : 0 + height: !isCamera ? width : 0 + + onClicked: { + if (text === closedText) { + open() + text = openText + } else { + close() + text = closedText + } + } + } + +} diff --git a/dialogs/WidgetConfig.qml b/dialogs/WidgetConfig.qml new file mode 100644 index 00000000..4b21c6a2 --- /dev/null +++ b/dialogs/WidgetConfig.qml @@ -0,0 +1,529 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts + +import Qt.labs.qmlmodels +import QFRCDashboard + +Dialog { + id: widgetConf + + anchors.centerIn: parent + + width: parent.width / 1.6 + height: parent.height - 60 + background: Rectangle { + color: Constants.palette.dialogBg + } + + ColorDialog { + id: colorDialog + } + + property var item + + function openUp(item) { + lm.clear() + for (var p in item) { + if (p.startsWith("item_") && typeof item[p] !== "function") { + let sub = p.substr(5) + let typeName = MetaObjectHelper.typeName(item, p) + var choices = [] + let obj = {} + + if (typeName === "QVariant") { + choices = item[sub + "Choices"] + + typeName = "list" + } + + else if (typeName === "QVariantList") { + obj["valueType"] = item[sub + "ValueType"] + obj["valueName"] = item[sub + "ValueName"] + } + + else if (typeName === "int" || typeName === "double") { + let min = item[sub + "Min"]; + let max = item[sub + "Max"]; + + obj["min"] = (typeof min === "undefined") ? 0 : min + obj["max"] = (typeof max === "undefined") ? 100000 : max + } + + lm.append({ + "name": p, + "type": typeName, + "itemValue": item[p], + "choices": choices, + "item": item, + "map": obj + }) + } + } + + this.item = item + + open() + } + + ListModel { + id: lm + dynamicRoles: true + } + + function displayText(text) { + const result = text.substring(5).replace(/([A-Z])/g, " $1") + return result.charAt(0).toUpperCase() + result.slice(1) + } + + function getValues() { + for (var i = 0; i < listView.count; ++i) { + let idx = lm.get(i) + item[idx.name] = idx.itemValue + } + } + + ListView { + anchors { + top: parent.top + bottom: buttonBox.bottom + left: parent.left + right: parent.right + + margins: 8 + } + + spacing: 8 + + id: listView + + clip: true + + boundsBehavior: Flickable.StopAtBounds + + model: lm + + component FieldLabel: Label { + Layout.fillWidth: true + + text: displayText(model.name) + font.pixelSize: 15 + color: Constants.palette.text + } + + delegate: DelegateChooser { + role: "type" + + // TODO: may want to find a way to avoid all this repeat code + DelegateChoice { + roleValue: "QString" + + RowLayout { + clip: true + width: parent.width + + uniformCellSizes: true + + FieldLabel {} + + TextField { + id: txt + Layout.fillWidth: true + + font.pixelSize: 15 + text: model.itemValue + + onTextEdited: model.itemValue = text + } + } + } + + DelegateChoice { + roleValue: "bool" + + RowLayout { + clip: true + width: parent.width + + uniformCellSizes: true + + FieldLabel {} + + CheckBox { + checked: model.itemValue + + onClicked: model.itemValue = checked + + indicator.implicitHeight: 20 + indicator.implicitWidth: 20 + } + } + } + + DelegateChoice { + roleValue: "int" + + RowLayout { + clip: true + width: parent.width + + uniformCellSizes: true + + FieldLabel {} + + SpinBox { + from: map.min + to: map.max + + id: sb + + Layout.fillWidth: true + + font.pixelSize: 15 + value: model.itemValue + + onValueModified: model.itemValue = value + } + } + } + + DelegateChoice { + roleValue: "double" + + RowLayout { + clip: true + width: parent.width + + uniformCellSizes: true + + FieldLabel {} + + DoubleSpinBox { + from: map.min + to: map.max + + id: dsb + font.pixelSize: 15 + Layout.fillWidth: true + + value: model.itemValue + + stepSize: 0.1 + + onValueModified: model.itemValue = value + } + } + } + + DelegateChoice { + roleValue: "QColor" + + RowLayout { + clip: true + width: parent.width + + uniformCellSizes: true + + FieldLabel {} + + TextField { + id: colorField + font.pixelSize: 15 + Layout.fillWidth: true + + text: model.itemValue + + onTextEdited: model.itemValue = text + } + + Button { + Layout.fillWidth: true + text: "Pick" + + function setColor() { + colorField.text = colorDialog.selectedColor + model.itemValue = colorDialog.selectedColor + colorDialog.accepted.disconnect(setColor) + } + + onClicked: { + colorDialog.selectedColor = colorField.text + colorDialog.accepted.connect(setColor) + colorDialog.open() + } + } + } + } + + DelegateChoice { + roleValue: "list" + + RowLayout { + clip: true + width: parent.width + + uniformCellSizes: true + + FieldLabel {} + + function setValue(v) { + model.itemValue = v + } + + ComboBox { + model: choices + + delegate: ItemDelegate { + id: delegate + + width: choices.width + contentItem: Text { + text: modelData + color: "white" + font.pixelSize: 15 + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + highlighted: choices.highlightedIndex === index + } + + id: cb + font.pixelSize: 15 + Layout.fillWidth: true + + Component.onCompleted: { + currentIndex = choices.indexOf(itemValue) + currentIndexChanged.connect(updateValue) + } + + function updateValue() { + setValue(valueAt(currentIndex)) + } + } + } + } + + DelegateChoice { + roleValue: "QVariantList" + + GridLayout { + clip: true + width: parent.width + + rowSpacing: 0 + + rows: 4 + columns: 2 + + FieldLabel {} + + function setValue(v) { + model.itemValue = v + } + + function getValue() { + // stupid workaround because of cancer + // itemValue gets corrupted to a QQmlListModel for some reason + // kill me + return model.item[model.name] + } + + HorizontalHeaderView { + Layout.minimumWidth: parent.width - 90 + Layout.row: 0 + Layout.column: 1 + + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.columnSpan: 2 + id: horizontalHeader + syncView: tbl + clip: true + } + + TableView { + Layout.minimumWidth: parent.width - 90 + Layout.minimumHeight: 40 + columnSpacing: 1 + rowSpacing: 1 + clip: true + + id: tbl + + Layout.fillWidth: true + + Layout.row: 1 + Layout.column: 1 + + Layout.rowSpan: 3 + + interactive: false + + function resetHeight() { + Layout.minimumHeight = 42 * tblModel.rowCount() + } + + Component.onCompleted: { + let vl = map.valueName + let iv = getValue() + + for (let i = 0; i < iv.length; ++i) { + tblModel.add(iv[i]["Value"], iv[i][vl]) + tbl.resetHeight() + } + + setValue(tblModel.asList()) + tblModel.dataChanged.connect(() => setValue(tblModel.asList())) + } + + model: MapModel { + id: tblModel + valueName: map.valueName + } + + selectionModel: ItemSelectionModel {} + selectionBehavior: TableView.SelectRows + selectionMode: TableView.SingleSelection + + delegate: Rectangle { + border { + color: Constants.palette.text + width: 2 + } + + required property bool selected + required property bool current + + implicitWidth: tbl.width / 2 + implicitHeight: 40 + + color: current ? "blue" : "black" + + Text { + font.pixelSize: 15 + anchors.centerIn: parent + text: display + color: Constants.palette.text + } + + TableView.editDelegate: TextField { + font.pixelSize: 15 + anchors.fill: parent + text: display + horizontalAlignment: TextInput.AlignHCenter + verticalAlignment: TextInput.AlignVCenter + Component.onCompleted: selectAll() + + TableView.onCommit: { + display = text + } + } + } + } + + Button { + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Layout.row: 1 + Layout.column: 0 + text: "Add" + + onClicked: { + tblModel.add("", "") + setValue(tblModel.asList()) + tbl.resetHeight() + } + } + + Button { + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Layout.row: 2 + Layout.column: 0 + text: "Delete" + + onClicked: { + tblModel.remove(tbl.currentRow) + setValue(tblModel.asList()) + tbl.resetHeight() + } + } + } + } + + DelegateChoice { + roleValue: "QSizeF" + + RowLayout { + clip: true + width: parent.width + + + FieldLabel { + Layout.rowSpan: 4 + } + + Label { + text: "Width:" + font.pixelSize: 15 + } + + SpinBox { + from: 0 + to: 2000 + + id: width + + Layout.fillWidth: true + + font.pixelSize: 15 + value: model.itemValue.width + + onValueModified: model.itemValue.width = value + } + + Label { + text: "Height:" + font.pixelSize: 15 + } + + SpinBox { + from: 0 + to: 2000 + + id: height + + Layout.fillWidth: true + + font.pixelSize: 15 + value: model.itemValue.height + + onValueModified: model.itemValue.height = value + } + } + } + } + } + + DialogButtonBox { + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + + margins: 8 + } + + id: buttonBox + standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel + + onAccepted: { + getValues() + widgetConf.accept() + } + + onRejected: { + widgetConf.reject() + } + } +} diff --git a/lib/fields.qrc b/fields.qrc similarity index 53% rename from lib/fields.qrc rename to fields.qrc index c8449309..8096da7d 100644 --- a/lib/fields.qrc +++ b/fields.qrc @@ -1,6 +1,8 @@ images/2023-field.png + images/2023-field-vertical.png images/2024-field.png + images/2024-field-vertical.png diff --git a/images/2023-field-vertical.png b/images/2023-field-vertical.png new file mode 100644 index 00000000..bfb91a86 Binary files /dev/null and b/images/2023-field-vertical.png differ diff --git a/lib/images/2023-field.png b/images/2023-field.png similarity index 100% rename from lib/images/2023-field.png rename to images/2023-field.png diff --git a/images/2024-field-vertical.png b/images/2024-field-vertical.png new file mode 100644 index 00000000..2447baa1 Binary files /dev/null and b/images/2024-field-vertical.png differ diff --git a/lib/images/2024-field.png b/images/2024-field.png similarity index 100% rename from lib/images/2024-field.png rename to images/2024-field.png diff --git a/lib/include/Constants.h b/include/Constants.h similarity index 64% rename from lib/include/Constants.h rename to include/Constants.h index 7d293322..4fd23dfb 100644 --- a/lib/include/Constants.h +++ b/include/Constants.h @@ -5,6 +5,11 @@ #include typedef struct Setting { + Q_PROPERTY(QString name MEMBER Name CONSTANT FINAL) + Q_PROPERTY(QVariant value READ value CONSTANT) + Q_GADGET + +public: const QString Name; const QVariant DefaultValue; @@ -13,10 +18,11 @@ typedef struct Setting { } Setting; namespace Settings { -extern const Setting FirstRun; +Q_NAMESPACE extern const Setting RecentFiles; extern const Setting LoadRecent; -extern const Setting StyleSheet; +extern const Setting Theme; +extern const Setting Accent; } #endif // CONSTANTS_H diff --git a/include/Flags.h b/include/Flags.h new file mode 100644 index 00000000..7f03c682 --- /dev/null +++ b/include/Flags.h @@ -0,0 +1,38 @@ +#ifndef QFDFlags_H +#define QFDFlags_H + +#include +#include + +namespace QFDFlags { + Q_NAMESPACE + +enum Direction { + LEFT = 0x1, + RIGHT = 0x2, + TOP = 0x4, + BOTTOM = 0x8 +}; + +Q_ENUM_NS(Direction); + +Q_DECLARE_FLAGS(Directions, Direction); +Q_DECLARE_OPERATORS_FOR_FLAGS(Directions) + +enum class ControlWord { + Invalid = 0x0, + Enabled = 0x1, + Auto = 0x2, + Test = 0x4, + EStop = 0x8, + FMSAttached = 0x10, + DSAttached = 0x20 +}; + +Q_ENUM_NS(ControlWord) + +Q_DECLARE_FLAGS(ControlFlags, ControlWord) +Q_DECLARE_OPERATORS_FOR_FLAGS(ControlFlags) +} + +#endif // QFDFlags_H diff --git a/include/Globals.h b/include/Globals.h new file mode 100644 index 00000000..b37ff016 --- /dev/null +++ b/include/Globals.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +// STRUCTS // +typedef struct { + bool teamNumber; + std::string server; + int port; + QString switchTopic; +} ServerData; + +// NAMESPACES // +namespace Globals { +extern nt::NetworkTableInstance inst; +extern ServerData server; +} diff --git a/include/MetaObjectHelper.h b/include/MetaObjectHelper.h new file mode 100644 index 00000000..5560a194 --- /dev/null +++ b/include/MetaObjectHelper.h @@ -0,0 +1,23 @@ +#ifndef METAOBJECTHELPER_H +#define METAOBJECTHELPER_H + +#include +#include +#include + +class MetaObjectHelper : public QObject { + Q_OBJECT + QML_ELEMENT + QML_SINGLETON +public: + using QObject::QObject; + Q_INVOKABLE QString typeName(QObject* object, const QString& property) const + { + QQmlProperty qmlProperty(object, property); + QMetaProperty metaProperty = qmlProperty.property(); + return metaProperty.typeName(); + } +}; + +#endif // METAOBJECTHELPER_H + diff --git a/include/SettingsManager.h b/include/SettingsManager.h new file mode 100644 index 00000000..4637c0b8 --- /dev/null +++ b/include/SettingsManager.h @@ -0,0 +1,68 @@ +#ifndef SETTINGSMANAGER_H +#define SETTINGSMANAGER_H + +#include +#include +#include + +class SettingsManager : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(bool loadRecent READ loadRecent WRITE setLoadRecent NOTIFY loadRecentChanged FINAL) + Q_PROPERTY(QStringList recentFiles READ recentFiles WRITE setRecentFiles NOTIFY recentFilesChanged FINAL) + Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged FINAL) + + Q_PROPERTY(bool useTeam READ useTeam WRITE setUseTeam NOTIFY useTeamChanged FINAL) + Q_PROPERTY(QString ip READ ip WRITE setIp NOTIFY ipChanged FINAL) + Q_PROPERTY(int port READ getPort WRITE setPort NOTIFY portChanged FINAL) + Q_PROPERTY(QString switchTopic READ switchTopic WRITE setSwitchTopic NOTIFY switchTopicChanged FINAL) +public: + explicit SettingsManager(QObject *parent = nullptr); + + // NT + bool useTeam() const; + void setUseTeam(bool newUseTeam); + + QString ip() const; + void setIp(const QString &newIp); + + int getPort() const; + void setPort(int newPort); + + QString switchTopic() const; + void setSwitchTopic(const QString &newTopic); + + // other + bool loadRecent() const; + void setLoadRecent(bool newLoadRecent); + + QStringList recentFiles() const; + void setRecentFiles(const QStringList &newRecentFiles); + + void addRecentFile(QFile &file); + + void reconnectServer(); + + QString theme() const; + void setTheme(const QString &newTheme); + + QString accent() const; + void setAccent(const QString &newAccent); + +signals: + void useTeamChanged(); + void ipChanged(); + void portChanged(); + void switchTopicChanged(); + + void recentFilesChanged(); + void loadRecentChanged(); + void themeChanged(); + void accentChanged(); +private: + Q_PROPERTY(QString accent READ accent WRITE setAccent NOTIFY accentChanged FINAL) +}; + +#endif // SETTINGSMANAGER_H diff --git a/include/TitleManager.h b/include/TitleManager.h new file mode 100644 index 00000000..dad99c6c --- /dev/null +++ b/include/TitleManager.h @@ -0,0 +1,27 @@ +#ifndef TITLEMANAGER_H +#define TITLEMANAGER_H + +#include "BuildConfig.h" +#include +#include + +class TitleManager : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + explicit TitleManager(QObject *parent = nullptr); + + QString title() const; + void setTitle(const QString &newTitle); + void resetTitle(); + +private: + QString m_title = BuildConfig.APP_NAME + " - Not Connected"; + Q_PROPERTY(QString title READ title WRITE setTitle RESET resetTitle NOTIFY titleChanged FINAL) + +signals: + void titleChanged(); +}; + +#endif // TITLEMANAGER_H diff --git a/include/models/CameraListModel.h b/include/models/CameraListModel.h new file mode 100644 index 00000000..3ccfe4c6 --- /dev/null +++ b/include/models/CameraListModel.h @@ -0,0 +1,60 @@ +#ifndef CAMERALISTMODEL_H +#define CAMERALISTMODEL_H + +#include "TopicStore.h" +#include "networktables/NetworkTable.h" +#include +#include +#include + +typedef struct Camera +{ +public: + static struct Camera fromTable(std::shared_ptr table); + + QList urls; + QString name; + QString source; +} Camera; + +class CameraListModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + enum CLMRoleTypes { + NAME = Qt::UserRole, + URLS, + SOURCE + }; + + explicit CameraListModel(TopicStore &store, QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Editable: + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + // Add data: + Q_INVOKABLE void add(std::shared_ptr table); + + // Remove data: + void clear(); + Q_INVOKABLE bool remove(int row, const QModelIndex &parent = QModelIndex()); + +protected: + QHash roleNames() const override; + +private: + QList m_data; + + TopicStore *m_store; +}; + +#endif // CAMERALISTMODEL_H diff --git a/include/models/MapModel.h b/include/models/MapModel.h new file mode 100644 index 00000000..7ee564b3 --- /dev/null +++ b/include/models/MapModel.h @@ -0,0 +1,60 @@ +#ifndef MAPMODELMODEL_H +#define MAPMODELMODEL_H + +#include +#include + +typedef struct { + QString key; + QString value; +} Data; + +class MapModel : public QAbstractTableModel +{ + Q_OBJECT + QML_ELEMENT +public: + enum MMRoleNames { + KEY = Qt::UserRole + 1, + VALUE + }; + explicit MapModel(QObject *parent = nullptr); + + // Header: + QVariant headerData(int section, + Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Editable: + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + Q_INVOKABLE void add(QString key = "", QString value = ""); + Q_INVOKABLE void remove(int row); + Q_INVOKABLE QVariantList asList(); + + QString valueName() const; + void setValueName(const QString &newValueName); + +signals: + void valueNameChanged(); + +protected: + QHash roleNames() const override; + + +private: + QList m_data; + + QString m_valueName; + Q_PROPERTY(QString valueName READ valueName WRITE setValueName NOTIFY valueNameChanged FINAL) +}; + +#endif // MAPMODELMODEL_H diff --git a/include/models/TabListModel.h b/include/models/TabListModel.h new file mode 100644 index 00000000..76e758bd --- /dev/null +++ b/include/models/TabListModel.h @@ -0,0 +1,67 @@ +#ifndef TABLISTMODEL_H +#define TABLISTMODEL_H + +#include +#include +#include +#include +#include + +#include "SettingsManager.h" +#include "TabWidgetsModel.h" + +typedef struct { + QString title; + + int rows; + int cols; + + TabWidgetsModel *model; +} Tab; + +class TabListModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + enum TLMRoleTypes { + TITLE = Qt::UserRole, + ROWS, + COLS, + WIDGETS + }; + + explicit TabListModel(SettingsManager *settings = nullptr, QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Editable: + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + // Add data: + Q_INVOKABLE void add(Tab t); + Q_INVOKABLE void add(QString title); + + // Remove data: + Q_INVOKABLE bool remove(int row, const QModelIndex &parent = QModelIndex()); + + Q_INVOKABLE void save(const QString &filename = ""); + Q_INVOKABLE QJsonDocument saveObject() const; + Q_INVOKABLE void loadObject(const QJsonDocument &doc); + Q_INVOKABLE void load(const QString &fileName = ""); + +protected: + QHash roleNames() const override; + +private: + QList m_data; + + SettingsManager *m_settings; +}; +#endif // TABLISTMODEL_H diff --git a/include/models/TabWidgetsModel.h b/include/models/TabWidgetsModel.h new file mode 100644 index 00000000..24f2770a --- /dev/null +++ b/include/models/TabWidgetsModel.h @@ -0,0 +1,109 @@ +#ifndef TABWIDGETSMODEL_H +#define TABWIDGETSMODEL_H + +#include +#include +#include + +typedef struct Widget { + Q_GADGET + QML_ELEMENT + +public: + QString title; + QString topic; + + QMetaType dataType; + QString type; + + int row; + int col; + int rowSpan; + int colSpan; + + QVariantMap properties; +} Widget; + +class TabWidgetsModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + enum TWMRoleTypes { + TITLE = Qt::UserRole, + ROW, + COL, + ROWSPAN, + COLSPAN, + TYPE, + TOPIC, + PROPERTIES, + IDX + }; + + explicit TabWidgetsModel(QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Editable: + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + // Add data: + Q_INVOKABLE Widget copy(int idx); + Q_INVOKABLE void add(Widget w); + Q_INVOKABLE void add(QString title, QString topic, QString type); + Q_INVOKABLE void addCamera(QString name, QString source, QVariantList urls); + + Q_INVOKABLE void setEqualTo(TabWidgetsModel *w); + + QList data(); + + // Remove data: + Q_INVOKABLE bool remove(int idx); + Q_INVOKABLE bool removeLatest(); + + int rows() const; + void setRows(int newRows); + + int cols() const; + void setCols(int newCols); + + Q_INVOKABLE bool cellOccupied(int row, int col, int rowSpan = 1, int colSpan = 1, QRectF ignore = QRectF(-1, -1, -1, -1)); + + int unoccupiedCells() const; + void setUnoccupiedCells(int newUnoccupiedCells); + + QJsonArray saveObject() const; + static TabWidgetsModel *loadObject(QObject *parent, const QJsonArray &arr); + +protected: + QHash roleNames() const override; + + +signals: + void rowsChanged(); + + void colsChanged(); + + void unoccupiedCellsChanged(); + +private: + QList m_data; + + int m_rows; + int m_cols; + + Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged FINAL) + Q_PROPERTY(int cols READ cols WRITE setCols NOTIFY colsChanged FINAL) + Q_PROPERTY(int unoccupiedCells READ unoccupiedCells NOTIFY unoccupiedCellsChanged FINAL) +}; + +Q_DECLARE_METATYPE(TabWidgetsModel) + +#endif // TABWIDGETSMODEL_H diff --git a/include/models/TopicListModel.h b/include/models/TopicListModel.h new file mode 100644 index 00000000..e8792102 --- /dev/null +++ b/include/models/TopicListModel.h @@ -0,0 +1,33 @@ +#ifndef TOPICLISTMODEL_H +#define TOPICLISTMODEL_H + +#include "TopicStore.h" +#include +#include + +class TopicListModel : public QStandardItemModel +{ + QML_ELEMENT + Q_OBJECT + +public: + enum TLMRoleTypes { + NAME = Qt::UserRole, + TYPE, + TOPIC + }; + + TopicListModel(TopicStore &store, QObject *parent = nullptr); + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + Q_INVOKABLE void reload(); + + void add(const QString &toAdd); + void remove(const QString &toRemove); + +private: + QStringList m_data; + TopicStore *m_store; +}; + +#endif // MODELSTOPICLISTMODEL_H diff --git a/include/stores/TopicStore.h b/include/stores/TopicStore.h new file mode 100644 index 00000000..050787d0 --- /dev/null +++ b/include/stores/TopicStore.h @@ -0,0 +1,63 @@ +#ifndef TopicStore_H +#define TopicStore_H + +#include "networktables/NetworkTableEntry.h" +#include "Globals.h" +#include "Flags.h" + +#include +#include +#include +#include + +struct Listener { + QString topic; + NT_Listener listenerHandle; + nt::ListenerCallback callback; + int numSubscribed; + bool isNull; + + bool operator==(const Listener &other) const; +}; + +class TopicStore : public QObject +{ + Q_OBJECT + QML_ELEMENT +private: + bool hasEntry(QString topic); + + Q_INVOKABLE Listener entry(QString topic); + Q_INVOKABLE Listener changeNumSubscribed(QString topic, int changeBy = 1); + + QList Listeners; + QHash topicEntryMap; + +public: + static QVariant toVariant(const nt::Value &value); + static nt::Value toValue(const QVariant &value); + + TopicStore(QObject *parent = nullptr); + + void connect(bool connected); + + Q_INVOKABLE void subscribe(QString ntTopic); + Q_INVOKABLE void unsubscribe(QString ntTopic); + + double getDoubleFromEntry(nt::NetworkTableEntry entry); + + Q_INVOKABLE QVariant getValue(QString topic); + Q_INVOKABLE void setValue(QString topic, const QVariant &value); + + QString typeString(QString topic); + + Q_INVOKABLE inline QFDFlags::ControlWord toWord(int val) + { + return (QFDFlags::ControlWord) val; + } +signals: + void topicUpdate(QString topic, QVariant newValue); + void connected(bool connected); +}; + +#endif // TopicStore_H diff --git a/items/GridHandler.qml b/items/GridHandler.qml new file mode 100644 index 00000000..5f807379 --- /dev/null +++ b/items/GridHandler.qml @@ -0,0 +1,246 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 6.6 + +import QFRCDashboard + +Repeater { + id: rep + model: grid.rows * grid.columns + + signal resizeBegin(var drag, var valid) + signal resizeEnd(var drag, var valid) + + signal fullCancelDrag() + signal intersectionCheck(var drag, var source) + + function checkIntersects(drag, source) { + intersected = [] + + intersectionCheck(drag, source) + + calcRectangle(drag) + fullCancelDrag() + } + + function recalcValid() { + let valid = true + + for (let i = 0; i < validResize.length; ++i) { + if (!validResize[i]) valid = false + } + return valid + } + + function beginResize(drag) { + let valid = recalcValid() + + resizeBegin(drag, valid) + drag.source.caught = valid + } + + function endResize(drag) { + let valid = recalcValid() + + drag.source.caught = valid + intersected = [] + resizeEnd(drag, valid) + + calcRectangle(drag) + } + + property list validResize + + Component.onCompleted: { + for (let i = 0; i < model; ++i) { + validResize[i] = true + } + } + + property list intersected + + function occupied() { + for (let i = 0; i < model; ++i) { + if (itemAt(i).isOccupied()) { + return Qt.point(i / grid.columns, i % grid.columns) + } + } + + return Qt.point(-1, -1) + } + + function calcRectangle(drag) { + let rows = [] + let cols = [] + + for (let i = 0; i < intersected.length; ++i) { + let r = intersected[i] + if (r === Qt.rect(0, 0, 0, 0)) continue; + + rows.push(r.y) + cols.push(r.x) + } + + let minRow = Math.min(...rows) + let maxRow = Math.max(...rows) + let minCol = Math.min(...cols) + let maxCol = Math.max(...cols) + + let height = maxRow - minRow + 1 + let width = maxCol - minCol + 1 + + drag.source.Layout.rowSpan = grid.rows + 1 + drag.source.Layout.columnSpan = grid.columns + 1 + drag.source.Layout.row = grid.rows + 1 + drag.source.Layout.column = grid.columns + 1 + drag.source.update() + + drag.source.mrowSpan = height + drag.source.mcolumnSpan = width + drag.source.mrow = minRow + drag.source.mcolumn = minCol + + drag.source.Layout.rowSpan = height + drag.source.Layout.columnSpan = width + drag.source.Layout.column = minCol + drag.source.Layout.row = minRow + } + + delegate: Rectangle { + id: delRect + required property int modelData + + color: Constants.palette.bg + z: 1 + border { + color: "gray" + width: 2 + } + + Layout.row: modelData / grid.columns + Layout.column: modelData % grid.columns + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + + function isOccupied() { + // @disable-check M126 + return border.color == "#90ee90" + } + + function resetBorder() { + border.color = "gray" + border.width = 2 + } + + function valueInRange(value, min, max) { + return (value >= min) && (value <= max) + } + + function intersects(A, B) { + let xOverlap = valueInRange(A.x, B.x, B.x + B.width - 1) || + valueInRange(B.x, A.x, A.x + A.width - 1); + + let yOverlap = valueInRange(A.y, B.y, B.y + B.height - 1) || + valueInRange(B.y, A.y, A.y + A.height - 1); + + return xOverlap && yOverlap; + } + + function rectDrag(drag) { + let sourceRect = Qt.rect(drag.source.x, drag.source.y, drag.source.width, drag.source.height) + let myRect = Qt.rect(x, y, width, height) + + if (intersects(sourceRect, myRect)) { + drop.dragEnter(drag) + } else { + rep.validResize[modelData] = true + resetBorder() + } + } + + function endDrag(drag) { + let sourceRect = Qt.rect(drag.source.x, drag.source.y, drag.source.width, drag.source.height) + let myRect = Qt.rect(x, y, width, height) + + if (intersects(sourceRect, myRect)) { + rep.intersected[modelData] = Qt.rect(Layout.column, Layout.row, 1, 1) + } + + rep.validResize[modelData] = true + resetBorder() + } + + function checkIntersect(drag, source) { + let myRect = Qt.rect(Layout.column, Layout.row, 1, 1) + + if (intersects(source, myRect)) { + let realSource = Qt.rect(drag.source.mcolumn, drag.source.mrow, drag.source.mcolumnSpan, drag.source.mrowSpan) + rep.validResize[modelData] = drop.validSpotRect(realSource) + + rep.intersected[modelData] = Qt.rect(Layout.column, Layout.row, 1, 1) + + border.color = rep.recalcValid() ? "light green" : "red" + border.width = 5 + } else { + rep.validResize[modelData] = true + resetBorder() + } + + let valid = rep.recalcValid() + + drag.source.caught = valid + } + + Component.onCompleted: { + rep.resizeBegin.connect(rectDrag) + rep.resizeEnd.connect(endDrag) + rep.intersectionCheck.connect(checkIntersect) + rep.fullCancelDrag.connect(resetBorder) + } + + DropArea { + id: drop + anchors.fill: parent + + function validSpotRect(source) { + return !twm.cellOccupied(parent.Layout.row, parent.Layout.column, parent.Layout.rowSpan, parent.Layout.columnSpan, source) + } + + function validSpot(drag) { + let source = Qt.rect(drag.source.mcolumn, drag.source.mrow, drag.source.mcolumnSpan, drag.source.mrowSpan) + return validSpotRect(source) + } + + function dragEnter(drag, valid) { + parent.border.width = 5 + + rep.validResize[modelData] = validSpot(drag) + + parent.border.color = rep.recalcValid() ? "light green" : "red" + } + + onEntered: (drag) => { + { + parent.border.width = 5 + + let source = Qt.rect(parent.Layout.column, parent.Layout.row, drag.source.mcolumnSpan, drag.source.mrowSpan) + rep.intersectionCheck(drag, source) + } + } + + onExited: { + rep.validResize[modelData] = true + resetBorder() + } + + onDropped: (drag) => { + let source = Qt.rect(parent.Layout.column, parent.Layout.row, drag.source.mcolumnSpan, drag.source.mrowSpan) + rep.checkIntersects(drag, source) + drag.accept() + + rep.fullCancelDrag() + } + } + } +} diff --git a/items/MainScreen.qml b/items/MainScreen.qml new file mode 100644 index 00000000..20a1e566 --- /dev/null +++ b/items/MainScreen.qml @@ -0,0 +1,358 @@ +import QtCore +import QtQuick 6.7 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 6.6 +import QtQuick.Dialogs + +import QFRCDashboard + +Rectangle { + id: mainScreen + width: Constants.width + height: Constants.height + color: Constants.palette.bg + + property string filename: "" + property bool readyDragging + property var clipboard: null + + Shortcut { + sequences: ["Ctrl+Tab"] + onActivated: swipe.incrementCurrentIndex() + } + + Shortcut { + sequences: ["Ctrl+Shift+Tab"] + onActivated: swipe.decrementCurrentIndex() + } + + function openConf(item) { + widgetConf.openUp(item) + } + + Component.onCompleted: { + if (settings.loadRecent && !settings.recentFiles.empty) { + filename = settings.recentFiles[0] + tlm.load(filename) + } + } + + function drag(pos, fromList) { + if (currentTab() !== null) { + let w = currentTab().latestWidget + w.x = pos.x + w.y = pos.y - (fromList ? tabs.height : 0) + + w.width = 1 + w.height = 1 + + w.visible = false + + if (!readyDragging) { + w.dragArea.drag.target = w + readyDragging = true + + mainScreen.z = 3 + } + w.resizeBegin(w.Drag) + } + } + + function drop(pos, fromList) { + if (currentTab() !== null) { + let w = currentTab().latestWidget + if (!w.caught) { + w.cancelDrag() + if (fromList) currentTab().removeLatest() + } else { + let p = currentTab().gridHandler.occupied() + if (p.x === -1 || p.y === -1) { + w.cancelDrag() + if (fromList) currentTab().removeLatest() + } else { + w.mrow = p.x + w.mcolumn = p.y + + w.z = 2 + w.visible = true + w.cancelDrag() + } + } + } + } + + + TopicView { + id: tv + + onAddWidget: (title, topic, type) => { + currentTab().add(title, topic, type) + } + + anchors { + left: parent.left + leftMargin: -(parent.width / 3) + + top: parent.top + bottom: parent.bottom + } + + onOpen: { + menuAnim.from = -(parent.width / 3) + menuAnim.to = 0 + menuAnim.start() + } + + onClose: { + menuAnim.to = -(parent.width / 3) + menuAnim.from = 0 + menuAnim.start() + } + + onDragging: pos => drag(pos, true); + + onDropped: pos => drop(pos, true); + } + + TopicView { + id: cl + + isCamera: true + + onAddCamera: (name, source, urls) => { + currentTab().addCamera(name, source, urls) + } + + anchors { + right: parent.right + rightMargin: -(parent.width / 3) + + top: parent.top + bottom: parent.bottom + } + + onOpen: { + menuAnim.from = -(parent.width / 3) + menuAnim.to = 0 + menuAnim.start() + } + + onClose: { + menuAnim.to = -(parent.width / 3) + menuAnim.from = 0 + menuAnim.start() + } + + onDragging: pos => drag(pos, true) + + onDropped: pos => drop(pos, true) + } + + TabNameDialog { + id: tabNameDialog + } + + TabNameDialog { + id: tabRenameDialog + } + + TabSizeDialog { + id: tabSizeDialog + } + + WidgetConfig { + id: widgetConf + } + + /** SAVE */ + FileDialog { + id: saveDialog + currentFolder: StandardPaths.writableLocation( + StandardPaths.HomeLocation) + fileMode: FileDialog.SaveFile + defaultSuffix: "json" + selectedNameFilter.index: 0 + nameFilters: ["JSON files (*.json)", "All files (*)"] + } + + function save() { + if (filename === "") + return saveAsAction() + + tlm.save(filename) + } + + function saveAs() { + filename = saveDialog.selectedFile + + tlm.save(filename) + } + + function saveAsAction() { + saveDialog.accepted.connect(saveAs) + saveDialog.open() + } + + /** LOAD */ + FileDialog { + id: loadDialog + currentFolder: StandardPaths.writableLocation( + StandardPaths.HomeLocation) + fileMode: FileDialog.OpenFile + defaultSuffix: "json" + selectedNameFilter.index: 0 + nameFilters: ["JSON files (*.json)", "All files (*)"] + } + + function load() { + filename = loadDialog.selectedFile + tlm.load(filename) + } + + function loadAction() { + loadDialog.accepted.connect(load) + loadDialog.open() + } + + /** TAB SETTINGS */ + function addTab() { + tabNameDialog.accepted.disconnect(addTab) + tlm.add(tabNameDialog.tabName.text) + swipe.setCurrentIndex(swipe.count - 1) + } + + function newTab() { + tabNameDialog.accepted.connect(addTab) + tabNameDialog.openUp("") + } + + function tabRename() { + tabRenameDialog.accepted.disconnect(tabRename) + currentTab().setName(tabRenameDialog.tabName.text) + } + + function renameTab() { + tabRenameDialog.accepted.connect(tabRename) + tabRenameDialog.openUp(currentTab().name()) + } + + function setSize() { + tabSizeDialog.accepted.disconnect(setSize) + currentTab().setSize(tabSizeDialog.rowValue.value, + tabSizeDialog.columnValue.value) + } + + function tabSize() { + tabSizeDialog.accepted.connect(setSize) + tabSizeDialog.openUp(currentTab().rows(), currentTab().cols()) + } + + function currentTab() { + return swipe.currentItem + } + + /** CLOSE TAB */ + MessageDialog { + id: tabClose + title: "Close Tab?" + text: "Are you sure you want to close this tab?" + buttons: MessageDialog.Yes | MessageDialog.No + + onAccepted: { + tlm.remove(swipe.currentIndex) + } + } + + function closeTab() { + tabClose.open() + } + + /** PASTE */ + function paste() { + if (clipboard != null) { + currentTab().paste(clipboard) + } + } + + /** SERVER SETTINGS */ + ServerDialog { + id: serverDialog + } + + function serverSettings() { + serverDialog.open() + } + + SwipeView { + id: swipe + + z: 0 + + anchors { + top: tabs.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + currentIndex: tabs.currentIndex + Repeater { + id: swRep + model: tlm + + Tab { + width: swipe.width + height: swipe.height + + onCopying: pos => drag(pos, false) + + onDropped: pos => drop(pos, false) + + onStoreWidget: w => clipboard = w + } + } + } + + TabBar { + id: tabs + + anchors { + top: parent.top + left: tv.right + right: cl.left + } + + position: TabBar.Footer + currentIndex: swipe.currentIndex + spacing: 2 + + Repeater { + id: tabRep + model: tlm + + TabButton { + text: model.title + + font.pixelSize: 18 + width: Math.max(100, tabs.width / 6) + + contentItem: Label { + font.pixelSize: 18 + text: parent.text + + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignHCenter + + color: index === tabs.currentIndex ? Constants.tab : Constants.palette.text + } + + background: Rectangle { + implicitWidth: parent.width + topLeftRadius: 7 + topRightRadius: 7 + color: index !== tabs.currentIndex ? Constants.tab : Constants.palette.text + } + } + } + } +} diff --git a/items/Tab.qml b/items/Tab.qml new file mode 100644 index 00000000..85f110db --- /dev/null +++ b/items/Tab.qml @@ -0,0 +1,348 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 6.6 + +import Qt.labs.qmlmodels + +import QFRCDashboard + +Rectangle { + id: tab + width: Constants.width + height: Constants.height + color: Constants.palette.bg + + property var latestWidget + + property alias gridHandler: rep + + signal copying(point mousePos) + signal dropped(point mousePos) + + signal storeWidget(var w) + + property bool isCopying: false + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + + property point mouseCoordinates: Qt.point(0, 0) + + onPositionChanged: mouse => + { + mouseCoordinates = Qt.point(mouse.x, mouse.y) + if (isCopying) { + copying(mouseCoordinates) + } + } + + onClicked: { + if (copying) { + isCopying = false + dropped(mouseCoordinates) + } + } + } + + + TabWidgetsModel { + id: twm + + rows: model.rows + cols: model.cols + } + + Component.onCompleted: { + if (model.widgets === null) + model.widgets = twm + else { + twm.setEqualTo(model.widgets) + model.widgets = twm + } + } + + function copy(idx) { + let w = twm.copy(idx); + storeWidget(w) + } + + function paste(w) { + twm.add(w) + isCopying = true + copying(mouseArea.mouseCoordinates) + } + + function removeLatest() { + twm.removeLatest() + } + + function add(title, topic, type) { + twm.add(title, topic, type) + } + + function addCamera(name, source, urls) { + twm.addCamera(name, source, urls) + } + + function setName(name) { + model.title = name + } + + function name() { + return model.title + } + + function setSize(r, c) { + model.rows = r + model.cols = c + + twm.rows = r + twm.cols = c + + grid.rows = r + grid.columns = c + } + + function cols() { + return model.cols + } + + function rows() { + return model.rows + } + + GridLayout { + id: grid + rows: model.rows + columns: model.cols + + anchors.fill: parent + + columnSpacing: 0 + rowSpacing: 0 + + function colWidth() { + return grid.width / grid.columns + } + + function rowWidth() { + return grid.height / grid.rows + } + + function prefWidth(item) { + return colWidth() * item.Layout.columnSpan + } + + function prefHeight(item) { + return rowWidth() * item.Layout.rowSpan + } + + Repeater { + model: twm + + delegate: DelegateChooser { + id: chooser + role: "type" + DelegateChoice { + roleValue: "int" + IntWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + DelegateChoice { + roleValue: "string" + TextWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "double" + DoubleWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "bool" + BoolWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "dial" + IntDialWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "doubleDial" + DoubleDialWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "color" + ColorWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "FMSInfo" + FMSInfo { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "Field2d" + Field2d { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "Command" + Command { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "String Chooser" + StringChooser { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "camera" + CameraView { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + + DelegateChoice { + roleValue: "enum" + EnumWidget { + Layout.row: model.row + Layout.column: model.column + Layout.rowSpan: model.rowSpan + Layout.columnSpan: model.colSpan + + Layout.margins: 8 + + Layout.preferredWidth: grid.prefWidth(this) + Layout.preferredHeight: grid.prefHeight(this) + } + } + } + } + + GridHandler { + id: rep + } + } +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt deleted file mode 100644 index d776c9cc..00000000 --- a/lib/CMakeLists.txt +++ /dev/null @@ -1,171 +0,0 @@ -cmake_minimum_required(VERSION 3.25) - -project(QFRCDashboardLib VERSION 1.0.0 LANGUAGES CXX) - -set(CMAKE_AUTOUIC_SEARCH_PATHS "ui/") - -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -if(UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC") -endif() - -option(BUILD_SHARED_LIBS "Build using shared libraries" ON) - -option(USE_SYSTEM_NTCORE "Use system wpilib installation" ON) -option(USE_SYSTEM_PROTOBUF "Use system protobuf installation" ON) - -if( ${USE_SYSTEM_NTCORE}) - find_dependency(ntcore) - if( NOT ${ntcore_FOUND} ) - set(USE_SYSTEM_NTCORE OFF) - endif() -endif() - -if( ${USE_SYSTEM_PROTOBUF}) - find_dependency(Protobuf) - if( NOT ${Protobuf_FOUND} ) - set(USE_SYSTEM_PROTOBUF OFF) - endif() -endif() - -find_dependency(QT NAMES Qt6 REQUIRED) -find_dependency(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets MultimediaWidgets Charts) -add_subdirectory(3rd_party EXCLUDE_FROM_ALL) - -set(PROJECT_SOURCES - src/Globals.cpp - src/MainWindow.cpp - src/Constants.cpp - - src/widgets/BaseWidget.cpp - src/widgets/DoubleDisplayWidget.cpp - src/widgets/TabWidget.cpp - src/widgets/BooleanDisplayWidget.cpp - src/widgets/StringDisplayWidget.cpp - src/widgets/TextWidget.cpp - src/widgets/BooleanCheckboxWidget.cpp - src/widgets/DoubleDialWidget.cpp - src/widgets/StringChooserWidget.cpp - src/widgets/CameraViewWidget.cpp - src/widgets/EnumWidget.cpp - src/widgets/IntegerDisplayWidget.cpp - src/widgets/IntegerDialWidget.cpp - src/widgets/FieldWidget.cpp - src/widgets/SendableFieldWidget.cpp - src/widgets/CommandWidget.cpp - src/widgets/GraphWidget.cpp - src/widgets/FMSInfoWidget.cpp - src/widgets/SwerveWidget.cpp - - src/misc/ShapedFrame.cpp - src/misc/BetterDial.cpp - src/misc/FieldImage.cpp - src/misc/ChartView.cpp - src/misc/GridLineWidget.cpp - src/misc/SwerveTrain.cpp - - src/stores/TopicStore.cpp - src/stores/TypeStore.cpp - src/stores/FilterStore.cpp - src/stores/CameraStore.cpp - - src/dialogs/CameraSelectionDialog.cpp - src/dialogs/NewWidgetTreeDialog.cpp - src/dialogs/NTSettingsDialog.cpp - src/dialogs/WidgetDialogGenerator.cpp - src/dialogs/TabMaxSizeDialog.cpp - src/dialogs/PreferencesDialog.cpp - - # ======================================= - - include/Globals.h - include/MainWindow.h - include/Constants.h - - include/widgets/BaseWidget.h - include/widgets/DoubleDisplayWidget.h - include/widgets/TabWidget.h - include/widgets/BooleanDisplayWidget.h - include/widgets/StringDisplayWidget.h - include/widgets/TextWidget.h - include/widgets/BooleanCheckboxWidget.h - include/widgets/DoubleDialWidget.h - include/widgets/StringChooserWidget.h - include/widgets/CameraViewWidget.h - include/widgets/EnumWidget.h - include/widgets/IntegerDisplayWidget.h - include/widgets/IntegerDialWidget.h - include/widgets/FieldWidget.h - include/widgets/SendableFieldWidget.h - include/widgets/CommandWidget.h - include/widgets/GraphWidget.h - include/widgets/FMSInfoWidget.h - include/widgets/SwerveWidget.h - - include/misc/ShapedFrame.h - include/misc/BetterDial.h - include/misc/FieldImage.h - include/misc/ChartView.h - include/misc/GridLineWidget.h - include/misc/SwerveTrain.h - - include/stores/TopicStore.h - include/stores/TypeStore.h - include/stores/FilterStore.h - include/stores/CameraStore.h - - include/dialogs/CameraSelectionDialog.h - include/dialogs/NewWidgetTreeDialog.h - include/dialogs/NTSettingsDialog.h - include/dialogs/WidgetDialogGenerator.h - include/dialogs/TabMaxSizeDialog.h - include/dialogs/PreferencesDialog.h - - # ================================== - ui/MainWindow.ui - ui/NTSettingsDialog.ui - ui/WidgetDialogGenerator.ui - ui/NewWidgetTreeDialog.ui - ui/CameraSelectionDialog.ui - ui/TabMaxSizeDialog.ui - ui/PreferencesDialog.ui -) - -if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) - qt_add_library(QFRCDashboardLib - STATIC - fields.qrc - theme/qrc/breeze.qrc - icons.qrc - ${PROJECT_SOURCES} - - ) -endif() - -add_subdirectory("buildconfig") - -target_link_libraries(QFRCDashboardLib PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::MultimediaWidgets Qt${QT_VERSION_MAJOR}::Charts ntcore BuildConfig) - -target_include_directories(QFRCDashboardLib PRIVATE "$") -target_include_directories(QFRCDashboardLib PRIVATE ui/) -target_include_directories(QFRCDashboardLib PRIVATE include/) - -set(_Dashboard_AUTOGEN "${CMAKE_CURRENT_BINARY_DIR}/QFRCDashboardLib_autogen" PARENT_SCOPE) - -include(GNUInstallDirs) -install(TARGETS QFRCDashboardLib - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - -install( - CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/qt.conf\" \" \")" - COMPONENT Runtime -) diff --git a/lib/icons.qrc b/lib/icons.qrc deleted file mode 100644 index 5a34d45c..00000000 --- a/lib/icons.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - icons/xmark.svg - icons/check.svg - - diff --git a/lib/icons/check.svg b/lib/icons/check.svg deleted file mode 100644 index b56f1101..00000000 --- a/lib/icons/check.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/icons/xmark.svg b/lib/icons/xmark.svg deleted file mode 100644 index 3d4317e5..00000000 --- a/lib/icons/xmark.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/lib/include/Globals.h b/lib/include/Globals.h deleted file mode 100644 index ce2b00e8..00000000 --- a/lib/include/Globals.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -// ENUMS // -enum class WidgetTypes { - None = 65535, - BooleanCheckbox = 0, - BooleanDisplay = 1, - StringDisplay = 2, - DoubleDisplay = 3, - DoubleDial = 4, - SendableChooser = 5, - CameraView = 6, - EnumWidget = 7, - IntegerDisplay = 8, - IntegerDial = 9, - SendableField = 10, // UNUSED BY SAVE/LOAD - Field = 11, - Command = 12, - Graph = 13, - FMSInfo = 14, - Swerve = 15 -}; - -enum class TopicTypes { - None = 65535, - Double = 0, - DoubleArray = 1, - String = 2, - Boolean = 3, - Int = 4, - StringArray = 9, - - SendableChooser = 5, - Field2d = 6, - Command = 7, - FMSInfo = 8 -}; - -// STRUCTS // -typedef struct { - bool teamNumber; - std::string server; - int port; - QString switchTopic; -} ServerData; - -typedef struct { - int row; - int col; - int rowSpan; - int colSpan; -} WidgetData; - -class TypeStore; - -// NAMESPACES // -namespace Globals { - -Q_NAMESPACE - -enum class FrameShape { - Circle, - Triangle, - Rectangle, - Hexagon -}; - -Q_ENUM_NS(FrameShape) - -extern nt::NetworkTableInstance inst; -extern ServerData server; - -extern TypeStore typeStore; - -extern QMap topicTypeDisplayNames; - -extern QStringList ntTopics; -extern WidgetData *defaultWidgetData; - -extern QMap shapeNameMap; - -typedef struct { - QString fileName; -} File; - -typedef struct GraphXAxis { - bool useTime; - QString topic; - - bool operator==(const struct GraphXAxis &other) const; -} GraphXAxis; - -class NumberTopic; -class DoubleArrayTopic; - -class Topic -{ -public: - QString Name; - TopicTypes Type; - - Topic(QString name = "", TopicTypes type = TopicTypes::None) : Name(name), Type(type) {} - - bool operator==(const Topic &other) const; - - void operator=(const NumberTopic &other); - void operator=(const DoubleArrayTopic &other); -}; - -class NumberTopic : public Topic -{ -public: - NumberTopic(QString name = "", TopicTypes type = TopicTypes::None) : Topic(name, type) {} - - void operator=(const Topic &other); -}; - -class DoubleArrayTopic : public Topic -{ -public: - DoubleArrayTopic(QString name = "") : Topic(name, TopicTypes::DoubleArray) {} - - void operator=(const Topic &other); -}; - -extern uint qHash(const Topic &topic); - -} - -Q_DECLARE_METATYPE(Globals::File) -Q_DECLARE_METATYPE(Globals::NumberTopic) -Q_DECLARE_METATYPE(Globals::GraphXAxis) - -namespace CustomMetaTypes { -static const int FrameShape = qMetaTypeId(); -static const int File = qMetaTypeId(); -static const int DATopic = qMetaTypeId(); -static const int NumberTopicList = qMetaTypeId>(); -static const int XAxis = qMetaTypeId(); -static const int NumberTopicColorMap = qMetaTypeId>(); -static const int DoubleArrayTopic = qMetaTypeId(); -} - -extern bool operator==(const WidgetData &a, const WidgetData &b); - -extern void setAppStyleSheet(QString styleSheet); diff --git a/lib/include/MainWindow.h b/lib/include/MainWindow.h deleted file mode 100644 index d211056d..00000000 --- a/lib/include/MainWindow.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include "widgets/BaseWidget.h" -#include "widgets/TabWidget.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "Globals.h" -#include "ui_MainWindow.h" - -class MainWindow : public QMainWindow, Ui::MainWindow -{ - Q_OBJECT -private: - QList m_tabs; - - QString m_filename{}; - int m_lastIdx = 0; - - void makeNewWidget(WidgetTypes type); -public: - MainWindow(); - virtual ~MainWindow(); - - TabWidget *currentTab(); - int currentTabIdx(); - - TabWidget *tabNamed(QString name); - void selectTab(TabWidget *tab); - - // File I/O - QJsonDocument saveObject(); - void loadObject(const QJsonDocument &doc); - -public slots: - // Internal Stuff - void forceUpdateTab(int idx); - void moveTab(int from, int to); - void setConnected(bool connected); - - // Preferences - void preferences(); - - // NT Settings - void ntSettingsPopup(); - void setNtSettings(ServerData data); - - // File Actions - void save(); - void saveAs(); - void openDialog(); - void open(QFile &file); - - void refreshRecentFiles(); - void addRecentFile(QFile &file); - - // Tab Actions - void newTab(); - void closeTab(); - void setMaxSize(); - void renameTab(); - - // New Widget - void newWidgetPopup(); - void beginNewWidgetDrag(BaseWidget *widget, WidgetData data); - void newCameraView(); - void newGraph(); - void newSwerve(); - - // Camera - void cameraServerPopup(); - - // About Menu - void about(); - void aboutQt(); - -signals: - void switchTopicChanged(); -}; diff --git a/lib/include/dialogs/CameraSelectionDialog.h b/lib/include/dialogs/CameraSelectionDialog.h deleted file mode 100644 index 24ebcbff..00000000 --- a/lib/include/dialogs/CameraSelectionDialog.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef CAMERASELECTIONDIALOG_H -#define CAMERASELECTIONDIALOG_H - -#include "stores/CameraStore.h" -#include -#include -#include -#include - -#include "ui_CameraSelectionDialog.h" - -class CameraSelectionDialog : public QDialog, Ui::CameraSelectionDialog -{ - Q_OBJECT -public: - CameraSelectionDialog(QWidget *parent = nullptr); - -public slots: - void emitCamera(); -signals: - void selectedCamera(Camera camera); -}; - -#endif // CAMERASELECTIONDIALOG_H diff --git a/lib/include/dialogs/NTSettingsDialog.h b/lib/include/dialogs/NTSettingsDialog.h deleted file mode 100644 index c141cf1d..00000000 --- a/lib/include/dialogs/NTSettingsDialog.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef NTSETTINGSDIALOG_H -#define NTSETTINGSDIALOG_H - -#include "Globals.h" -#include - -#include -#include -#include -#include -#include - -#include "ui_NTSettingsDialog.h" - -class NTSettingsDialog : public QDialog, Ui::NTSettingsDialog -{ - Q_OBJECT -public: - NTSettingsDialog(QWidget *parent = nullptr); - virtual ~NTSettingsDialog(); - -public slots: - void serializeData(); - void putTopic(); -signals: - void dataReady(ServerData data); -}; - -#endif // NTSETTINGSDIALOG_H diff --git a/lib/include/dialogs/NewWidgetTreeDialog.h b/lib/include/dialogs/NewWidgetTreeDialog.h deleted file mode 100644 index ec408566..00000000 --- a/lib/include/dialogs/NewWidgetTreeDialog.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef NEWWIDGETTREEDIALOG_H -#define NEWWIDGETTREEDIALOG_H - -#include - -#include -#include -#include -#include - -#include "Globals.h" -#include "widgets/BaseWidget.h" -#include "ui_NewWidgetTreeDialog.h" - -class NewWidgetTreeDialog : public QDialog, Ui::NewWidgetTreeDialog -{ - Q_OBJECT -private: - QMap m_itemTableMap; - - bool m_emitTopic = false; - - void createTreeIfNotExists(const Globals::Topic &topic); - - QString getParentPath(QTreeWidgetItem *item); - -public: - explicit NewWidgetTreeDialog(bool emitTopic = false, QWidget *parent = nullptr); - virtual ~NewWidgetTreeDialog(); - - void keyPressEvent(QKeyEvent *event); - void constructList(QList topics); - - static QList filterTopicTypes(QList list, QList acceptableTypes); - static QList filterNumberTypes(QList list); - static QList filterStringTypes(QList list); -signals: - void widgetReady(BaseWidget *widget, WidgetData data); - void topicReady(const Globals::Topic &topic); -}; - -#endif // NEWWIDGETTREEDIALOG_H diff --git a/lib/include/dialogs/PreferencesDialog.h b/lib/include/dialogs/PreferencesDialog.h deleted file mode 100644 index 7cf38a18..00000000 --- a/lib/include/dialogs/PreferencesDialog.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef PREFERENCESDIALOG_H -#define PREFERENCESDIALOG_H - -#include -#include -#include -#include - -#include "ui_PreferencesDialog.h" - -extern QMap StyleSheetMap; - -class PreferencesDialog : public QDialog, Ui::PreferencesDialog -{ - Q_OBJECT - -public: - PreferencesDialog(QWidget *parent); -public slots: - void emitData(); -signals: - void dataReady(QString styleSheet, bool loadRecent); -}; - -#endif // PREFERENCESDIALOG_H diff --git a/lib/include/dialogs/TabMaxSizeDialog.h b/lib/include/dialogs/TabMaxSizeDialog.h deleted file mode 100644 index 9ba86790..00000000 --- a/lib/include/dialogs/TabMaxSizeDialog.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TABMAXSIZEDIALOG_H -#define TABMAXSIZEDIALOG_H - -#include -#include -#include -#include - -#include "ui_TabMaxSizeDialog.h" - -class TabMaxSizeDialog : public QDialog, Ui::TabMaxSizeDialog -{ - Q_OBJECT -public: - TabMaxSizeDialog(QWidget *parent = nullptr, const QPoint &maxSize = QPoint()); - ~TabMaxSizeDialog(); - -public slots: - void emitData(); -signals: - void dataReady(QPoint point); -}; - -#endif // TABMAXSIZEDIALOG_H diff --git a/lib/include/dialogs/WidgetDialogGenerator.h b/lib/include/dialogs/WidgetDialogGenerator.h deleted file mode 100644 index 2f5aa4e8..00000000 --- a/lib/include/dialogs/WidgetDialogGenerator.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef WIDGETDIALOGGENERATOR_H -#define WIDGETDIALOGGENERATOR_H - -#include -#include -#include -#include -#include -#include - -#include "Globals.h" -#include "ui_WidgetDialogGenerator.h" - -typedef std::function Getter; -class BaseWidget; - -class WidgetDialogGenerator : public QDialog, Ui::WidgetDialogGenerator -{ - Q_OBJECT -private: - BaseWidget *m_widget; - bool m_isResize; - - QDialogButtonBox *m_buttonBox; - - WidgetData m_data; - - QMultiMap m_propertyGetterMap; - - QMetaProperty m_currentProperty; - BaseWidget *m_currentWidget; - - QPushButton *selectTopicButton(QList types); - QPushButton *numberTopicButton(); - QPushButton *doubleArrayTopicButton(); - QPushButton *selectColorButton(); - - std::tuple setupTable(const QStringList &headers); - -public: - WidgetDialogGenerator(BaseWidget *widget, QWidget *parent = nullptr, bool isResize = false, WidgetData data = WidgetData{0, 0, 1, 1}); - ~WidgetDialogGenerator(); - - void bindMetaProperty(const QMetaProperty property, Getter getter); - - QVariantMap serializeTable(QTableWidget *widget); - void serializeMap(QVariantMap map, QTableWidget *widget); - void serializeMap(QHash map, QTableWidget *widget); - - /** property functions **/ - QWidget *doubleProperty(QMetaProperty property); - QWidget *intProperty(QMetaProperty property); - - QWidget *colorProperty(QMetaProperty property); - - QWidget *mapProperty(QMetaProperty property); - - QWidget *bitmapProperty(QMetaProperty property); - QWidget *imageProperty(QMetaProperty property); - - QWidget *fileProperty(QMetaProperty property); - - QWidget *fontProperty(QMetaProperty property); - - QWidget *stringProperty(QMetaProperty property); - - QWidget *shapeProperty(QMetaProperty property); - - QWidget *topicListProperty(QMetaProperty property); - - QWidget *xAxisProperty(QMetaProperty property); - - QWidget *numberTopicColorMapProperty(QMetaProperty property); - - QWidget *doubleArrayTopicProperty(QMetaProperty property); -signals: - void widgetReady(BaseWidget *widget, WidgetData data); - void cancelled(BaseWidget *widget); - - void topicSelected(const Globals::Topic &topic, QWidget *receiver); - void colorSelected(const QColor &color); -}; - -bool operator<(const QMetaProperty a, const QMetaProperty b); - -#endif // WIDGETDIALOGGENERATOR_H diff --git a/lib/include/misc/BetterDial.h b/lib/include/misc/BetterDial.h deleted file mode 100644 index 0ee062d0..00000000 --- a/lib/include/misc/BetterDial.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef BETTERDIAL_H -#define BETTERDIAL_H - -#include - -class BetterDial : public QWidget -{ - Q_OBJECT -private: - int m_min = 0; - int m_max = 0; - - int m_radius = 0; - - int m_value = 0; - - double m_start = 0.; - - bool m_isDragging = false; -public: - BetterDial(QWidget *parent = nullptr); - - int min(); - void setMin(int min); - - int max(); - void setMax(int max); - - int value(); - void setValue(int value); - - double startingAngle(); - void setStartingAngle(double angle); - - bool isDragging(); - - void paintEvent(QPaintEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - - double valueFromPoint(QPointF point); - -signals: - void sliderMoved(int position); -}; - -#endif // BETTERDIAL_H diff --git a/lib/include/misc/ChartView.h b/lib/include/misc/ChartView.h deleted file mode 100644 index 15508649..00000000 --- a/lib/include/misc/ChartView.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CHARTVIEW_H -#define CHARTVIEW_H - -#include - -class ChartView : public QChartView -{ - Q_OBJECT -public: - ChartView(QChart *chart, QWidget *parent); - - void mouseReleaseEvent(QMouseEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); -}; - -#endif // CHARTVIEW_H diff --git a/lib/include/misc/FieldImage.h b/lib/include/misc/FieldImage.h deleted file mode 100644 index 1c34caf5..00000000 --- a/lib/include/misc/FieldImage.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef FIELDIMAGE_H -#define FIELDIMAGE_H - -#include "Globals.h" -#include - -class FieldImage : public QLabel -{ - Q_OBJECT -private: - double m_width = 0.5; - double m_length = 0.5; - - double m_x = 0.; - double m_y = 0.; - double m_theta = 0.; - - double m_imageWidth; - double m_imageHeight; - - Globals::File m_image{":/2024Field.png"}; - - QRect m_lastRect{}; - QPixmap m_pixmap{}; - bool m_imageChanged = false; -public: - FieldImage(QWidget *parent = nullptr); - - void setValue(std::span value); - void setRobotWidth(double width); - void setRobotLength(double length); - - void setImage(Globals::File image); - - void paintEvent(QPaintEvent *event) override; -}; - -#endif // FIELDIMAGE_H diff --git a/lib/include/misc/GridLineWidget.h b/lib/include/misc/GridLineWidget.h deleted file mode 100644 index 2ba90b05..00000000 --- a/lib/include/misc/GridLineWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef GRIDLINEWIDGET_H -#define GRIDLINEWIDGET_H - -#include -#include - -#include "Globals.h" - -class GridLineWidget : public QWidget -{ - Q_OBJECT -private: - QPoint m_size; - - WidgetData m_selection; - - bool m_hasSelection = false; - bool m_isValidSelection = true; - -public: - explicit GridLineWidget(QWidget *parent = nullptr); - - void setSize(QPoint size); - - WidgetData selection(); - bool hasSelection(); - bool isValidSelection(); - - void setSelection(const WidgetData &selectedIndex); - void setHasSelection(const bool &hasSelection); - void setValidSelection(const bool &isValidSelection); - - void paintEvent(QPaintEvent *event) override; - -signals: -}; - -#endif // GRIDLINEWIDGET_H diff --git a/lib/include/misc/ShapedFrame.h b/lib/include/misc/ShapedFrame.h deleted file mode 100644 index 70f576c6..00000000 --- a/lib/include/misc/ShapedFrame.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef SHAPEDFRAME_H -#define SHAPEDFRAME_H - -#include "Globals.h" - -#include -#include - -class ShapedFrame : public QFrame -{ - Q_OBJECT -public: - - ShapedFrame(Globals::FrameShape shape = Globals::FrameShape::Circle, QWidget *parent = nullptr); - virtual ~ShapedFrame(); - - Globals::FrameShape shape(); - void setShape(Globals::FrameShape shape); - - QColor color(); - void setColor(QColor color); - void setColor(QString color); - - void paintEvent(QPaintEvent *event) override; -private: - Globals::FrameShape m_shape; - - QColor m_color; -}; - -#endif // SHAPEDFRAME_H diff --git a/lib/include/misc/SwerveTrain.h b/lib/include/misc/SwerveTrain.h deleted file mode 100644 index 3e0188eb..00000000 --- a/lib/include/misc/SwerveTrain.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SWERVETRAIN_H -#define SWERVETRAIN_H - -#include - -class SwerveTrain : public QWidget -{ - Q_OBJECT -private: - QList m_locations = { - 1, 1, - -1, 1, - 1, -1, - -1, -1 - }; - - QList m_states = { - 5, 0, - 5, 0, - 5, 0, - 5, 0 - }; - - void paintEvent(QPaintEvent *event) override; - -public: - SwerveTrain(QWidget *parent = nullptr); - - void setLocations(QList locations); - void setStates(QList states); -}; - -#endif // SWERVETRAIN_H diff --git a/lib/include/stores/CameraStore.h b/lib/include/stores/CameraStore.h deleted file mode 100644 index 5ad3bb59..00000000 --- a/lib/include/stores/CameraStore.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CAMERASTORE_H -#define CAMERASTORE_H - -#include -#include -#include "networktables/NetworkTable.h" - -class Camera -{ -private: - QUrl m_urls{}; - QString m_name{}; - QString m_source{}; -public: - Camera() = default; - - static Camera fromTable(std::shared_ptr table); - - QList Urls; - QString Name; - QString Source; -}; - -class CameraStore : public QObject -{ - Q_OBJECT -private: -public: - CameraStore(); - - static QList Cameras; - static void filterCameras(); - static Camera getCameraFromName(QString name); - -signals: -}; - -#endif // CAMERASTORE_H diff --git a/lib/include/stores/FilterStore.h b/lib/include/stores/FilterStore.h deleted file mode 100644 index 2e5d0fe6..00000000 --- a/lib/include/stores/FilterStore.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef FILTERSTORE_H -#define FILTERSTORE_H - -#include -#include - -#include "Globals.h" - -class FilterStore : public QObject -{ - Q_OBJECT -private: - static QMap m_sendableTypeMap; - static QMap m_ntTypeMap; - -public: - FilterStore(); - - static void registerSendable(std::string typeString, TopicTypes topicType); - static void registerNTType(nt::NetworkTableType ntType, TopicTypes topicType, const QString &displayName); - - static std::optional sendableTypeForTypeString(std::string typeString); - static std::optional topicTypeForNTType(nt::NetworkTableType ntType); - - static QList FilteredTopics; - static QList UnfilteredTopics; - - static Globals::Topic topicFromName(const QString &topicName, const QList &topics); - - static QStringList topicNames(QList list); - static QStringList topicNames(QList list); - -public slots: - static void filterTopics(); - static void sortTopic(QString topic); - -signals: - -}; - -#endif // FILTERSTORE_H diff --git a/lib/include/stores/TopicStore.h b/lib/include/stores/TopicStore.h deleted file mode 100644 index ac47a6bf..00000000 --- a/lib/include/stores/TopicStore.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef TopicStore_H -#define TopicStore_H - -#include "networktables/NetworkTableEntry.h" -#include "Globals.h" - -#include -#include -#include - -class BaseWidget; - -struct Listener { - std::string topic; - QString label; - nt::NetworkTableEntry entry; - BaseWidget *subscriber; - NT_Listener listenerHandle; - nt::ListenerCallback callback; - NT_Type desiredType; - bool isNull; - - bool operator==(const Listener &other) const; -}; - -class TopicStore : public QObject -{ - Q_OBJECT -private: - static bool hasEntry(std::string topic); - static bool hasEntry(QString topic); - - static bool widgetSubscribed(std::string topic, BaseWidget *subscriber); - static bool widgetSubscribed(QString topic, BaseWidget *subscriber); - - static Listener getEntry(std::string topic, BaseWidget *subscriber); - static Listener getEntry(QString topic, BaseWidget *subscriber); -public: - static QList Listeners; - static QHash topicEntryMap; - - TopicStore(); - - static nt::NetworkTableEntry subscribe(std::string ntTopic, BaseWidget *subscriber, NT_Type desiredType = NT_UNASSIGNED, QString label = "", bool writeOnly = false); - - static void unsubscribe(std::string ntTopic, BaseWidget *subscriber); - static void unsubscribe(QString ntTopic, BaseWidget *subscriber); - static void unsubscribe(nt::NetworkTableEntry entry, BaseWidget *subscriber); - - static double getDoubleFromEntry(nt::NetworkTableEntry entry); - - static void updateTopic(std::string topic, BaseWidget *subscriber, QString label); -}; - -#endif // TopicStore_H diff --git a/lib/include/stores/TypeStore.h b/lib/include/stores/TypeStore.h deleted file mode 100644 index b6f8372c..00000000 --- a/lib/include/stores/TypeStore.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TYPESTORE_H -#define TYPESTORE_H - -#include "Globals.h" - -#include -#include - -class BaseWidget; - -class TypeStore : public QObject -{ - Q_OBJECT -private: - QMultiMap m_typeWidgetMap{}; - QMap m_widgetNameMap{}; -public: - explicit TypeStore(); - - void registerType(TopicTypes topicType, WidgetTypes widgetType, QString displayName); - QList generateActionsForTopic(Globals::Topic topic); - QMenu *generateMenuForTopic(Globals::Topic topic); - - QString widgetDisplayName(WidgetTypes type); - -public slots: - void emitWidget(BaseWidget *widget, WidgetData data); -signals: - void widgetReady(BaseWidget *widget, WidgetData data); -}; - -#endif // TYPESTORE_H diff --git a/lib/include/widgets/BaseWidget.h b/lib/include/widgets/BaseWidget.h deleted file mode 100644 index dcb0a785..00000000 --- a/lib/include/widgets/BaseWidget.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include "networktables/NetworkTableEntry.h" -#include "Globals.h" - -#include -#include -#include -#include -#include - -enum ResizeFlags { - NONE = 0, - TOP = 1 << 0, - BOTTOM = 1 << 1, - RIGHT = 1 << 2, - LEFT = 1 << 3 -}; - -Q_DECLARE_FLAGS(ResizeDirection, ResizeFlags) -Q_DECLARE_OPERATORS_FOR_FLAGS(ResizeDirection) - -class BaseWidget : public QFrame -{ - Q_OBJECT - - Q_PROPERTY(QString title READ title WRITE setTitle) - Q_PROPERTY(QFont titleFont READ titleFont WRITE setTitleFont) -protected: - WidgetTypes m_type; - QGridLayout *m_layout; - - QLineEdit *m_title; - bool m_ready = false; - - QString m_topic; - - ResizeDirection m_resize = NONE; - - bool m_sendable = false; - bool m_connected = true; - - nt::NetworkTableEntry m_entry; -public: - explicit BaseWidget(const WidgetTypes &type = WidgetTypes::StringDisplay, const QString &title = "", const QString &topic = "", const bool sendable = false); - ~BaseWidget(); - - bool ready(); - void setReady(bool ready); - - QFont titleFont(); - void setTitleFont(const QFont &font); - - QString title(); - void setTitle(const QString &title); - - QString topic(); - virtual void setTopic(const QString &topic); - - ResizeDirection resizing(); - void setResizing(ResizeDirection direction); - - virtual QMenu *constructContextMenu(WidgetData data); - - virtual void setValue(const nt::Value &value, QString label = "", bool force = false); - virtual void forceUpdate(); - virtual void setConnected(bool connected = true); - - void paintEvent(QPaintEvent *event); - - QJsonObject saveObject(); - WidgetData fromJson(QJsonObject obj); - - static BaseWidget *defaultWidgetFromTopic(QString ntTopic, WidgetTypes type); - - inline static WidgetTypes WidgetType = WidgetTypes::None; - inline static TopicTypes TopicType = TopicTypes::None; - inline static QString SendableName = ""; - inline static QString DisplayName = ""; - -signals: - void reconfigRequested(BaseWidget *widget, WidgetData data); - void deleteRequested(); - void isReady(); -private: - QVariant readDoubleProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readIntProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readBoolProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readColorProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readMapProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readListProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readFileProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readFontProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readStringProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readShapeProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readTopicListProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readXAxisProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readNumberTopicColorMapProperty(const QMetaProperty &property, const QJsonValue &value); - QVariant readDoubleArrayTopicProperty(const QMetaProperty &property, const QJsonValue &value); - - QJsonValue writeDoubleProperty(const QMetaProperty &property); - QJsonValue writeIntProperty(const QMetaProperty &property); - QJsonValue writeBoolProperty(const QMetaProperty &property); - QJsonValue writeColorProperty(const QMetaProperty &property); - QJsonValue writeMapProperty(const QMetaProperty &property); - QJsonValue writeListProperty(const QMetaProperty &property); - QJsonValue writeFileProperty(const QMetaProperty &property); - QJsonValue writeFontProperty(const QMetaProperty &property); - QJsonValue writeStringProperty(const QMetaProperty &property); - QJsonValue writeShapeProperty(const QMetaProperty &property); - QJsonValue writeTopicListProperty(const QMetaProperty &property); - QJsonValue writeXAxisProperty(const QMetaProperty &property); - QJsonValue writeNumberTopicColorMapProperty(const QMetaProperty &property); - QJsonValue writeDoubleArrayTopicProperty(const QMetaProperty &property); - - void mouseMoveEvent(QMouseEvent *event); - void mousePressEvent(QMouseEvent *event); -}; diff --git a/lib/include/widgets/BooleanCheckboxWidget.h b/lib/include/widgets/BooleanCheckboxWidget.h deleted file mode 100644 index 7a3dc14b..00000000 --- a/lib/include/widgets/BooleanCheckboxWidget.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef BOOLEANCHECKBOXWIDGET_H -#define BOOLEANCHECKBOXWIDGET_H - -#include -#include "widgets/BaseWidget.h" - -class BooleanCheckboxWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(bool value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) - Q_PROPERTY(int Checkbox_Size READ checkboxSize WRITE setCheckboxSize REQUIRED) -protected: - bool m_value = false; - - int m_checkboxSize = 30; - - QCheckBox *m_checkbox; -public: - BooleanCheckboxWidget(const QString &topic = "", const bool &defaultValue = false, const QString &title = ""); - ~BooleanCheckboxWidget(); - - int checkboxSize(); - void setCheckboxSize(int size); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::BooleanCheckbox; - inline static TopicTypes TopicType = TopicTypes::Boolean; - inline static QString SendableName = ""; - inline static QString DisplayName = "Checkbox"; -}; - -#endif // BOOLEANCHECKBOXWIDGET_H diff --git a/lib/include/widgets/BooleanDisplayWidget.h b/lib/include/widgets/BooleanDisplayWidget.h deleted file mode 100644 index 5c3ae4d3..00000000 --- a/lib/include/widgets/BooleanDisplayWidget.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "widgets/BaseWidget.h" -#include "misc/ShapedFrame.h" - -class BooleanDisplayWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(bool value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) - Q_PROPERTY(QColor True_Color READ trueColor WRITE setTrueColor REQUIRED) - Q_PROPERTY(QColor False_Color READ falseColor WRITE setFalseColor REQUIRED) - Q_PROPERTY(Globals::FrameShape Shape READ shape WRITE setShape REQUIRED) -protected: - bool m_value = false; - - ShapedFrame *m_colorWidget; - - QColor m_trueColor = Qt::green; - QColor m_falseColor = Qt::red; - Globals::FrameShape m_shape = Globals::FrameShape::Rectangle; -public: - BooleanDisplayWidget(const QString &topic = "", const bool &defaultValue = false, const QString &title = ""); - ~BooleanDisplayWidget(); - - QColor trueColor(); - void setTrueColor(const QColor &color); - - QColor falseColor(); - void setFalseColor(const QColor &color); - - Globals::FrameShape shape(); - void setShape(Globals::FrameShape shape); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::BooleanDisplay; - inline static TopicTypes TopicType = TopicTypes::Boolean; - inline static QString SendableName = ""; - inline static QString DisplayName = "Boolean Display"; -}; diff --git a/lib/include/widgets/CameraViewWidget.h b/lib/include/widgets/CameraViewWidget.h deleted file mode 100644 index 6111fdff..00000000 --- a/lib/include/widgets/CameraViewWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CAMERAVIEWWIDGET_H -#define CAMERAVIEWWIDGET_H - -#include -#include -#include "widgets/BaseWidget.h" - -class CameraViewWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(QUrl value READ url WRITE setUrl MEMBER m_url) - Q_PROPERTY(QUrl URL READ url WRITE setUrl MEMBER m_url REQUIRED) -protected: - QMediaPlayer *m_player; - QVideoWidget *m_videoWidget; - - QUrl m_url = QUrl(); -public: - // blank param added to ensure meta type safety - CameraViewWidget(const QString &title = "", const QUrl &url = QUrl()); - ~CameraViewWidget(); - - QUrl url(); - void setUrl(const QUrl &url); - - void forceUpdate() override; - void setConnected(bool connected = true) override; - - QMenu *constructContextMenu(WidgetData data) override; - - inline static WidgetTypes WidgetType = WidgetTypes::CameraView; - inline static TopicTypes TopicType = TopicTypes::None; - inline static QString SendableName = ""; - inline static QString DisplayName = "Camera View"; -}; - -#endif // CAMERAVIEWWIDGET_H diff --git a/lib/include/widgets/CommandWidget.h b/lib/include/widgets/CommandWidget.h deleted file mode 100644 index 6f6c00ee..00000000 --- a/lib/include/widgets/CommandWidget.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CommandWidget_H -#define CommandWidget_H - -#include -#include "widgets/BaseWidget.h" - -class CommandWidget : public BaseWidget -{ - Q_OBJECT -protected: - QPushButton *m_button; - - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) - - nt::NetworkTableEntry m_name; - nt::NetworkTableEntry m_running; -public: - CommandWidget(const QString &topic = "", const QString &title = ""); - ~CommandWidget(); - - void setTopic(const QString &topic) override; - - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::Command; - inline static TopicTypes TopicType = TopicTypes::Command; - inline static QString SendableName = "Command"; - inline static QString DisplayName = "Command"; -}; - -#endif // CommandWidget_H diff --git a/lib/include/widgets/DoubleDialWidget.h b/lib/include/widgets/DoubleDialWidget.h deleted file mode 100644 index e39780f0..00000000 --- a/lib/include/widgets/DoubleDialWidget.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef DOUBLEDIALWIDGET_H -#define DOUBLEDIALWIDGET_H - -#include "widgets/DoubleDisplayWidget.h" -#include "misc/BetterDial.h" - -#include - -class BaseWidget; - -/** - * @brief Display a double on a dial. - * - * Due to limitations within QDial, this only supports up to two decimal places. - */ -class DoubleDialWidget : public DoubleDisplayWidget -{ - Q_OBJECT - - Q_PROPERTY(double value MEMBER m_value) - Q_PROPERTY(double Maximum READ max WRITE setMax REQUIRED) - Q_PROPERTY(double Minimum READ min WRITE setMin REQUIRED) - Q_PROPERTY(double Starting_Angle READ startingAngle WRITE setStartingAngle REQUIRED) -protected: - BetterDial *m_dial; - - double m_min = 0; - double m_max = 360.; - - int m_fakeValue = 0; - - double m_startingAngle = 180.; - - void keyPressEvent(QKeyEvent *event) override; -public: - DoubleDialWidget(const QString &topic = "", const double &defaultValue = 0., const QString &title = ""); - ~DoubleDialWidget(); - - double min(); - void setMin(double min); - - double max(); - void setMax(double max); - - double startingAngle(); - // input degrees - void setStartingAngle(double angle); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::DoubleDial; - inline static TopicTypes TopicType = TopicTypes::Double; - inline static QString SendableName = ""; - inline static QString DisplayName = "Dial"; -}; - -#endif // DOUBLEDIALWIDGET_H diff --git a/lib/include/widgets/DoubleDisplayWidget.h b/lib/include/widgets/DoubleDisplayWidget.h deleted file mode 100644 index 7805b5ad..00000000 --- a/lib/include/widgets/DoubleDisplayWidget.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "TextWidget.h" - -class BaseWidget; - -class DoubleDisplayWidget : public TextWidget -{ - Q_OBJECT - - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) - Q_PROPERTY(double value MEMBER m_value) -protected: - double m_value = 0.; - - QHash m_map{}; - - void keyPressEvent(QKeyEvent *event) override; -public: - DoubleDisplayWidget(const QString &topic = "", const double &defaultValue = 0., const QString &title = "", const bool &ready = true); - ~DoubleDisplayWidget(); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::DoubleDisplay; - inline static TopicTypes TopicType = TopicTypes::Double; - inline static QString SendableName = ""; - inline static QString DisplayName = "Double Display"; -}; diff --git a/lib/include/widgets/EnumWidget.h b/lib/include/widgets/EnumWidget.h deleted file mode 100644 index 4c2c34c6..00000000 --- a/lib/include/widgets/EnumWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "widgets/BaseWidget.h" -#include "misc/ShapedFrame.h" - -class EnumWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(QString value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) - Q_PROPERTY(QVariantMap Colors READ colors WRITE setColors REQUIRED) - Q_PROPERTY(Globals::FrameShape Shape READ shape WRITE setShape REQUIRED) -protected: - QString m_value = ""; - - ShapedFrame *m_colorWidget; - - QVariantMap m_colors{}; - Globals::FrameShape m_shape = Globals::FrameShape::Rectangle; -public: - EnumWidget(const QString &topic = "", const QString &defaultValue = "", const QString &title = ""); - ~EnumWidget(); - - QVariantMap colors(); - void setColors(QVariantMap colors); - - Globals::FrameShape shape(); - void setShape(Globals::FrameShape shape); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::EnumWidget; - inline static TopicTypes TopicType = TopicTypes::String; - inline static QString SendableName = ""; - inline static QString DisplayName = "Enum"; -}; diff --git a/lib/include/widgets/FMSInfoWidget.h b/lib/include/widgets/FMSInfoWidget.h deleted file mode 100644 index fdc76e73..00000000 --- a/lib/include/widgets/FMSInfoWidget.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef FMSINFOWIDGET_H -#define FMSINFOWIDGET_H - -#include "widgets/BaseWidget.h" - -enum class MatchType { - None = 0, - Practice = 1, - Qualification = 2, - Elimination = 3 -}; - -enum class AllianceStation { - Invalid = 0, - Red1 = 1, - Red2 = 2, - Red3 = 3, - - Blue1 = 4, - Blue2 = 5, - Blue3 = 6 -}; - -enum class ControlWord { - Invalid = 0x0, - Enabled = 0x1, - Auto = 0x2, - Test = 0x4, - EStop = 0x8, - FMSAttached = 0x10, - DSAttached = 0x20 -}; - -Q_DECLARE_FLAGS(ControlFlags, ControlWord) -Q_DECLARE_OPERATORS_FOR_FLAGS(ControlFlags) - -extern QMap MatchTypeNames; -extern QMap StationNames; - -class FMSInfoWidget : public BaseWidget -{ - Q_OBJECT - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) -private: - int m_number = 0; - MatchType m_type = MatchType::None; - ControlWord m_word = ControlWord::Invalid; - AllianceStation m_station = AllianceStation::Invalid; - QString m_eventString = ""; - QString m_gsm = ""; - - QLabel *m_matchLabel; - QLabel *m_stationLabel; - QLabel *m_eventLabel; - QLabel *m_gsmLabel; - - QLabel *m_dsIconLabel; - QLabel *m_dsLabel; - QLabel *m_fmsIconLabel; - QLabel *m_fmsLabel; - - QLabel *m_controlLabel; - - nt::NetworkTableEntry m_event; - nt::NetworkTableEntry m_controlWord; - nt::NetworkTableEntry m_gameSpecificMessage; - nt::NetworkTableEntry m_redAlliance; - nt::NetworkTableEntry m_matchNumber; - nt::NetworkTableEntry m_matchType; - nt::NetworkTableEntry m_allianceStation; -public: - FMSInfoWidget(const QString &table = "", const QString &title = ""); - virtual ~FMSInfoWidget(); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::FMSInfo; - inline static TopicTypes TopicType = TopicTypes::FMSInfo; - inline static QString SendableName = "FMSInfo"; - inline static QString DisplayName = "FMSInfo"; -}; - -#endif // FMSINFOWIDGET_H diff --git a/lib/include/widgets/FieldWidget.h b/lib/include/widgets/FieldWidget.h deleted file mode 100644 index 6771f564..00000000 --- a/lib/include/widgets/FieldWidget.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "widgets/BaseWidget.h" - -#include "misc/FieldImage.h" - -class FieldWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(QVariantList value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) - Q_PROPERTY(Globals::File Image READ image WRITE setImage REQUIRED) - Q_PROPERTY(double Robot_Width READ robotWidth WRITE setRobotWidth REQUIRED) - Q_PROPERTY(double Robot_Length READ robotLength WRITE setRobotLength REQUIRED) - // TODO: possibly different robot shapes? eh not worth it. -protected: - QVariantList m_value{}; - - FieldImage *m_imageLabel; - Globals::File m_image{":/2024Field.png"}; - - double m_width = 0.5; - double m_length = 0.5; -public: - FieldWidget(const QString &topic = "", QVariantList defaultValue = QVariantList{}, const QString &title = ""); - ~FieldWidget(); - - double robotWidth(); - void setRobotWidth(double width); - - double robotLength(); - void setRobotLength(double length); - - Globals::File &image(); - void setImage(Globals::File image); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - void resizeEvent(QResizeEvent *event) override; - - inline static WidgetTypes WidgetType = WidgetTypes::Field; - inline static TopicTypes TopicType = TopicTypes::DoubleArray; - inline static QString SendableName = ""; - inline static QString DisplayName = "Field2d"; -}; - diff --git a/lib/include/widgets/GraphWidget.h b/lib/include/widgets/GraphWidget.h deleted file mode 100644 index 5dbebc1f..00000000 --- a/lib/include/widgets/GraphWidget.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef GRAPHWIDGET_H -#define GRAPHWIDGET_H - -#include "widgets/BaseWidget.h" - -#include -#include -#include -#include -#include -#include - -#include - -class ChartView; - -class GraphWidget : public BaseWidget -{ - Q_OBJECT - Q_PROPERTY(QHash Topics READ topics WRITE setTopics REQUIRED) - Q_PROPERTY(double Update_Frequency_Seconds READ updateFrequency WRITE setUpdateFrequency REQUIRED) - Q_PROPERTY(double Time_Scale_Seconds READ maxTimeScale WRITE setMaxTimeScale REQUIRED) - Q_PROPERTY(double Max_Y_Value READ maxYValue WRITE setMaxYValue REQUIRED) - Q_PROPERTY(double Min_Y_Value READ minYValue WRITE setMinYValue REQUIRED) - Q_PROPERTY(Globals::GraphXAxis X_Axis_Topic READ xAxisData WRITE setXAxisData REQUIRED) -private: - QHash m_topics{}; - - QHash m_entryMap; - QHash m_seriesMap; - - QTimer *m_timer; - QElapsedTimer m_elapsed; - ChartView *m_chart; - - QValueAxis *m_xAxis; - QValueAxis *m_yAxis; - - double m_maxTimeScale = 30.; - double m_updateFrequency = 1.; - - double m_maxYValue = 10.; - double m_minYValue = -10.; - - double m_maxXValue = -10.; - double m_minXValue = 10.; - - Globals::GraphXAxis m_xAxisData{true, ""}; - - nt::NetworkTableEntry m_xAxisEntry; - - void mouseReleaseEvent(QMouseEvent *event) override; - -public: - GraphWidget(const QString &topic = "", const QString &title = ""); - - QHash topics(); - void setTopics(QHash topics); - - double maxTimeScale(); - void setMaxTimeScale(double maxTimeScale); - - double updateFrequency(); - void setUpdateFrequency(double frequency); - - double maxYValue(); - void setMaxYValue(double max); - - double minYValue(); - void setMinYValue(double min); - - Globals::GraphXAxis xAxisData(); - void setXAxisData(const Globals::GraphXAxis &axis); - - double getCurrentXAxis(); - - QMenu *constructContextMenu(WidgetData data) override; - - inline static WidgetTypes WidgetType = WidgetTypes::Graph; - inline static TopicTypes TopicType = TopicTypes::None; - inline static QString SendableName = ""; - inline static QString DisplayName = "Graph"; - -public slots: - void updateGraph(); - - void exportCSVPopup(); - void exportCSV(const QString &fileName); -}; - -#endif // GRAPHWIDGET_H diff --git a/lib/include/widgets/IntegerDialWidget.h b/lib/include/widgets/IntegerDialWidget.h deleted file mode 100644 index 410825bb..00000000 --- a/lib/include/widgets/IntegerDialWidget.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef IntegerDialWIDGET_H -#define IntegerDialWIDGET_H - -#include "widgets/IntegerDisplayWidget.h" -#include "misc/BetterDial.h" - -class BaseWidget; - -class IntegerDialWidget : public IntegerDisplayWidget -{ - Q_OBJECT - - Q_PROPERTY(int value MEMBER m_value) - Q_PROPERTY(int Maximum READ max WRITE setMax REQUIRED) - Q_PROPERTY(int Minimum READ min WRITE setMin REQUIRED) - Q_PROPERTY(double Starting_Angle READ startingAngle WRITE setStartingAngle REQUIRED) -protected: - BetterDial *m_dial; - - int m_min = 0; - int m_max = 1000.; - - double m_startingAngle = 180.; - - void keyPressEvent(QKeyEvent *event) override; -public: - IntegerDialWidget(const QString &topic = "", const int &defaultValue = 0, const QString &title = ""); - ~IntegerDialWidget(); - - int min(); - void setMin(int min); - - int max(); - void setMax(int max); - - double startingAngle(); - // input degrees - void setStartingAngle(double angle); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::IntegerDial; - inline static TopicTypes TopicType = TopicTypes::Int; - inline static QString SendableName = ""; - inline static QString DisplayName = "Dial"; -}; - -#endif // IntegerDialWIDGET_H diff --git a/lib/include/widgets/IntegerDisplayWidget.h b/lib/include/widgets/IntegerDisplayWidget.h deleted file mode 100644 index f75486f7..00000000 --- a/lib/include/widgets/IntegerDisplayWidget.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "TextWidget.h" - -class BaseWidget; - -class IntegerDisplayWidget : public TextWidget -{ - Q_OBJECT - - Q_PROPERTY(int value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) -protected: - int m_value = 0; - - void keyPressEvent(QKeyEvent *event) override; -public: - IntegerDisplayWidget(const QString &topic = "", const int &defaultValue = 0, const QString &title = "", const bool &ready = true); - ~IntegerDisplayWidget(); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::IntegerDisplay; - inline static TopicTypes TopicType = TopicTypes::Int; - inline static QString SendableName = ""; - inline static QString DisplayName = "Int Display"; -}; diff --git a/lib/include/widgets/SendableFieldWidget.h b/lib/include/widgets/SendableFieldWidget.h deleted file mode 100644 index d2808641..00000000 --- a/lib/include/widgets/SendableFieldWidget.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SENDABLEFIELDWIDGET_H -#define SENDABLEFIELDWIDGET_H - -#include "widgets/FieldWidget.h" - -class SendableFieldWidget : public FieldWidget -{ -public: - SendableFieldWidget(QString topic = "") : FieldWidget(topic + "/Robot") {} - ~SendableFieldWidget(); - - inline static WidgetTypes WidgetType = WidgetTypes::SendableField; - inline static TopicTypes TopicType = TopicTypes::Field2d; - inline static QString SendableName = "Field2d"; - inline static QString DisplayName = "Field2d"; -}; - -#endif // SENDABLEFIELDWIDGET_H diff --git a/lib/include/widgets/StringChooserWidget.h b/lib/include/widgets/StringChooserWidget.h deleted file mode 100644 index f2c2f905..00000000 --- a/lib/include/widgets/StringChooserWidget.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef STRINGCHOOSERWIDGET_H -#define STRINGCHOOSERWIDGET_H - -#include "widgets/BaseWidget.h" - -#include - -class BaseWidget; - -class StringChooserWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(QString value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) -protected: - QString m_value = ""; - - nt::NetworkTableEntry m_active; - nt::NetworkTableEntry m_default; - nt::NetworkTableEntry m_choices; - nt::NetworkTableEntry m_selected; - - QComboBox *m_chooser; - - qsizetype m_flashCounter = 0; - - bool m_readyToUpdate = true; - - QString m_lastSelected = ""; -public: - StringChooserWidget(const QString &topic = "", const QString &defaultValue = "", const QString &title = ""); - ~StringChooserWidget(); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - void setConnected(bool connected = true) override; - - inline static WidgetTypes WidgetType = WidgetTypes::SendableChooser; - inline static TopicTypes TopicType = TopicTypes::SendableChooser; - inline static QString SendableName = "String Chooser"; - inline static QString DisplayName = "Sendable Chooser"; - -public slots: - void updateSelected(const QString text); -}; - -#endif // STRINGCHOOSERWIDGET_H diff --git a/lib/include/widgets/StringDisplayWidget.h b/lib/include/widgets/StringDisplayWidget.h deleted file mode 100644 index 5ac7a1fa..00000000 --- a/lib/include/widgets/StringDisplayWidget.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "TextWidget.h" - -class StringDisplayWidget : public TextWidget -{ - Q_OBJECT - - Q_PROPERTY(QString value MEMBER m_value) - Q_PROPERTY(QString Topic READ topic WRITE setTopic REQUIRED) -protected: - QString m_value = ""; - - void keyPressEvent(QKeyEvent *event) override; -public: - StringDisplayWidget(const QString &topic = "", const QString &defaultValue = "", const QString &title = ""); - ~StringDisplayWidget(); - - void setTopic(const QString &topic) override; - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - inline static WidgetTypes WidgetType = WidgetTypes::StringDisplay; - inline static TopicTypes TopicType = TopicTypes::String; - inline static QString SendableName = ""; - inline static QString DisplayName = "String Display"; -}; diff --git a/lib/include/widgets/SwerveWidget.h b/lib/include/widgets/SwerveWidget.h deleted file mode 100644 index 5bcbb1d8..00000000 --- a/lib/include/widgets/SwerveWidget.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SWERVEWIDGET_H -#define SWERVEWIDGET_H - -#include "widgets/BaseWidget.h" -#include "misc/SwerveTrain.h" - -class SwerveWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(Globals::DoubleArrayTopic Location_Topic READ locationTopic WRITE setLocationTopic REQUIRED) - Q_PROPERTY(Globals::DoubleArrayTopic States_Topic READ statesTopic WRITE setStatesTopic REQUIRED) -private: - nt::NetworkTableEntry m_locEntry; - nt::NetworkTableEntry m_stateEntry; - - QString m_locTopic = ""; - QString m_stateTopic = ""; - - SwerveTrain *m_train; -public: - SwerveWidget(const QString &topic = "", const QString &title = ""); - virtual ~SwerveWidget(); - - void setValue(const nt::Value &value, QString label = "", bool force = false) override; - - void setLocationTopic(const Globals::DoubleArrayTopic &topic); - Globals::DoubleArrayTopic locationTopic(); - - void setStatesTopic(const Globals::DoubleArrayTopic &topic); - Globals::DoubleArrayTopic statesTopic(); - - inline static WidgetTypes WidgetType = WidgetTypes::Swerve; - inline static TopicTypes TopicType = TopicTypes::None; - inline static QString SendableName = ""; - inline static QString DisplayName = "Swerve"; -}; - -#endif // SWERVEWIDGET_H diff --git a/lib/include/widgets/TabWidget.h b/lib/include/widgets/TabWidget.h deleted file mode 100644 index c6e17da1..00000000 --- a/lib/include/widgets/TabWidget.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "Globals.h" -#include "widgets/BaseWidget.h" -#include "misc/GridLineWidget.h" - -class TabWidget : public QWidget -{ - Q_OBJECT -private: - QGridLayout *m_layout; - - GridLineWidget *m_gridLine; - - QPoint m_maxSize; - WidgetData m_selectedIndex; - - QString m_name; - - QList m_widgets; - - // drag and drop - - // Dragging - QPoint m_dragStart; - QPoint m_dragOffset; - BaseWidget *m_draggedWidget = nullptr; - WidgetData m_draggedWidgetData; - bool m_dragging = false; - - void dragMove(QPoint point); - void dragRelease(QPoint point); - - // Resizing - ResizeDirection m_currentResize; - QRect m_initialSize; - bool m_resizing = false; - - void resizeStart(QPoint point); - void resizeMove(QPoint point); - void resizeRelease(QPoint point); - - // Events - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - -public: - TabWidget(const QPoint &maxSize, QWidget *parent = nullptr); - virtual ~TabWidget(); - - QList widgets(); - - bool widgetAtPoint(WidgetData data); - - WidgetData widgetData(BaseWidget *widget); - - QGridLayout *layout(); - - QString name() const; - void setName(const QString &newName); - - QPoint maxSize(); - void setMaxSize(const QPoint &maxSize); - - QJsonObject saveObject(); - void loadObject(const QJsonObject &object); - - // drag and drop epic edition - void setDragData(BaseWidget *widget, WidgetData data); - void dragStart(QPoint point, QPoint offset); - - // awesome - bool hasWidget(BaseWidget *widget); - void cancelDrags(); - -public slots: - void addWidget(BaseWidget *widget, WidgetData data); - void deleteWidget(BaseWidget *widget); - - // forced updates - void forceUpdateWidgets(); - -signals: - void dragDone(BaseWidget *widget, WidgetData data); - void dragCancelled(BaseWidget *widget); - -}; diff --git a/lib/include/widgets/TextWidget.h b/lib/include/widgets/TextWidget.h deleted file mode 100644 index 1151704c..00000000 --- a/lib/include/widgets/TextWidget.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "widgets/BaseWidget.h" - -#include - -class BaseWidget; - -class TextWidget : public BaseWidget -{ - Q_OBJECT - - Q_PROPERTY(QFont font READ font WRITE setFont) -protected: - QLineEdit *m_text; -public: - TextWidget(const WidgetTypes &type, const QString &topic, const QString &defaultText, const QString &title); - ~TextWidget(); - - QString text(); - void setText(const QString &text); - - QFont font(); - void setFont(const QFont &font); - - QMenu *constructContextMenu(WidgetData data) override; -}; diff --git a/lib/src/Globals.cpp b/lib/src/Globals.cpp deleted file mode 100644 index 8e28c018..00000000 --- a/lib/src/Globals.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "Globals.h" -#include "qapplication.h" -#include "stores/TypeStore.h" - -#include - -nt::NetworkTableInstance Globals::inst = nt::NetworkTableInstance::GetDefault(); -ServerData Globals::server{false, "0.0.0.0", NT_DEFAULT_PORT4}; - -QMap Globals::topicTypeDisplayNames{}; - -QStringList Globals::ntTopics{}; - -TypeStore Globals::typeStore; - -bool operator==(const WidgetData &a, const WidgetData &b) { - return (a.row == b.row) && - (a.col == b.col) && - (a.rowSpan == b.rowSpan) && - (a.colSpan == b.rowSpan); -} - -uint Globals::qHash(const Globals::Topic &topic) { - return qHash(topic.Name); -} - -bool Globals::Topic::operator==(const Globals::Topic &other) const { - return (this->Name == other.Name); -} - -void Globals::Topic::operator=(const Globals::NumberTopic &other) { - this->Name = other.Name; - this->Type = other.Type; -} -void Globals::Topic::operator=(const Globals::DoubleArrayTopic &other) { - this->Name = other.Name; - this->Type = other.Type; -} - -void Globals::NumberTopic::operator=(const Globals::Topic &other) { - this->Name = other.Name; - this->Type = other.Type; -} - -void Globals::DoubleArrayTopic::operator=(const Globals::Topic &other) { - this->Name = other.Name; - this->Type = other.Type; -} - -bool Globals::GraphXAxis::operator==(const Globals::GraphXAxis &other) const { - return (other.useTime && this->useTime) || ((other.useTime == this->useTime) && (other.topic == this->topic)); -} - -QMap Globals::shapeNameMap = { - {"Circle", Globals::FrameShape::Circle}, - {"Triangle", Globals::FrameShape::Triangle}, - {"Rectangle", Globals::FrameShape::Rectangle}, - {"Hexagon", Globals::FrameShape::Hexagon} -}; - -void setAppStyleSheet(QString styleSheet) { - QFile file(styleSheet); - file.open(QFile::ReadOnly | QFile::Text); - QTextStream stream(&file); - qApp->setStyleSheet(stream.readAll()); -} - diff --git a/lib/src/MainWindow.cpp b/lib/src/MainWindow.cpp deleted file mode 100644 index c408c69f..00000000 --- a/lib/src/MainWindow.cpp +++ /dev/null @@ -1,501 +0,0 @@ -#include "MainWindow.h" -#include "Globals.h" - -#include "dialogs/NewWidgetTreeDialog.h" -#include "dialogs/NTSettingsDialog.h" -#include "dialogs/PreferencesDialog.h" -#include "dialogs/WidgetDialogGenerator.h" -#include "dialogs/TabMaxSizeDialog.h" -#include "dialogs/CameraSelectionDialog.h" - -#include "stores/FilterStore.h" -#include "stores/TypeStore.h" -#include "widgets/CameraViewWidget.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "Constants.h" -#include "ui_MainWindow.h" - -MainWindow::MainWindow() : QMainWindow(), Ui::MainWindow() -{ - setupUi(this); - - // this isn't available in the ui lol - connect(centralwidget->tabBar(), &QTabBar::tabMoved, this, &MainWindow::moveTab); -} - -MainWindow::~MainWindow() { -} - -TabWidget *MainWindow::currentTab() { - return m_tabs.at(currentTabIdx()); -} - -int MainWindow::currentTabIdx() { - return centralwidget->currentIndex(); -} - -TabWidget *MainWindow::tabNamed(QString name) { - for (TabWidget *tab : m_tabs) { - if (tab->name() == name) { - return tab; - } - } - - return nullptr; -} - -void MainWindow::selectTab(TabWidget *tab) { - int idx = m_tabs.indexOf(tab); - if (idx != -1) centralwidget->setCurrentIndex(idx); -} - -/* File I/O */ - -QJsonDocument MainWindow::saveObject() { - QJsonDocument doc{}; - QJsonObject topObject{}; - QJsonArray tabs{}; - - for (TabWidget *tab : m_tabs) { - QJsonObject object = tab->saveObject(); - tabs.append(object); - } - - QJsonObject serverObj{}; - serverObj.insert("useTeamNumber", Globals::server.teamNumber); - serverObj.insert("address", QString::fromStdString(Globals::server.server)); - serverObj.insert("port", Globals::server.port); - serverObj.insert("topic", Globals::server.switchTopic); - - topObject.insert("server", serverObj); - topObject.insert("tabs", tabs); - doc.setObject(topObject); - return doc; -} - -void MainWindow::loadObject(const QJsonDocument &doc) { - QJsonObject object = doc.object(); - - QJsonObject serverObj = object.value("server").toObject(); - - ServerData server = ServerData{ - serverObj.value("useTeamNumber").toBool(false), - serverObj.value("address").toString("0.0.0.0").toStdString(), - serverObj.value("port").toInt(NT_DEFAULT_PORT4), - serverObj.value("topic").toString("") - }; - - setNtSettings(server); - - QJsonArray array = object.value("tabs").toArray(); - - for (QJsonValueRef ref : array) { - QJsonObject object = ref.toObject(); - - TabWidget *tab = new TabWidget(QPoint(3,3), this); - tab->setMouseTracking(true); - - tab->loadObject(object); - centralwidget->addTab(tab, tab->name()); - m_tabs.append(tab); - - for (BaseWidget *widget : tab->widgets()) { - if (centralwidget->currentWidget() != tab) { - widget->setDisabled(true); - } - widget->setReady(true); - } - } // tabs -} - -/* Private Member Functions */ -void MainWindow::makeNewWidget(WidgetTypes type) { - if (m_tabs.length() == 0) { - QMessageBox::StandardButton warning = QMessageBox::warning(this, "Cannot Add Widget", "You must select a tab before adding a widget.\nWould you like to add a tab now?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (warning == QMessageBox::StandardButton::Yes) { - newTab(); - } - } else { - BaseWidget *widget = BaseWidget::defaultWidgetFromTopic("", type); - - widget->setTitle(Globals::typeStore.widgetDisplayName(type)); - WidgetData data{0, 0, 1, 1}; - - beginNewWidgetDrag(widget, data); - } -} - -/* Slots */ - -// Internal Stuff -void MainWindow::forceUpdateTab(int idx) { - if (m_tabs.length() <= idx || idx == -1) return; - - for (BaseWidget *widget : m_tabs.at(m_lastIdx)->widgets()) { - widget->setDisabled(true); - } - - for (BaseWidget *widget : m_tabs.at(idx)->widgets()) { - widget->setEnabled(true); - widget->forceUpdate(); - } - - m_lastIdx = idx; -} - -void MainWindow::moveTab(int from, int to) { - m_tabs.move(from, to); -} - -void MainWindow::setConnected(bool connected) { - for (TabWidget *tab : m_tabs) { - for (BaseWidget *widget : tab->widgets()) { - widget->setConnected(connected); - } - } -} - -// Preferences -void MainWindow::preferences() { - PreferencesDialog *dialog = new PreferencesDialog(this); - - dialog->show(); - - connect(dialog, &PreferencesDialog::dataReady, this, [](QString styleSheet, bool loadRecent) { - setAppStyleSheet(styleSheet); - - Settings::StyleSheet.setValue(styleSheet); - Settings::LoadRecent.setValue(loadRecent); - }); -} - -// NT Settings -void MainWindow::ntSettingsPopup() { - NTSettingsDialog *dialog = new NTSettingsDialog(this); - - dialog->show(); - - connect(dialog, &NTSettingsDialog::dataReady, this, &MainWindow::setNtSettings); -} - -void MainWindow::setNtSettings(ServerData data) { - std::string server = data.server; - bool isTeamNumber = data.teamNumber; - int port = data.port; - QString switchTopic = data.switchTopic; - - if (server.empty()) return; - - if (isTeamNumber) { - int team; - try { - team = std::stoi(server); - } catch (std::invalid_argument const &) { - QMessageBox::critical(this, "Malformed Input", "Team number was selected but address does not resemble an integer."); - return; - } - - Globals::inst.SetServerTeam(team, port); - } else { - Globals::inst.SetServer(server.c_str(), port); - } - - if (Globals::server.server != data.server || - Globals::server.teamNumber != data.teamNumber || - Globals::server.port != data.port) { - Globals::inst.Disconnect(); - } - - QString serverTopic = Globals::server.switchTopic; - - Globals::server = data; - - if (serverTopic != switchTopic) { - emit switchTopicChanged(); - } -} - -// File Actions -void MainWindow::save() { - if (m_filename.isEmpty()) { - m_filename = QFileDialog::getSaveFileName( - this, "Save File", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); - } - - QFile file(m_filename); - - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::critical(this, "Save Failed!", "Failed to open file for writing. " - "Directory may not exist or may be read-only.", - QMessageBox::StandardButton::Ok); - return; - } - - addRecentFile(file); - refreshRecentFiles(); - - QTextStream stream(&file); - stream << saveObject().toJson(); - file.close(); -} - -void MainWindow::saveAs() { - m_filename = ""; - save(); -} - -void MainWindow::openDialog() { - QFile file(QFileDialog::getOpenFileName( - this, "Open File", QDir::homePath(), "JSON Files (*.json);;All Files (*)")); - - open(file); -} - -void MainWindow::open(QFile &file) { - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox::critical(this, "Load Failed!", "Failed to open file for reading. " - "Directory may not exist or may be inaccessible.", - QMessageBox::StandardButton::Ok); - return; - } - - m_filename = file.fileName(); - - addRecentFile(file); - refreshRecentFiles(); - - QTextStream stream(&file); - QByteArray data = stream.readAll().toUtf8(); - - QJsonDocument doc = QJsonDocument::fromJson(data); - - loadObject(doc); - file.close(); -} - -void MainWindow::refreshRecentFiles() { - menuRecent_Files->clear(); - QStringList recentFiles = Settings::RecentFiles.value().toStringList(); - size_t i = 0; - - for (const QString &file : recentFiles) { - ++i; - QString actionName = QString( - "&%1. %2" - ).arg(QString::number(i), file); - QAction *action = new QAction(actionName, menuRecent_Files); - connect(action, &QAction::triggered, this, [this, file]() { - QFile qfile(file); - open(qfile); - }); - - menuRecent_Files->addAction(action); - } -} - -void MainWindow::addRecentFile(QFile &file) { - QStringList recentFiles = Settings::RecentFiles.value().toStringList(); - - QString fileName = file.fileName(); - int index = recentFiles.indexOf(fileName); - - if (index != -1) { - recentFiles.move(index, 0); - } else { - recentFiles.prepend(fileName); - } - - if (recentFiles.length() > 5) { - recentFiles.removeLast(); - } - - Settings::RecentFiles.setValue(recentFiles); - refreshRecentFiles(); -} - -// Tab Actions -void MainWindow::newTab() { - bool ok; - QString tabName = QInputDialog::getText(this, "New Tab Name", "Input new tab name", QLineEdit::Normal, "", &ok); - - if (!tabName.isEmpty() && ok) { - TabWidget *tab = new TabWidget(QPoint(3, 3), this); - tab->setMouseTracking(true); - - m_tabs.append(tab); - centralwidget->addTab(tab, tabName); - centralwidget->setCurrentWidget(tab); - tab->setName(tabName); - } -} - -void MainWindow::closeTab() { - if (m_tabs.empty()) return; - int index = currentTabIdx(); - - QMessageBox::StandardButton close = QMessageBox::question(this, "Close Tab?", "Are you sure you want to close this tab?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - - if (close == QMessageBox::Yes) { - TabWidget *tab = m_tabs.at(index); - centralwidget->removeTab(index); - m_tabs.remove(index); - delete tab; - } -} - -void MainWindow::renameTab() { - if (m_tabs.empty()) return; - - bool ok; - QString tabName = QInputDialog::getText(this, "Tab Name", "Input new tab name", QLineEdit::Normal, centralwidget->tabText(currentTabIdx()), &ok); - - if (!tabName.isEmpty() && ok) { - centralwidget->setTabText(currentTabIdx(), tabName); - currentTab()->setName(tabName); - } -} - -void MainWindow::setMaxSize() { - if (m_tabs.empty()) return; - - TabWidget *tab = currentTab(); - TabMaxSizeDialog *dialog = new TabMaxSizeDialog(this, tab->maxSize()); - dialog->show(); - - connect(dialog, &TabMaxSizeDialog::dataReady, this, [tab](QPoint point) { - tab->setMaxSize(point); - }); -} - -// New Widget -void MainWindow::newWidgetPopup() { - - if (m_tabs.length() == 0) { - QMessageBox::StandardButton warning = QMessageBox::warning(this, "Cannot Add Widget", "You must select a tab before adding a widget.\nWould you like to add a tab now?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (warning == QMessageBox::StandardButton::Yes) { - newTab(); - } - } else { - NewWidgetTreeDialog *listDialog = new NewWidgetTreeDialog(false, this); - listDialog->setWindowTitle("Select Widget..."); - listDialog->constructList(FilterStore::FilteredTopics); - - // Width: 1/2 of available space - // Height: 1/2 of available space - QRect screenSize = qApp->primaryScreen()->geometry(); - listDialog->resize(screenSize.width() / 2., screenSize.height() / 2.); - - connect(listDialog, &NewWidgetTreeDialog::widgetReady, this, &MainWindow::beginNewWidgetDrag, Qt::SingleShotConnection); - - listDialog->show(); - } -} - -void MainWindow::beginNewWidgetDrag(BaseWidget *widget, WidgetData data) { - TabWidget *tab = currentTab(); - - tab->setDragData(widget, data); - tab->dragStart(QCursor::pos(), QPoint(0, 0)); - - QMetaObject::Connection *doneConn = new QMetaObject::Connection; - QMetaObject::Connection *cancelConn = new QMetaObject::Connection; - *doneConn = connect(tab, &TabWidget::dragDone, this, [cancelConn, doneConn](BaseWidget *widget, WidgetData data) { - disconnect(*doneConn); - delete doneConn; - - disconnect(*cancelConn); - delete cancelConn; - }, Qt::SingleShotConnection); - - *cancelConn = connect(tab, &TabWidget::dragCancelled, this, [doneConn, cancelConn, widget](BaseWidget *draggedWidget) { - if (draggedWidget == widget) { - delete widget; - - disconnect(*doneConn); - delete doneConn; - disconnect(*cancelConn); - delete cancelConn; - } - - }, Qt::SingleShotConnection); -} - -void MainWindow::newCameraView() { - makeNewWidget(WidgetTypes::CameraView); -} - -void MainWindow::newGraph() { - makeNewWidget(WidgetTypes::Graph); -} - -void MainWindow::newSwerve() { - makeNewWidget(WidgetTypes::Swerve); -} - -void MainWindow::cameraServerPopup() { - if (m_tabs.length() == 0) { - QMessageBox::StandardButton warning = QMessageBox::warning(this, "Cannot Add Widget", "You must select a tab before adding a widget.\nWould you like to add a tab now?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (warning == QMessageBox::StandardButton::Yes) { - newTab(); - } - } else { - CameraSelectionDialog *dialog = new CameraSelectionDialog(this); - dialog->show(); - - connect(dialog, &CameraSelectionDialog::selectedCamera, this, [this](Camera camera) { - QUrl url; - if (camera.Urls.isEmpty() || !camera.Urls.at(0).isValid()) { - QMessageBox::critical(this, - "Invalid Stream", - "This camera contains an invalid or nonexistent stream " - "URL. Please manually enter the correct URL if it " - "exists."); - url = QUrl(camera.Source); - } else { - url = camera.Urls.at(0); - } - - CameraViewWidget *widget = new CameraViewWidget( - QString("%1 (%2)").arg(camera.Name, camera.Source), url); - WidgetData data{0, 0, 1, 1}; - - beginNewWidgetDrag(widget, data); - }); - } -} - -// Menu -void MainWindow::about() { - QStringList aboutString; - aboutString << "Current Version: " + BuildConfig.versionString() - << "Build Platform: " + BuildConfig.BUILD_PLATFORM - << "Build Date: " + BuildConfig.BUILD_DATE - << "Git Repo: " + BuildConfig.GIT_REPO - << "Author: Carson Rueter " - << "Contributors: Ashley Hawkins " - << "Copyleft 2023-2024 Carson Rueter" - << "Enjoy :)"; - QMessageBox::about(this, "About " + BuildConfig.APP_NAME, aboutString.join("\n")); -} - -void MainWindow::aboutQt() { - QMessageBox::aboutQt(this, "About Qt"); -} diff --git a/lib/src/dialogs/CameraSelectionDialog.cpp b/lib/src/dialogs/CameraSelectionDialog.cpp deleted file mode 100644 index a69ceacf..00000000 --- a/lib/src/dialogs/CameraSelectionDialog.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "dialogs/CameraSelectionDialog.h" - -#include - -CameraSelectionDialog::CameraSelectionDialog(QWidget *parent) : QDialog(parent), Ui::CameraSelectionDialog() -{ - setupUi(this); - CameraStore::filterCameras(); - - for (const Camera &camera : CameraStore::Cameras) { - listWidget->addItem(camera.Name); - } -} - -void CameraSelectionDialog::emitCamera() { - auto items = listWidget->selectedItems(); - - if (items.empty()) return; - QString selected = items.at(0)->text(); - - Camera camera; - try { - camera = CameraStore::getCameraFromName(selected); - } catch (std::exception &e) { - qCritical() << "uh oh stinky"; - return; - } - - emit selectedCamera(camera); - accept(); -} diff --git a/lib/src/dialogs/NTSettingsDialog.cpp b/lib/src/dialogs/NTSettingsDialog.cpp deleted file mode 100644 index 58e7ce08..00000000 --- a/lib/src/dialogs/NTSettingsDialog.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "dialogs/NTSettingsDialog.h" - -#include "dialogs/NewWidgetTreeDialog.h" -#include "stores/FilterStore.h" - -#include - -NTSettingsDialog::NTSettingsDialog(QWidget *parent) : QDialog(parent), Ui::NTSettingsDialog() -{ - setupUi(this); - teamNumber->setChecked(Globals::server.teamNumber); - server->setText(QString::fromStdString(Globals::server.server)); - port->setValue(Globals::server.port); - topic->setText(Globals::server.switchTopic); -} - -NTSettingsDialog::~NTSettingsDialog() {} - -void NTSettingsDialog::serializeData() { - emit dataReady(ServerData{teamNumber->isChecked(), server->text().toStdString(), port->value(), topic->text()}); -} - -void NTSettingsDialog::putTopic() { - NewWidgetTreeDialog *dialog = new NewWidgetTreeDialog(true, this); - auto filteredTopics = NewWidgetTreeDialog::filterStringTypes(FilterStore::UnfilteredTopics); - - dialog->constructList(filteredTopics); - dialog->setWindowTitle("Select a Topic"); - - QRect screenSize = qApp->primaryScreen()->geometry(); - dialog->resize(screenSize.width() / 2., screenSize.height() / 2.); - - dialog->show(); - - connect(dialog, &NewWidgetTreeDialog::topicReady, this, [this](const Globals::Topic &topic) { - this->topic->setText(topic.Name); - }, Qt::SingleShotConnection); -} diff --git a/lib/src/dialogs/NewWidgetTreeDialog.cpp b/lib/src/dialogs/NewWidgetTreeDialog.cpp deleted file mode 100644 index 5bdcc64f..00000000 --- a/lib/src/dialogs/NewWidgetTreeDialog.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "dialogs/NewWidgetTreeDialog.h" - -#include "stores/TypeStore.h" -#include "stores/FilterStore.h" - -#include -#include -#include - -#include "Globals.h" - -NewWidgetTreeDialog::NewWidgetTreeDialog(bool emitTopic, QWidget *parent) : QDialog(parent), Ui::NewWidgetTreeDialog() -{ - setupUi(this); - m_emitTopic = emitTopic; - - tree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - - FilterStore::filterTopics(); - - if (!emitTopic) { - connect(&Globals::typeStore, &TypeStore::widgetReady, this, &NewWidgetTreeDialog::widgetReady); - connect(&Globals::typeStore, &TypeStore::widgetReady, this, &NewWidgetTreeDialog::close); - } -} - -NewWidgetTreeDialog::~NewWidgetTreeDialog() {} - -void NewWidgetTreeDialog::constructList(QList topics) { - tree->clear(); - - for (const Globals::Topic & topic : topics) - { - createTreeIfNotExists(topic); - - if (m_emitTopic) { - connect(tree, &QTreeWidget::itemActivated, this, [this, topic](QTreeWidgetItem *item) { - if (getParentPath(item) == topic.Name) { - emit topicReady(topic); - close(); - } - }); - } else { - QMenu *widgetMenu = Globals::typeStore.generateMenuForTopic(topic); - - connect(tree, &QTreeWidget::itemActivated, this, [this, topic, widgetMenu](QTreeWidgetItem *item) { - if (getParentPath(item) == topic.Name) { - widgetMenu->popup(QCursor::pos()); - } - }); - } - } -} - -void NewWidgetTreeDialog::createTreeIfNotExists(const Globals::Topic &topic) { - QStringList split = topic.Name.split('/'); - if (!split.at(0).isEmpty()) { // protect against entries not prefixed with / - split.prepend(""); - } - - QStringList tablePath = split.sliced(0, split.length() - 1); - - // The first entry will always be a blank string - for (int i = 1; i < tablePath.length(); ++i) { - QString table = tablePath.sliced(0, i + 1).join("/"); - QString superTable = tablePath.sliced(0, i).join("/"); - - if (m_itemTableMap.contains(table)) continue; - - QStringList columns{}; - columns << tablePath.at(i) << ""; - - QTreeWidgetItem *item = new QTreeWidgetItem(columns); - item->setFirstColumnSpanned(true); - - if (i == 1) { - m_itemTableMap.insert("/" + tablePath.at(1), item); - tree->addTopLevelItem(item); - - continue; - } - - QTreeWidgetItem *parent = m_itemTableMap.value(superTable); - - if (parent == nullptr) { - qCritical() << "something bad happened with" << table << superTable; - continue; - } - - parent->addChild(item); - m_itemTableMap.insert(table, item); - } - - QString topicEnd = split.last(); - QString superTable = tablePath.join('/'); - - QStringList columns{}; - columns << topicEnd << Globals::topicTypeDisplayNames.value(topic.Type); - - QTreeWidgetItem *item = new QTreeWidgetItem(columns); - item->setToolTip(1, columns.at(1)); - item->setFirstColumnSpanned(true); - - if (superTable.isEmpty()) { - tree->addTopLevelItem(item); - return; - } - - QTreeWidgetItem *parent = m_itemTableMap.value(superTable); - - if (parent == nullptr) { - qCritical() << "something very bad happened with topic" << topicEnd << superTable; - return; - } - - parent->addChild(item); -} - -QString NewWidgetTreeDialog::getParentPath(QTreeWidgetItem *item) { - QTreeWidgetItem *parent = item->parent(); - QStringList list{}; - list << item->text(0); - - while (parent != nullptr) { - list.prepend(parent->text(0)); - parent = parent->parent(); - } - - return "/" + list.join('/'); -} - -QList NewWidgetTreeDialog::filterTopicTypes(QList list, QList acceptableTypes) { - QList newList{}; - - for (const Globals::Topic &topic : list) { - if (acceptableTypes.contains(topic.Type)) { - newList.append(topic); - } - } - - return newList; -} - -QList NewWidgetTreeDialog::filterNumberTypes(QList list) { - QList acceptableTypes = { - TopicTypes::Int, - TopicTypes::Double, - TopicTypes::Boolean - }; - - return filterTopicTypes(list, acceptableTypes); -} - -QList NewWidgetTreeDialog::filterStringTypes(QList list) { - QList acceptableTypes = { - TopicTypes::String - }; - - return filterTopicTypes(list, acceptableTypes); -} - -void NewWidgetTreeDialog::keyPressEvent(QKeyEvent *event) { - if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { - return; - } - QDialog::keyPressEvent(event); -} diff --git a/lib/src/dialogs/PreferencesDialog.cpp b/lib/src/dialogs/PreferencesDialog.cpp deleted file mode 100644 index 3cf25578..00000000 --- a/lib/src/dialogs/PreferencesDialog.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "dialogs/PreferencesDialog.h" -#include "Constants.h" - -QMap StyleSheetMap = { - {"Dark", ":/dark/stylesheet.qss"}, - {"Dark Purple", ":/dark-purple/stylesheet.qss"}, - {"Light", ":/light/stylesheet.qss"}, - {"Light Purple", ":/light-purple/stylesheet.qss"}, -}; - -PreferencesDialog::PreferencesDialog(QWidget *parent) : QDialog(parent), Ui::PreferencesDialog() { - setupUi(this); - - lastLoaded->setChecked(Settings::LoadRecent.value().toBool()); - choices->setCurrentText(StyleSheetMap.key(Settings::StyleSheet.value().toString())); -} - -void PreferencesDialog::emitData() { - emit dataReady( - StyleSheetMap.value(choices->currentText(), ":/light/stylesheet.qss"), - lastLoaded->isChecked()); - accept(); -} diff --git a/lib/src/dialogs/TabMaxSizeDialog.cpp b/lib/src/dialogs/TabMaxSizeDialog.cpp deleted file mode 100644 index 08d3d99e..00000000 --- a/lib/src/dialogs/TabMaxSizeDialog.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "dialogs/TabMaxSizeDialog.h" - -TabMaxSizeDialog::TabMaxSizeDialog(QWidget *parent, const QPoint &maxSize) : QDialog(parent) , Ui::TabMaxSizeDialog() -{ - setupUi(this); - xBox->setValue(maxSize.x()); - yBox->setValue(maxSize.y()); -} - -TabMaxSizeDialog::~TabMaxSizeDialog() {} - -void TabMaxSizeDialog::emitData() { - emit dataReady(QPoint(xBox->value(), yBox->value())); - accept(); -} diff --git a/lib/src/dialogs/WidgetDialogGenerator.cpp b/lib/src/dialogs/WidgetDialogGenerator.cpp deleted file mode 100644 index 5d2bdb1f..00000000 --- a/lib/src/dialogs/WidgetDialogGenerator.cpp +++ /dev/null @@ -1,660 +0,0 @@ -#include "dialogs/WidgetDialogGenerator.h" - -#include "dialogs/NewWidgetTreeDialog.h" - -#include "qscreen.h" -#include "stores/FilterStore.h" - -#include "qheaderview.h" -#include "widgets/BaseWidget.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool operator<(QMetaProperty a, QMetaProperty b) { - return a.name() < b.name(); -} - -WidgetDialogGenerator::WidgetDialogGenerator(BaseWidget *widget, QWidget *parent, bool isResize, WidgetData data) : QDialog(parent), Ui::WidgetDialogGenerator() -{ - setupUi(this); - m_widget = widget; - m_isResize = isResize; - - nameInput->setText(widget->title()); - rowInput->setValue(data.row); - colInput->setValue(data.col); - rowSpanInput->setValue((data.rowSpan == 0 ? 1 : data.rowSpan)); - colSpanInput->setValue((data.colSpan == 0 ? 1 : data.colSpan)); - - m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - - connect(m_buttonBox, &QDialogButtonBox::rejected, this, &WidgetDialogGenerator::reject); - connect(this, &WidgetDialogGenerator::rejected, this, [this] { - emit cancelled(m_widget); - }); - - // only properties at BaseWidget and above - int offset = BaseWidget::staticMetaObject.propertyOffset(); - int propertyCount = widget->metaObject()->propertyCount(); - - for (int i = offset; i < propertyCount; ++i) { - QMetaProperty property = widget->metaObject()->property(i); - if (!property.isRequired() || (m_isResize && property.isConstant())) { - continue; - } - - QWidget *widgetToAdd; - int id = property.typeId(); - -#define PROPERTY_FUNCTION(type, function) if (id == type) widgetToAdd = function(property); else - - PROPERTY_FUNCTION(QMetaType::Double, doubleProperty) - PROPERTY_FUNCTION(QMetaType::Int, intProperty) - PROPERTY_FUNCTION(QMetaType::QColor, colorProperty) - PROPERTY_FUNCTION(QMetaType::QVariantMap, mapProperty) - PROPERTY_FUNCTION(QMetaType::QBitmap, bitmapProperty) - PROPERTY_FUNCTION(QMetaType::QImage, imageProperty) - PROPERTY_FUNCTION(CustomMetaTypes::File, fileProperty) - PROPERTY_FUNCTION(QMetaType::QFont, fontProperty) - PROPERTY_FUNCTION(QMetaType::QString, stringProperty) - PROPERTY_FUNCTION(QMetaType::QUrl, stringProperty) - PROPERTY_FUNCTION(CustomMetaTypes::FrameShape, shapeProperty) - PROPERTY_FUNCTION(CustomMetaTypes::NumberTopicList, topicListProperty) - PROPERTY_FUNCTION(CustomMetaTypes::XAxis, xAxisProperty) - PROPERTY_FUNCTION(CustomMetaTypes::NumberTopicColorMap, numberTopicColorMapProperty) - PROPERTY_FUNCTION(CustomMetaTypes::DoubleArrayTopic, doubleArrayTopicProperty) - { // else - qCritical() << "Bad metatype for property" << property.name(); - continue; - } // else - -#undef PROPERTY_FUNCTION - - formLayout->addRow(QString(property.name()).replace('_', ' '), widgetToAdd); - } - - formLayout->addRow(m_buttonBox); - - connect(m_buttonBox, &QDialogButtonBox::accepted, this, [this] { - // preset values - QString name = nameInput->text(); - - WidgetData widgetData; - - widgetData.row = rowInput->value(); - widgetData.col = colInput->value(); - widgetData.rowSpan = rowSpanInput->value(); - widgetData.colSpan = colSpanInput->value(); - - m_widget->setTitle(name); - - QMultiMapIterator iter(m_propertyGetterMap); - - while (iter.hasNext()) { - iter.next(); - - const QMetaProperty property = iter.key(); - Getter getter = iter.value(); - - property.write(m_widget, getter()); - } - - m_widget->setReady(true); - emit widgetReady(m_widget, widgetData); - accept(); - }); -} - -WidgetDialogGenerator::~WidgetDialogGenerator() {} - -void WidgetDialogGenerator::bindMetaProperty(const QMetaProperty property, Getter getter) { - m_propertyGetterMap.insert(property, getter); -} - -QVariantMap WidgetDialogGenerator::serializeTable(QTableWidget *widget) { - QVariantMap variantMap{}; - - for (int i = 0; i < widget->rowCount(); ++i) { - QString key = widget->item(i, 0)->text(); - QString value = widget->item(i, 1)->text(); - - if (!key.isEmpty() && !value.isEmpty()) variantMap.insert(key, value); - } - - return variantMap; -} - -void WidgetDialogGenerator::serializeMap(QHash map, QTableWidget *widget) { - QHashIterator iter(map); - - int i = 0; - while (iter.hasNext()) { - iter.next(); - - widget->insertRow(i); - widget->setItem(i, 0, new QTableWidgetItem(iter.key().Name)); - widget->setItem(i, 1, new QTableWidgetItem(iter.value().name())); - ++i; - } -} - -void WidgetDialogGenerator::serializeMap(QVariantMap map, QTableWidget *widget) { - QMapIterator iter(map); - - int i = 0; - while (iter.hasNext()) { - iter.next(); - - widget->insertRow(i); - widget->setItem(i, 0, new QTableWidgetItem(iter.key())); - widget->setItem(i, 1, new QTableWidgetItem(iter.value().toString())); - ++i; - } -} - -QPushButton *WidgetDialogGenerator::selectTopicButton(QList types) { - QPushButton *topicButton = new QPushButton("Select Topic...", this); - connect(topicButton, &QPushButton::clicked, this, [this, topicButton, types] { - NewWidgetTreeDialog *dialog = new NewWidgetTreeDialog(true, this); - auto filteredTopics = NewWidgetTreeDialog::filterTopicTypes(FilterStore::UnfilteredTopics, types); - - dialog->constructList(filteredTopics); - dialog->setWindowTitle("Select a Topic"); - - QRect screenSize = qApp->primaryScreen()->geometry(); - dialog->resize(screenSize.width() / 2., screenSize.height() / 2.); - - dialog->show(); - - connect(dialog, &NewWidgetTreeDialog::topicReady, this, [this, topicButton](const Globals::Topic &topic) { - emit topicSelected(topic, topicButton); - }, Qt::SingleShotConnection); - }); - - return topicButton; -} - -QPushButton *WidgetDialogGenerator::numberTopicButton() { - return selectTopicButton({ - TopicTypes::Double, - TopicTypes::Int, - TopicTypes::Boolean - }); -} - -QPushButton *WidgetDialogGenerator::doubleArrayTopicButton() { - return selectTopicButton({ - TopicTypes::DoubleArray - }); -} - -QPushButton *WidgetDialogGenerator::selectColorButton() { - QPushButton *colorButton = new QPushButton("Select Color...", this); - connect(colorButton, &QPushButton::clicked, this, [this] { - QColor random = QColor::fromRgb(QRandomGenerator::global()->generate()); - QColor color = QColorDialog::getColor(random, this, "Select Color"); - - if (color.isValid()) emit colorSelected(color); - }); - - return colorButton; -} - -std::tuple WidgetDialogGenerator::setupTable(const QStringList &headers) { - QTableWidget *table = new QTableWidget(0, headers.count(), this); - - table->setHorizontalHeaderLabels(headers); - table->horizontalHeader()->setStretchLastSection(false); - table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - QPushButton *addButton = new QPushButton("Add", this); - connect(addButton, &QPushButton::clicked, this, [table, headers] { - int rowCount = table->rowCount(); - - table->insertRow(rowCount); - for (int i = 0; i < headers.count(); ++i) { - table->setItem(rowCount, i, new QTableWidgetItem("")); - } - }); - - QPushButton *removeButton = new QPushButton("Delete", this); - removeButton->setShortcut(Qt::Key_Delete); - connect(removeButton, &QPushButton::clicked, this, [table] { - QList selectedItems = table->selectedItems(); - - if (selectedItems.empty()) return; - - table->removeRow(selectedItems.at(0)->row()); - }); - - return {table, addButton, removeButton}; -} - -QWidget *WidgetDialogGenerator::doubleProperty(QMetaProperty property) { - // QDoubleSpinBox - QDoubleSpinBox *spinBox = new QDoubleSpinBox(this); - spinBox->setRange(-1000000, 1000000); - /*if (isResize)*/ spinBox->setValue(property.read(m_widget).toDouble()); - - auto func = std::bind(&QDoubleSpinBox::value, spinBox); - bindMetaProperty(property, func); - - return spinBox; -} - -QWidget *WidgetDialogGenerator::intProperty(QMetaProperty property) { - // QSpinBox - QSpinBox *spinBox = new QSpinBox(this); - spinBox->setRange(-1000000, 1000000); - /*if (isResize)*/ spinBox->setValue(property.read(m_widget).toInt()); - - auto func = std::bind(&QSpinBox::value, spinBox); - bindMetaProperty(property, func); - - return spinBox; -} - -QWidget *WidgetDialogGenerator::colorProperty(QMetaProperty property) { - // QColorDialog etc. - QWidget *input = new QWidget(this); - QHBoxLayout *inputLayout = new QHBoxLayout(input); - - QLineEdit *colorEdit = new QLineEdit(this); - /*if (isResize)*/ colorEdit->setText(property.read(m_widget).toString()); - - QPushButton *colorSelect = new QPushButton("Select Color...", this); - - inputLayout->addWidget(colorEdit); - inputLayout->addWidget(colorSelect); - - connect(colorSelect, &QPushButton::clicked, this, [colorEdit, this] { - QColor color = QColorDialog::getColor(QColor::fromString(colorEdit->text()), this); - - colorEdit->setText(color.name()); - }); - - auto func = std::bind(&QLineEdit::text, colorEdit); - bindMetaProperty(property, func); - - return input; -} - -QWidget *WidgetDialogGenerator::mapProperty(QMetaProperty property) { - // macos sucks - auto tableVars = setupTable({"Topic", "Color"}); - auto table = std::get<0>(tableVars); - auto addButton = std::get<1>(tableVars); - auto removeButton = std::get<2>(tableVars); - - serializeMap(property.read(m_widget).toMap(), table); - - QWidget *tableLayoutWidget = new QWidget; - - QVBoxLayout *tableLayout = new QVBoxLayout(tableLayoutWidget); - - QHBoxLayout *buttons = new QHBoxLayout; - - buttons->addWidget(addButton); - buttons->addWidget(removeButton); - - tableLayout->addWidget(table); - tableLayout->addLayout(buttons); - - // TODO: Insert buttons for certain types (images, etc) - - auto func = std::bind(&WidgetDialogGenerator::serializeTable, this, table); - bindMetaProperty(property, func); - - return tableLayoutWidget; -} - -QWidget *WidgetDialogGenerator::bitmapProperty(QMetaProperty property) { - qCritical() << "Not implemented"; - return nullptr; -} - -QWidget *WidgetDialogGenerator::imageProperty(QMetaProperty property) { - qCritical() << "Not implemented"; - return nullptr; -} - -QWidget *WidgetDialogGenerator::fileProperty(QMetaProperty property) { - QWidget *layoutWidget = new QWidget(this); - QHBoxLayout *layout = new QHBoxLayout(layoutWidget); - - QComboBox *builtinBox = new QComboBox(this); - - QString value = property.read(m_widget).value().fileName; - - for(const QString &image : QDir(":").entryList()) - { - if (image.endsWith(".png")) - builtinBox->addItem(image); - } - - QLineEdit *customEdit = new QLineEdit(this); - QPushButton *fileButton = new QPushButton("Select File...", this); - - connect(fileButton, &QPushButton::clicked, this, [customEdit, this] { - QString file = QFileDialog::getOpenFileName(this, "Open Image File", QDir::homePath(), "Images (*.png *.xpm *.jpg *.jpeg *.bmp)"); - customEdit->setText(file); - }); - - QCheckBox *switchBox = new QCheckBox("Use Built-In?", this); - switchBox->setChecked(value.startsWith(":")); - - if (switchBox->isChecked()) { - builtinBox->setCurrentText(value.mid(2)); - } else { - customEdit->setText(value); - } - - // re-lay each time for a smooth transition - auto updateLayout = [builtinBox, customEdit, fileButton, switchBox, layout](bool checked) { - layout->removeWidget(switchBox); - if (checked) { - layout->removeWidget(customEdit); - layout->removeWidget(fileButton); - - customEdit->hide(); - fileButton->hide(); - - layout->addWidget(builtinBox, 1); - - builtinBox->show(); - } else { - layout->removeWidget(builtinBox); - - builtinBox->hide(); - - layout->addWidget(customEdit, 1); - layout->addWidget(fileButton, 1); - - customEdit->show(); - fileButton->show(); - } - layout->addWidget(switchBox, 1); - }; - - updateLayout(switchBox->isChecked()); - - connect(switchBox, &QCheckBox::toggled, this, updateLayout); - - Getter func = [builtinBox, customEdit, switchBox]() -> QVariant { - QVariant value; - - if (switchBox->isChecked()) { - value.setValue(Globals::File{":/" + builtinBox->currentText()}); - } else { - value.setValue(Globals::File{customEdit->text()}); - } - - return value; - }; - - bindMetaProperty(property, func); - - return layoutWidget; -} - -QWidget *WidgetDialogGenerator::fontProperty(QMetaProperty property) { - qCritical() << "Not implemented"; - return nullptr; -} - -QWidget *WidgetDialogGenerator::stringProperty(QMetaProperty property) { - // bruh - QLineEdit *edit = new QLineEdit(this); - /*if (isResize)*/ edit->setText(property.read(m_widget).toString()); - - auto func = std::bind(&QLineEdit::text, edit); - bindMetaProperty(property, func); - - return edit; -} - -QWidget *WidgetDialogGenerator::shapeProperty(QMetaProperty property) { - - QComboBox *comboBox = new QComboBox(this); - comboBox->addItems(Globals::shapeNameMap.keys()); - - comboBox->setCurrentText(Globals::shapeNameMap.key(property.read(m_widget).value())); - - Getter func = [comboBox]() -> QVariant { - return QVariant::fromValue(Globals::shapeNameMap.value(comboBox->currentText())); - }; - - bindMetaProperty(property, func); - - return comboBox; -} QVariantMap variantMap{}; - -QWidget *WidgetDialogGenerator::topicListProperty(QMetaProperty property) { - QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); - - QListWidget *list = new QListWidget(widget); - layout->addWidget(list, 1); - - auto topics = property.read(m_widget).value>(); - - QStringList names = FilterStore::topicNames(topics); - list->addItems(names); - - QPushButton *topicButton = numberTopicButton(); - connect(this, &WidgetDialogGenerator::topicSelected, this, [list](const Globals::Topic &topic, QWidget *) { - list->addItem(topic.Name); - }); - - QPushButton *addButton = new QPushButton("Add", this); - connect(addButton, &QPushButton::clicked, this, [list] { - QListWidgetItem *item = new QListWidgetItem("Topic", list); - item->setFlags(Qt::ItemFlag::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); - list->addItem(item); - }); - - QPushButton *removeButton = new QPushButton("Delete", this); - removeButton->setShortcut(Qt::Key_Delete); - connect(removeButton, &QPushButton::clicked, this, [list] { - QList selectedItems = list->selectedItems(); - - for (QListWidgetItem * item : selectedItems) { - delete item; - } - }); - - QHBoxLayout *buttonLayout = new QHBoxLayout; - - buttonLayout->addWidget(topicButton); - buttonLayout->addWidget(addButton); - buttonLayout->addWidget(removeButton); - - layout->addLayout(buttonLayout); - - auto stringListToTopicList = [](const QStringList &list) -> QList { - QList topicList{}; - - for (const QString & name : list) { - Globals::Topic topic = FilterStore::topicFromName(name, FilterStore::FilteredTopics); - if (topic.Name == name) { - Globals::NumberTopic t; - t = topic; - topicList.append(t); - } - } - - return topicList; - };; - - auto func = [list, stringListToTopicList]() -> QVariant { - QStringList names{}; - for (int i = 0; i < list->count(); ++i) { - names.append(list->item(i)->text()); - } - - return QVariant::fromValue(stringListToTopicList(names)); - }; - - bindMetaProperty(property, func); - - return widget; -} - -QWidget *WidgetDialogGenerator::xAxisProperty(QMetaProperty property) { - Globals::GraphXAxis xAxis = property.read(m_widget).value(); - QWidget *widget = new QWidget(this); - QHBoxLayout *layout = new QHBoxLayout(widget); - - QCheckBox *checkbox = new QCheckBox("Use Time?", widget); - checkbox->setChecked(xAxis.useTime); - - QLineEdit *lineEdit = new QLineEdit(widget); - lineEdit->setText(xAxis.topic); - - QPushButton *topicButton = numberTopicButton(); - connect(this, &WidgetDialogGenerator::topicSelected, this, [lineEdit, topicButton](const Globals::Topic &topic, QWidget *receiver) { - if (topicButton == receiver) lineEdit->setText(topic.Name); - }); - - layout->addWidget(checkbox, 0); - layout->addWidget(lineEdit, 1); - layout->addWidget(topicButton, 0); - - auto showLineEdit = [lineEdit, topicButton](bool checked) { - lineEdit->setVisible(!checked); - topicButton->setVisible(!checked); - }; - - showLineEdit(checkbox->isChecked()); - - connect(checkbox, &QCheckBox::clicked, this, showLineEdit); - - auto func = [lineEdit, checkbox]() -> QVariant { - Globals::GraphXAxis axis{}; - axis.useTime = checkbox->isChecked(); - axis.topic = lineEdit->displayText(); - - return QVariant::fromValue(axis); - }; - - bindMetaProperty(property, func); - return widget; -} - -QWidget *WidgetDialogGenerator::numberTopicColorMapProperty(QMetaProperty property) { - // macos sucks - auto tableVars = setupTable({"Topic", "Color"}); - auto table = std::get<0>(tableVars); - auto addButton = std::get<1>(tableVars); - auto removeButton = std::get<2>(tableVars); - - serializeMap(property.read(m_widget).value>(), table); - - QPushButton *topicButton = numberTopicButton(); - connect(this, &WidgetDialogGenerator::topicSelected, this, [this, table, topicButton](const Globals::Topic &topic, QWidget *receiver) { - if (topicButton != receiver) return; - QList selectedItems = table->selectedItems(); - - if (selectedItems.empty()) { - qApp->clipboard()->setText(topic.Name); - QMessageBox::information(this, "Copied to Clipboard", "Copied topic selection to clipboard."); - return; - } - - int row = table->row(selectedItems.at(0)); - - table->item(row, 0)->setText(topic.Name); - }); - - QPushButton *colorButton = selectColorButton(); - connect(this, &WidgetDialogGenerator::colorSelected, this, [this, table](const QColor &color) { - QList selectedItems = table->selectedItems(); - - if (selectedItems.empty()) { - qApp->clipboard()->setText(color.name()); - QMessageBox::information(this, "Copied to Clipboard", "Copied color selection to clipboard."); - return; - } - - int row = table->row(selectedItems.at(0)); - - table->item(row, 1)->setText(color.name()); - }); - - QWidget *tableLayoutWidget = new QWidget(this); - - QVBoxLayout *tableLayout = new QVBoxLayout(tableLayoutWidget); - - QHBoxLayout *buttons = new QHBoxLayout; - - buttons->addWidget(addButton); - buttons->addWidget(removeButton); - buttons->addWidget(topicButton); - buttons->addWidget(colorButton); - - tableLayout->addWidget(table); - tableLayout->addLayout(buttons); - - auto func = [table]() -> QVariant { - QHash map{}; - - for (int i = 0; i < table->rowCount(); ++i) { - QString name = table->item(i, 0)->text(); - Globals::NumberTopic key; - key = FilterStore::topicFromName(name, FilterStore::UnfilteredTopics); - QString value = table->item(i, 1)->text(); - - if (!key.Name.isEmpty() && !value.isEmpty()) map.insert(key, QColor(value)); - } - - return QVariant::fromValue(map); - }; - - bindMetaProperty(property, func); - - return tableLayoutWidget; -} - -QWidget *WidgetDialogGenerator::doubleArrayTopicProperty(QMetaProperty property) { - QWidget *widget = new QWidget(this); - QLineEdit *topicEdit = new QLineEdit(this); - QHBoxLayout *layout = new QHBoxLayout(widget); - - topicEdit->setText(property.read(m_widget).value().Name); - - QPushButton *topicButton = doubleArrayTopicButton(); - connect(this, &WidgetDialogGenerator::topicSelected, this, [topicEdit, topicButton](const Globals::Topic &topic, QWidget *receiver) { - if (topicButton != receiver) return; - topicEdit->setText(topic.Name); - }); - - layout->addWidget(topicEdit); - layout->addWidget(topicButton); - - auto func = [topicEdit]() -> QVariant { - QString name = topicEdit->text(); - Globals::DoubleArrayTopic top; - top = FilterStore::topicFromName(name, FilterStore::UnfilteredTopics); - - return QVariant::fromValue(top); - }; - - bindMetaProperty(property, func); - - return widget; -} diff --git a/lib/src/misc/BetterDial.cpp b/lib/src/misc/BetterDial.cpp deleted file mode 100644 index 758e1c64..00000000 --- a/lib/src/misc/BetterDial.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "misc/BetterDial.h" - -#include -#include -#include -#include -#include - -#include - -BetterDial::BetterDial(QWidget *parent) - : QWidget{parent} -{ - setMin(0); - setMax(10); - setStartingAngle(0.); -} - -int BetterDial::min() { - return m_min; -} - -void BetterDial::setMin(int min) { - m_min = min; -} - -int BetterDial::max() { - return m_max; -} - -void BetterDial::setMax(int max) { - m_max = max; -} - -int BetterDial::value() { - return m_value; -} - -void BetterDial::setValue(int value) { - m_value = value; -} - -double BetterDial::startingAngle() { - return m_start; -} - -void BetterDial::setStartingAngle(double angle) { - m_start = angle; -} - -bool BetterDial::isDragging() { - return m_isDragging; -} - -void BetterDial::paintEvent(QPaintEvent *event) { - - QWidget::paintEvent(event); - - QPainter painter(this); - QPainterPath path; - QRect rect = event->rect(); - - int diameter = qMin(rect.width(), rect.height()); - int radius = diameter / 2; - - m_radius = radius; - - // Background Circle - painter.setBrush(QBrush(Qt::white)); - - path.addEllipse(rect.center(), radius, radius); - - painter.drawPath(path); - painter.fillPath(path, QBrush(Qt::white)); - - // Center Circle - painter.setBrush(QBrush(Qt::red)); - - QPainterPath centerPath; - centerPath.addEllipse(rect.center(), 10., 10.); - - painter.drawPath(centerPath); - painter.fillPath(centerPath, QBrush(Qt::red)); - - // Triangle Thing - double range = max() - min(); - double rotations = m_value / range; - double angle = rotations * 2 * M_PI - m_start; - - double x = rect.center().x() + radius * std::sin(angle); - double y = rect.center().y() + radius * std::cos(angle); - - double perpendicularSin = std::sin(angle + M_PI / 2.); - double perpendicularCos = std::cos(angle + M_PI / 2.); - - QPolygonF polygon; - polygon << QPointF(rect.center().x() + perpendicularSin * 10., rect.center().y() + perpendicularCos * 10.) - << QPointF(x + perpendicularSin, y + perpendicularCos) - << QPointF(x - perpendicularSin, y - perpendicularCos) - << QPointF(rect.center().x() - perpendicularSin * 10., rect.center().y() - perpendicularCos * 10.); - - painter.drawPolygon(polygon); - - return; -} - -void BetterDial::mousePressEvent(QMouseEvent *event) { - QWidget::mousePressEvent(event); - - if (std::abs((event->pos() - rect().center()).manhattanLength()) >= m_radius) return event->ignore(); - - if (event->buttons() & Qt::LeftButton) { - setValue(valueFromPoint(event->position())); - emit sliderMoved(m_value); - - m_isDragging = true; - event->accept(); - } -} - -void BetterDial::mouseMoveEvent(QMouseEvent *event) { - QWidget::mousePressEvent(event); - - if (std::abs((event->pos() - rect().center()).manhattanLength()) >= m_radius) return event->ignore(); - - if (event->buttons() & Qt::LeftButton) { - setValue(valueFromPoint(event->position())); - emit sliderMoved(m_value); - - m_isDragging = true; - - event->accept(); - } -} - -void BetterDial::mouseReleaseEvent(QMouseEvent *event) { - if (std::abs((event->pos() - rect().center()).manhattanLength()) >= m_radius) return event->ignore(); - - m_isDragging = false; -} - -double BetterDial::valueFromPoint(QPointF point) { - double x = -(point.x() - this->rect().center().x()); - double y = -(point.y() - this->rect().center().y()); - - double angle = std::fmod((std::atan2(x, y) + M_PI + m_start), (2 * M_PI)); - double rotations = angle / (2 * M_PI); - - double range = max() - min(); - - double offset = rotations * range; - - return min() + offset; -} diff --git a/lib/src/misc/ChartView.cpp b/lib/src/misc/ChartView.cpp deleted file mode 100644 index 5b7223f3..00000000 --- a/lib/src/misc/ChartView.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "misc/ChartView.h" - -ChartView::ChartView(QChart *chart, QWidget *parent) : QChartView(chart, parent){} - -void ChartView::mouseReleaseEvent(QMouseEvent *event) { - event->ignore(); -} - -void ChartView::mousePressEvent(QMouseEvent *event) { - event->ignore(); -} - -void ChartView::mouseMoveEvent(QMouseEvent *event) { - event->ignore(); -} - diff --git a/lib/src/misc/FieldImage.cpp b/lib/src/misc/FieldImage.cpp deleted file mode 100644 index 61b33f2b..00000000 --- a/lib/src/misc/FieldImage.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "misc/FieldImage.h" - -#include -#include -#include - -static double FieldWidth = 8.2296; -static double FieldLength = 8.2296 * 2.; - -FieldImage::FieldImage(QWidget *parent) : QLabel(parent) {} - -void FieldImage::setValue(std::span value) { - if (value.size() > 0) - m_x = value[0]; - if (value.size() > 1) - m_y = value[1]; - if (value.size() > 2) - m_theta = value[2]; -} - -void FieldImage::setRobotWidth(double width) { - m_width = width; -} - -void FieldImage::setRobotLength(double length) { - m_length = length; -} - -void FieldImage::setImage(Globals::File image) { - m_image = image; - m_imageChanged = true; - - update(); -} - -void FieldImage::paintEvent(QPaintEvent *event) { - QLabel::paintEvent(event); - - int w = event->rect().width(); - int h = event->rect().height() * 4. / 5.; - - // painter setup - QPainter painter(this); - - if (m_imageChanged || m_lastRect != event->rect()) { - QPixmap pixmap = QPixmap(m_image.fileName); - QPixmap pixmap2 = pixmap.scaled(w, h, Qt::KeepAspectRatio); - - m_pixmap = pixmap2; - - m_imageChanged = false; - m_lastRect = event->rect(); - } - - QPointF topLeft = QPointF(w / 2. - m_pixmap.width() / 2., h / 2. - m_pixmap.height() / 2.); - - painter.drawPixmap(topLeft, m_pixmap, m_pixmap.rect().toRectF()); - - m_imageWidth = m_pixmap.width(); - m_imageHeight = m_pixmap.height(); - - // meters->pixels - double meterRatio = (double) m_imageHeight / FieldWidth; - - double robotWidth = m_width * meterRatio; - double robotLength = m_length * meterRatio; - - // getting some important points & rects - QPointF robotTopLeft = topLeft + QPointF(m_x * meterRatio, (FieldWidth - m_y) * meterRatio - robotLength); - - QRectF robotRect(robotTopLeft, QSizeF(robotWidth, robotLength)); - QPointF absoluteCenter = robotTopLeft + QPointF(robotWidth / 2., robotLength / 2.); - - // painter setup - QPen pen(Qt::red, 3); - painter.setPen(pen); - - // I don't remember geometry class. - // God Bless the Qt forums - painter.translate(absoluteCenter); - painter.rotate(-m_theta); // make CCW positive, algebraic plane - painter.translate(-absoluteCenter); - - painter.drawRect(robotRect); - - QPen arrowPen(Qt::green, 3); - painter.setPen(arrowPen); - - // ;) - QPolygonF polygon; - polygon << robotTopLeft - << robotTopLeft + QPointF(robotWidth, robotLength / 2.) - << robotTopLeft + QPointF(0, robotLength) - << robotTopLeft; - - painter.drawPolygon(polygon); -} diff --git a/lib/src/misc/GridLineWidget.cpp b/lib/src/misc/GridLineWidget.cpp deleted file mode 100644 index e122be15..00000000 --- a/lib/src/misc/GridLineWidget.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "misc/GridLineWidget.h" - -#include -#include -#include - -GridLineWidget::GridLineWidget(QWidget *parent) - : QWidget{parent} -{ - setMouseTracking(true); -} - -void GridLineWidget::setSize(QPoint size) { - m_size = size; -} - -WidgetData GridLineWidget::selection() { - return m_selection; -} - -void GridLineWidget::setSelection(const WidgetData &selectedIndex) { - bool doUpdate = m_selection != selectedIndex; - - m_selection = selectedIndex; - setHasSelection(true); - - if (doUpdate) update(); -} - -bool GridLineWidget::hasSelection() { - return m_hasSelection; -} - -void GridLineWidget::setHasSelection(const bool &hasSelection) { - m_hasSelection = hasSelection; -} - -bool GridLineWidget::isValidSelection() { - return m_isValidSelection; -} - -void GridLineWidget::setValidSelection(const bool &isValidSelection) { - m_isValidSelection = isValidSelection; -} - -void GridLineWidget::paintEvent(QPaintEvent *event) { - QWidget::paintEvent(event); - - QPainter painter(this); - QPen pen; - pen.setColor(Qt::gray); - pen.setWidth(1); - painter.setPen(pen); - - for (int x = 0; x < m_size.x() + 1; ++x) { - double xPos = width() / m_size.x() * (double) x; - - painter.drawLine(QLineF( - QPointF(xPos, 0.), - QPointF(xPos, height()))); - } - - for (int y = 0; y < m_size.y() + 1; ++y) { - double yPos = height() / m_size.y() * (double) y; - - painter.drawLine(QLineF( - QPointF(0, yPos), - QPointF(width(), yPos))); - } - - if (m_hasSelection) { - int row = m_selection.row; - int col = m_selection.col; - int rowSpan = m_selection.rowSpan; - int colSpan = m_selection.colSpan; - - double w = this->width() / m_size.x(); - double h = this->height() / m_size.y(); - - double x = w * col; - double y = h * row; - - pen.setColor(m_isValidSelection ? Qt::green : Qt::red); - pen.setWidth(6); - - painter.setPen(pen); - - QPainterPath path; - - path.addRect(QRect( - x, y, w * colSpan, h * rowSpan)); - - painter.drawPath(path); - } -} diff --git a/lib/src/misc/ShapedFrame.cpp b/lib/src/misc/ShapedFrame.cpp deleted file mode 100644 index 9be910ac..00000000 --- a/lib/src/misc/ShapedFrame.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "misc/ShapedFrame.h" - -#include -#include -#include - -#include - -/** - * @brief getLine Get a point a certain length away at a specific angle. - * @param initial point to start from - * @param angle radians on the algebraic plane - * @param length line length (pixels) - * @return - */ -QPointF getLine(QPointF initial, double angle, double length) { - double x = initial.x() + length * std::cos(angle); - double y = initial.y() + length * std::sin(angle); - - return QPointF(x, y); -} - -ShapedFrame::ShapedFrame(Globals::FrameShape shape, QWidget *parent) : QFrame(parent) -{ - m_shape = shape; -} - -void ShapedFrame::paintEvent(QPaintEvent *event) { - QFrame::paintEvent(event); - - QPainter painter(this); - QPainterPath path; - QRect rect = event->rect(); - - int diameter = qMin(rect.width(), rect.height()); - int radius = diameter / 2; - - painter.setBrush(QBrush(m_color)); - - switch (m_shape) { - case Globals::FrameShape::Circle: { - path.addEllipse(rect.center(), radius, radius); - - painter.drawPath(path); - painter.fillPath(path, QBrush(m_color)); - - return; - } - case Globals::FrameShape::Hexagon: { - // shenanigans - radius *= std::cos(M_PI / 6.); - double sideLength = radius * 2. / std::sqrt(3); - - QPointF point1 = QPointF(rect.center().x() - sideLength, rect.height() / 2.); - - QPointF point2 = getLine(point1, M_PI / 3., sideLength); - QPointF point3 = getLine(point2, 0., sideLength); - QPointF point4 = getLine(point3, -M_PI / 3., sideLength); - QPointF point5 = getLine(point4, 4 * M_PI / 3., sideLength); - QPointF point6 = getLine(point5, M_PI, sideLength); - - QPolygonF polygon; - polygon << point1 << point2 << point3 << point4 << point5 << point6; - - painter.drawPolygon(polygon); - - return; - } - case Globals::FrameShape::Triangle: { - QPolygonF polygon; - polygon << QPointF(0, rect.height()) - << QPointF(rect.width() / 2., 0) - << QPointF(rect.width(), rect.height()); - - painter.drawPolygon(polygon); - - return; - } - case Globals::FrameShape::Rectangle: - painter.drawRect(rect); - painter.fillRect(rect, QBrush(m_color)); - return; - } -} - -ShapedFrame::~ShapedFrame() {} - -Globals::FrameShape ShapedFrame::shape() { - return m_shape; -} - -void ShapedFrame::setShape(Globals::FrameShape shape) { - m_shape = shape; -} - -QColor ShapedFrame::color() { - return m_color; -} - -void ShapedFrame::setColor(QColor color) { - setColor(color.name()); -} - -void ShapedFrame::setColor(QString color) { - m_color = color; -} diff --git a/lib/src/misc/SwerveTrain.cpp b/lib/src/misc/SwerveTrain.cpp deleted file mode 100644 index bd77cb20..00000000 --- a/lib/src/misc/SwerveTrain.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "misc/SwerveTrain.h" - -#include -#include -#include -#include - -SwerveTrain::SwerveTrain(QWidget *parent) : QWidget(parent) { - -} - -void SwerveTrain::setLocations(QList locations) { - m_locations = locations; -} - -void SwerveTrain::setStates(QList states) { - m_states = states; -} - -void SwerveTrain::paintEvent(QPaintEvent *event) { - QWidget::paintEvent(event); - - QRect rect = event->rect(); - - int w = rect.width(); - int h = rect.height(); - - double l = qMin(w, h); - double xOffset = qMax(0., (w - h) / 2.); - double yOffset = qMax(0., (h - w) / 2.); - - QPointF topLeft(xOffset, yOffset); - - QRectF baseRect = QRectF( - topLeft + QPointF(l / 6., l / 6.), - QSizeF((l * 2.) / 3., - (l * 2.) / 3.) - ); - - QPainter painter(this); - - int penWidth = qCeil(l / 100); - - QPen basePen(QBrush(Qt::white), penWidth); - painter.setPen(basePen); - - painter.drawRect(baseRect); - - if (m_states.size() != m_locations.size()) return; - - double xMax = 0; - double yMax = 0; - - for (int i = 0; i < m_locations.size(); ++i) { - double abs = std::abs(m_locations.at(i)); - if (i % 2 == 0 && abs > xMax) xMax = abs; - if (i % 2 == 1 && abs > yMax) yMax = abs; - } - - QPen arcPen(Qt::red); - QPen velPen(Qt::blue, l / 100.); - - double wheelScale = 8.; - - for (int i = 0; i < m_locations.size(); i += 2) { - double x = m_locations.at(i); - double y = m_locations.at(i + 1); - - double px = l / 2. - ( (x * l / 3.) / xMax); - double py = l / 2. - ( (y * l / 3.) / yMax); - - double r = l / (wheelScale * 2.); - - QPainterPath wheelsPath; - - QRectF wheelRect( - topLeft + QPointF(px - r, py - r), - QSizeF(r * 2., r * 2.)); - - painter.setPen(basePen); - - wheelsPath.addEllipse(wheelRect); - painter.drawPath(wheelsPath); - painter.fillPath(wheelsPath, QBrush(Qt::black)); - - // WHEEL ANGLE ARC - double velocity = m_states.at(i); - double angle = m_states.at(i + 1) + 90.; - - if (velocity < 0) { - velocity = qAbs(velocity); - angle += 180.; - } - - angle = std::fmod(angle, 360.); - if (angle < 0) angle += 360.; - - QPainterPath arcPath; - - arcPath.moveTo(wheelRect.center()); - arcPath.arcTo(wheelRect, angle - 10., 20.); - arcPath.closeSubpath(); - - painter.setPen(arcPen); - painter.drawPath(arcPath); - painter.fillPath(arcPath, Qt::red); - - // VELOCITY VECTOR ARROW - QPainterPath velPath; - - painter.setPen(velPen); - - QPointF velI = wheelsPath.pointAtPercent(0.75) + QPointF(0, -l / 100.); - QPointF velF = velI + QPointF(0., -l / 16. * velocity); - - velPath.moveTo(velI); - velPath.lineTo(velF); - velPath.lineTo(velF + QPointF(-l / 32., l / 32.)); - velPath.moveTo(velF); - velPath.lineTo(velF + QPointF(l / 32., l / 32.)); - - // funky rotation thing - painter.translate(wheelRect.center()); - painter.rotate(-angle + 90); - painter.translate(-wheelRect.center()); - - painter.drawPath(velPath); - - // undo the rotation - painter.translate(wheelRect.center()); - painter.rotate(angle - 90); - painter.translate(-wheelRect.center()); - } -} diff --git a/lib/src/stores/CameraStore.cpp b/lib/src/stores/CameraStore.cpp deleted file mode 100644 index d0a9e49f..00000000 --- a/lib/src/stores/CameraStore.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "stores/CameraStore.h" -#include "Globals.h" - -#include - -QList CameraStore::Cameras{}; - -CameraStore::CameraStore() -{ - throw std::runtime_error("CameraStore is a static class."); -} - -void CameraStore::filterCameras() { - Cameras.clear(); - std::shared_ptr table = Globals::inst.GetTable("/CameraPublisher"); - - for (const std::string &st : table->GetSubTables()) { - std::shared_ptr subtable = table->GetSubTable(st); - - Camera camera = Camera::fromTable(subtable); - Cameras.append(camera); - } -} - -Camera Camera::fromTable(std::shared_ptr table) { - Camera camera{}; - - camera.Name = QString::fromStdString(table->GetEntry("description").GetString("")); - camera.Source = QString::fromStdString(table->GetEntry("source").GetString("")); - - if (camera.Name.isEmpty()) { - std::string path{table->GetPath()}; - QString qpath = QString::fromStdString(path); - camera.Name = qpath.split("/").last(); - } - - std::vector streams = table->GetEntry("streams").GetStringArray({}); - - for (const std::string &stream : streams) { - static QRegularExpression re("^(mjpe?g|ip|usb):"); - QString qstream = QString::fromStdString(stream); - qstream.replace(re, ""); - qstream.replace("/?action=stream", "/stream.mjpg?"); - - camera.Urls.append(QUrl(qstream)); - } - - return camera; -} - -Camera CameraStore::getCameraFromName(QString name) { - for (const Camera &camera : Cameras) { - if (camera.Name == name) { - return camera; - } - } - - throw std::runtime_error("Camera does not exist."); -} diff --git a/lib/src/stores/FilterStore.cpp b/lib/src/stores/FilterStore.cpp deleted file mode 100644 index c1fc5f09..00000000 --- a/lib/src/stores/FilterStore.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "stores/FilterStore.h" - -#include "Globals.h" - -QMap FilterStore::m_sendableTypeMap{}; -QMap FilterStore::m_ntTypeMap{}; - -QList FilterStore::FilteredTopics{}; -QList FilterStore::UnfilteredTopics{}; - -FilterStore::FilterStore() -{ - throw std::runtime_error("FilterStore is a static class."); -} - -void FilterStore::registerSendable(std::string typeString, TopicTypes topicType) { - m_sendableTypeMap.insert(typeString, topicType); - Globals::topicTypeDisplayNames.insert(topicType, QString::fromStdString(typeString)); -} - -void FilterStore::registerNTType(nt::NetworkTableType ntType, TopicTypes topicType, const QString &displayName) { - m_ntTypeMap.insert(ntType, topicType); - Globals::topicTypeDisplayNames.insert(topicType, displayName); -} - -std::optional FilterStore::sendableTypeForTypeString(std::string typeString) { - return m_sendableTypeMap.contains(typeString) ? std::optional(m_sendableTypeMap.value(typeString)) : std::nullopt; -} - -std::optional FilterStore::topicTypeForNTType(nt::NetworkTableType ntType) { - return m_ntTypeMap.contains(ntType) ? std::optional(m_ntTypeMap.value(ntType)) : std::nullopt; -} - -void FilterStore::filterTopics() { - for (const QString &topic : Globals::ntTopics) { - sortTopic(topic); - } - - // delete unused topics - for (const Globals::Topic &topic : FilteredTopics) { - if (!FilteredTopics.contains(topic)) return; - - QStringList split = topic.Name.split('/'); - Globals::Topic supertable(topic); - supertable.Name = split.sliced(0, split.length() - 1).join('/'); - - const QString name = topic.Name; - - // account for sendables - if (FilteredTopics.contains(supertable)) { - FilteredTopics.removeAll(topic); - } - - if ( - (!Globals::ntTopics.contains(name) && - !Globals::ntTopics.contains(name + ("/.type")))) { - FilteredTopics.removeAll(topic); - UnfilteredTopics.removeAll(topic); - } - } -} - -void FilterStore::sortTopic(QString topic) { - std::string topicName = topic.toStdString(); - // TopicTypes topicType; - - nt::NetworkTableEntry entry = Globals::inst.GetEntry(topicName); - - if (!entry.GetValue().IsValid()) { - return; - } - - QStringList split = QString::fromStdString(topicName).split('/'); - QString supertable = split.sliced(0, split.length() - 1).join('/'); - - nt::NetworkTableEntry typeEntry; - std::shared_ptr table = Globals::inst.GetTable(supertable.toStdString()); - - if (!supertable.isEmpty()) { - typeEntry = table->GetEntry(".type"); - } else { - typeEntry = Globals::inst.GetEntry(""); - } - - std::string value = typeEntry.GetString(""); - - std::optional sendableType = sendableTypeForTypeString(value); - std::optional ntType = topicTypeForNTType(entry.GetType()); - if (ntType == std::nullopt) ntType = {TopicTypes::String}; - - Globals::Topic ntTopic{topic, ntType.value()}; - - if (sendableType != std::nullopt) { - Globals::Topic sendableTopic{supertable, sendableType.value()}; - if (!FilteredTopics.contains(sendableTopic)) FilteredTopics.append(sendableTopic); - } else { - if (!FilteredTopics.contains(ntTopic)) FilteredTopics.append(ntTopic); - } - - if (!UnfilteredTopics.contains(ntTopic)) UnfilteredTopics.append(ntTopic); - - entry.Unpublish(); - typeEntry.Unpublish(); -} - -Globals::Topic FilterStore::topicFromName(const QString &topicName, const QList &topics) { - for (const Globals::Topic & topic : topics) { - if (topic.Name == topicName) { - return topic; - } - } - - return Globals::Topic{topicName, TopicTypes::String}; -} - -// please coroutines -QStringList FilterStore::topicNames(QList list) { - QStringList strings{}; - for (const Globals::Topic &topic : list) { - strings.append(topic.Name); - } - return strings; -} - -QStringList FilterStore::topicNames(QList list) { - QStringList strings{}; - for (const Globals::NumberTopic &topic : list) { - strings.append(topic.Name); - } - return strings; -} diff --git a/lib/src/stores/TopicStore.cpp b/lib/src/stores/TopicStore.cpp deleted file mode 100644 index c251f762..00000000 --- a/lib/src/stores/TopicStore.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "stores/TopicStore.h" - -#include "qtimer.h" -#include "widgets/BaseWidget.h" - -#include "Globals.h" - -QList TopicStore::Listeners{}; -QHash TopicStore::topicEntryMap{}; - -TopicStore::TopicStore() -{ - throw std::runtime_error("TopicStore is a static class."); -} - -bool TopicStore::hasEntry(std::string topic) { - for (const Listener &listener : Listeners) { - if (topic == listener.topic) { - return true; - } - } - return false; -} - -bool TopicStore::hasEntry(QString topic) { - return hasEntry(topic.toStdString()); -} - -bool TopicStore::widgetSubscribed(std::string topic, BaseWidget *subscriber) { - return !getEntry(topic, subscriber).isNull; -} - -bool TopicStore::widgetSubscribed(QString topic, BaseWidget *subscriber) { - return !getEntry(topic, subscriber).isNull; -} - -Listener TopicStore::getEntry(std::string topic, BaseWidget *subscriber) { - for (Listener listener : Listeners) { - if (listener.topic == topic && listener.subscriber == subscriber) { - return listener; - } - } - - Listener l; - l.isNull = true; - return l; -} - -Listener TopicStore::getEntry(QString topic, BaseWidget *subscriber) { - return getEntry(topic.toStdString(), subscriber); -} - -bool Listener::operator==(const Listener &other) const { - return (other.topic == this->topic) && - (other.entry == this->entry) && - (other.label == this->label) && - (other.subscriber == this->subscriber) && - (other.listenerHandle == this->listenerHandle) && - (other.desiredType == this->desiredType) && - (other.isNull == this->isNull); -} - -nt::NetworkTableEntry TopicStore::subscribe(std::string ntTopic, BaseWidget *subscriber, NT_Type desiredType, QString label, bool writeOnly) { - Listener listener; - nt::NetworkTableEntry entry; - if (!hasEntry(ntTopic)) { - entry = Globals::inst.GetEntry(ntTopic); - - topicEntryMap.insert(ntTopic, entry); - } else { - entry = topicEntryMap.value(ntTopic); - } - - listener = { - ntTopic, - label, - entry, - subscriber, - 0, - nt::ListenerCallback(), - desiredType, - false - }; - - if (!writeOnly) { - nt::ListenerCallback updateWidget = [entry, subscriber, desiredType, label, ntTopic](const nt::Event &event = nt::Event()) { - if (!subscriber) { - return; - } - - nt::Value value; - if (!event.Is(nt::EventFlags::kValueAll)) { - value = entry.GetValue(); - } else { - value = event.GetValueEventData()->value; - } - - // ensure thread-safety - // this is mild anal cancer - if (value.IsValid()) { - QMetaObject::invokeMethod(subscriber, [subscriber, value, label, desiredType, ntTopic] { - if (value.type() != desiredType) { - qCritical() << "Type for topic" << - QString::fromStdString(ntTopic) << - "for widget" << - subscriber->title() << - "is of incorrect type" << - (int) value.type() << - "; expected" << - (int) desiredType; - return; - } - - if (subscriber->ready()) { - if (subscriber->isEnabled()) { - subscriber->setValue(value, label); - subscriber->update(); - } - } else { - connect(subscriber, &BaseWidget::isReady, subscriber, [subscriber, value, label] { - subscriber->setValue(value, label); - subscriber->update(); - }, Qt::SingleShotConnection); - } - }); // QMetaObject and its consequences have been a disaster for the human race - } - }; - - NT_Listener handle = Globals::inst.AddListener(Globals::inst.GetEntry(ntTopic), nt::EventFlags::kValueAll, updateWidget); - - listener.listenerHandle = handle; - listener.callback = updateWidget; - - QTimer::singleShot(1000, subscriber, [updateWidget] { - updateWidget(nt::Event()); - }); - } - - Listeners.append(listener); - - return entry; -} - -void TopicStore::unsubscribe(std::string ntTopic, BaseWidget *subscriber) { - if (!hasEntry(ntTopic) || !widgetSubscribed(ntTopic, subscriber)) return; - - Listener listener = getEntry(ntTopic, subscriber); - Listeners.removeOne(listener); - - if (!hasEntry(ntTopic)) { - listener.entry.Unpublish(); - topicEntryMap.remove(ntTopic); - } - - Globals::inst.RemoveListener(listener.listenerHandle); -} - -void TopicStore::unsubscribe(QString ntTopic, BaseWidget *subscriber) { - unsubscribe(ntTopic.toStdString(), subscriber); -} - -void TopicStore::unsubscribe(nt::NetworkTableEntry entry, BaseWidget *subscriber) { - unsubscribe(entry.GetName(), subscriber); -} - -double TopicStore::getDoubleFromEntry(nt::NetworkTableEntry entry) { - nt::Value value = entry.GetValue(); - - if (value.IsBoolean()) { - return (double) entry.GetBoolean(0); - } else if (value.IsDouble()) { - return entry.GetDouble(0.); - } else if (value.IsInteger()) { - return (double) entry.GetInteger(0); - } - - return 0.; -} - -void TopicStore::updateTopic(std::string topic, BaseWidget *subscriber, QString label) { - Listener l = getEntry(topic, subscriber); - if (l.isNull) return; - - nt::ListenerCallback callback = l.callback; - callback(nt::Event()); -} diff --git a/lib/src/stores/TypeStore.cpp b/lib/src/stores/TypeStore.cpp deleted file mode 100644 index f7a8cde2..00000000 --- a/lib/src/stores/TypeStore.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "stores/TypeStore.h" -#include "widgets/BaseWidget.h" - -#include - -TypeStore::TypeStore() -{ - // throw std::runtime_error("TypeStore is a static class."); -} - -void TypeStore::registerType(TopicTypes topicType, WidgetTypes widgetType, QString displayName) { - m_typeWidgetMap.insert(topicType, widgetType); - m_widgetNameMap.insert(widgetType, displayName); -} - -QList TypeStore::generateActionsForTopic(Globals::Topic topic) { - QList actions{}; - - QList widgetTypes = m_typeWidgetMap.values(topic.Type); - - for (WidgetTypes widgetType : widgetTypes) { - QString displayName = widgetDisplayName(widgetType); - - QAction *action = new QAction(displayName); - - connect(action, &QAction::triggered, action, [this, widgetType, displayName, topic] { - auto widget = BaseWidget::defaultWidgetFromTopic(topic.Name, widgetType); - - emit widgetReady(widget, WidgetData{0, 0, 1, 1}); - }); - - actions.append(action); - } - - return actions; -} - -QMenu *TypeStore::generateMenuForTopic(Globals::Topic topic) { - QMenu *menu = new QMenu((QWidget *) parent()); - - menu->addActions(generateActionsForTopic(topic)); - - return menu; -} - -QString TypeStore::widgetDisplayName(WidgetTypes type) { - return m_widgetNameMap.value(type, ""); -} - -void TypeStore::emitWidget(BaseWidget *widget, WidgetData data) { - emit widgetReady(widget, data); -} diff --git a/lib/src/widgets/BaseWidget.cpp b/lib/src/widgets/BaseWidget.cpp deleted file mode 100644 index 0c3f97e2..00000000 --- a/lib/src/widgets/BaseWidget.cpp +++ /dev/null @@ -1,565 +0,0 @@ -#include "widgets/BaseWidget.h" -#include "Globals.h" -#include "stores/TopicStore.h" - -#include "widgets/BooleanCheckboxWidget.h" -#include "widgets/BooleanDisplayWidget.h" -#include "widgets/CameraViewWidget.h" -#include "widgets/DoubleDialWidget.h" -#include "widgets/EnumWidget.h" -#include "widgets/DoubleDisplayWidget.h" -#include "widgets/StringChooserWidget.h" -#include "widgets/StringDisplayWidget.h" -#include "widgets/IntegerDisplayWidget.h" -#include "widgets/IntegerDialWidget.h" -#include "widgets/FieldWidget.h" -#include "widgets/SendableFieldWidget.h" -#include "widgets/CommandWidget.h" -#include "widgets/GraphWidget.h" -#include "widgets/FMSInfoWidget.h" -#include "widgets/SwerveWidget.h" - -#include "dialogs/WidgetDialogGenerator.h" - -#include -#include -#include -#include -#include -#include -#include - -BaseWidget::BaseWidget(const WidgetTypes &type, const QString &title, const QString &topic, const bool sendable) - : QFrame() -{ - setFocusPolicy(Qt::ClickFocus); - - m_type = type; - m_topic = topic; - m_sendable = sendable; - - m_layout = new QGridLayout(this); - m_title = new QLineEdit(title, this); - - m_layout->addWidget(m_title, 0, 0); - - setStyleSheet("BaseWidget { background-color: " + qApp->palette().color(QPalette::ColorRole::Base).darker(150).name() + "; border: 1px solid white; color: white; }"); - m_title->setStyleSheet("QLineEdit { border: none; border-bottom: 2px solid white; }"); - - this->setFrameShape(QFrame::Panel); - this->setFrameShadow(QFrame::Raised); - - QFont titleFont = this->titleFont(); - titleFont.setBold(true); - setTitleFont(titleFont); - - m_title->setAlignment(Qt::AlignHCenter); - - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - setMouseTracking(true); - setContentsMargins(5, 5, 5, 5); -} - -BaseWidget::~BaseWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -bool BaseWidget::ready() { - return m_ready; -} - -void BaseWidget::setReady(bool ready) { - m_ready = ready; - if (ready) emit isReady(); -} - -QFont BaseWidget::titleFont() -{ - return m_title->font(); -} - -void BaseWidget::setTitleFont(const QFont &font) -{ - m_title->setFont(font); -} - -QString BaseWidget::title() -{ - return m_title->text(); -} - -void BaseWidget::setTitle(const QString &title) -{ - m_title->setText(title); -} - -QString BaseWidget::topic() -{ - return m_topic; -} - -void BaseWidget::setTopic(const QString &topic) {} - -ResizeDirection BaseWidget::resizing() { - return m_resize; -} - -void BaseWidget::setResizing(ResizeDirection direction) { - m_resize = direction; -} - -QMenu *BaseWidget::constructContextMenu(WidgetData data) { - QMenu *menu = new QMenu(this); - - QAction *resizeAction = new QAction("Resize", menu); - - menu->addAction(resizeAction); - - connect(resizeAction, &QAction::triggered, this, [this, data](bool) { - WidgetDialogGenerator *dialog = new WidgetDialogGenerator(this, this, true, data); - dialog->setWindowTitle("Resize Widget"); - dialog->show(); - - connect(dialog, &WidgetDialogGenerator::widgetReady, this, [this](BaseWidget *widget, WidgetData data) { - emit reconfigRequested(widget, data); - }); - }); - - QAction *deleteAction = new QAction("Delete Widget", menu); - menu->addAction(deleteAction); - - connect(deleteAction, &QAction::triggered, this, [this](bool) { - emit deleteRequested(); - }); - - QAction *titleFontAction = new QAction("Title Font", menu); - menu->addAction(titleFontAction); - - connect(titleFontAction, &QAction::triggered, this, [this](bool) { - setTitleFont(QFontDialog::getFont(0, titleFont(), this, "Set Title Font")); - }); - - return menu; -} - -void BaseWidget::setValue(const nt::Value &value, QString label, bool force) {} - -void BaseWidget::forceUpdate() { - if (m_sendable) return setValue(nt::Value{}, "", true); - TopicStore::updateTopic(m_topic.toStdString(), this, ""); -} - -void BaseWidget::setConnected(bool connected) { - m_connected = connected; -} - -void BaseWidget::paintEvent(QPaintEvent *event) { - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - - QWidget::paintEvent(event); -} - -QJsonObject BaseWidget::saveObject() { - QJsonObject object{}; - - object.insert("widgetType", (int) m_type); - - int offset = BaseWidget::staticMetaObject.propertyOffset(); - int propertyCount = metaObject()->propertyCount(); - - for (int i = offset; i < propertyCount; ++i) { - QMetaProperty property = metaObject()->property(i); - QString propertyName(property.name()); - - int id = property.typeId(); - -#define PROPERTY_FUNCTION(type, function) if (id == type) object.insert(propertyName, function(property)); else - - PROPERTY_FUNCTION(QMetaType::Double, writeDoubleProperty) - PROPERTY_FUNCTION(QMetaType::Int, writeIntProperty) - PROPERTY_FUNCTION(QMetaType::Bool, writeBoolProperty) - PROPERTY_FUNCTION(QMetaType::QColor, writeColorProperty) - PROPERTY_FUNCTION(QMetaType::QVariantMap, writeMapProperty) - PROPERTY_FUNCTION(QMetaType::QVariantList, writeListProperty) - PROPERTY_FUNCTION(CustomMetaTypes::File, writeFileProperty) - PROPERTY_FUNCTION(QMetaType::QFont, writeFontProperty) - PROPERTY_FUNCTION(QMetaType::QString, writeStringProperty) - PROPERTY_FUNCTION(QMetaType::QUrl, writeStringProperty) - PROPERTY_FUNCTION(CustomMetaTypes::FrameShape, writeShapeProperty) - PROPERTY_FUNCTION(CustomMetaTypes::NumberTopicList, writeTopicListProperty) - PROPERTY_FUNCTION(CustomMetaTypes::XAxis, writeXAxisProperty) - PROPERTY_FUNCTION(CustomMetaTypes::NumberTopicColorMap, writeNumberTopicColorMapProperty) - PROPERTY_FUNCTION(CustomMetaTypes::DoubleArrayTopic, writeDoubleArrayTopicProperty) - - { // else - qCritical() << "Bad metatype for property" << property.name() << (QMetaType::Type) id; - continue; - } // else - -#undef PROPERTY_FUNCTION - } - - return object; -} - -WidgetData BaseWidget::fromJson(QJsonObject obj) { - int offset = staticMetaObject.propertyOffset(); - int propertyCount = metaObject()->propertyCount(); - - for (int i = offset; i < propertyCount; ++i) { - QMetaProperty property = metaObject()->property(i); - QString propertyName(property.name()); - if (!obj.contains(propertyName)) { - continue; - } - - QJsonValue value = obj.value(propertyName); - int id = property.typeId(); - -#define PROPERTY_FUNCTION(type, function) if (id == type) property.write(this, function(property, value)); else - - PROPERTY_FUNCTION(QMetaType::Double, readDoubleProperty) - PROPERTY_FUNCTION(QMetaType::Int, readIntProperty) - PROPERTY_FUNCTION(QMetaType::Bool, readBoolProperty) - PROPERTY_FUNCTION(QMetaType::QColor, readColorProperty) - PROPERTY_FUNCTION(QMetaType::QVariantMap, readMapProperty) - PROPERTY_FUNCTION(QMetaType::QVariantList, readListProperty) - PROPERTY_FUNCTION(CustomMetaTypes::File, readFileProperty) - PROPERTY_FUNCTION(QMetaType::QFont, readFontProperty) - PROPERTY_FUNCTION(QMetaType::QString, readStringProperty) - PROPERTY_FUNCTION(QMetaType::QUrl, readStringProperty) - PROPERTY_FUNCTION(CustomMetaTypes::FrameShape, readShapeProperty) - PROPERTY_FUNCTION(CustomMetaTypes::NumberTopicList, readTopicListProperty) - PROPERTY_FUNCTION(CustomMetaTypes::XAxis, readXAxisProperty) - PROPERTY_FUNCTION(CustomMetaTypes::NumberTopicColorMap, readNumberTopicColorMapProperty) - PROPERTY_FUNCTION(CustomMetaTypes::DoubleArrayTopic, readDoubleArrayTopicProperty) - { // else - qCritical() << "Bad metatype for property" << property.name() << (QMetaType::Type) id; - continue; - } // else - -#undef PROPERTY_FUNCTION - } - - QJsonArray geometry = obj.value("geometry").toArray({}); - - WidgetData data; - data.row = geometry.at(0).toInt(0); - data.col = geometry.at(1).toInt(0); - data.rowSpan = geometry.at(2).toInt(1); - data.colSpan = geometry.at(3).toInt(1); - - m_ready = true; - - return data; -} - -BaseWidget *BaseWidget::defaultWidgetFromTopic(QString ntTopic, WidgetTypes type) { - // wahoo - auto register_widget_types = [&ntTopic, &type]() -> BaseWidget * { - BaseWidget *result = nullptr; - QString topLevel = ntTopic.split("/").last(); - auto try_widget_type = [&]() -> bool { - if (WidgetType::WidgetType == type) - { - result = new WidgetType(ntTopic); - result->setTitle(topLevel); - return true; - } - return false; - }; - (try_widget_type.template operator()() || ...); - return (result ? result : new StringDisplayWidget(ntTopic, "", topLevel)); - }; - - auto widget = register_widget_types.template operator()(); - - return widget; -} - -// JSON stuff -QVariant BaseWidget::readDoubleProperty(const QMetaProperty &property, const QJsonValue &value) { - return value.toDouble(property.read(this).toDouble()); -} - -QVariant BaseWidget::readIntProperty(const QMetaProperty &property, const QJsonValue &value) { - return value.toInt(property.read(this).toInt()); -} - -QVariant BaseWidget::readBoolProperty(const QMetaProperty &property, const QJsonValue &value) { - return value.toBool(property.read(this).toBool()); -} - -QVariant BaseWidget::readColorProperty(const QMetaProperty &property, const QJsonValue &value) { - return QColor(value.toString(property.read(this).value().name())); -} - -QVariant BaseWidget::readMapProperty(const QMetaProperty &property, const QJsonValue &value) { - QVariantMap map = value.toObject().toVariantMap(); - return map.empty() ? property.read(this).toMap() : map; -} - -QVariant BaseWidget::readListProperty(const QMetaProperty &property, const QJsonValue &value) { - QVariantList list = value.toArray().toVariantList(); - return list.empty() ? property.read(this).toList() : list; -} - -QVariant BaseWidget::readFileProperty(const QMetaProperty &property, const QJsonValue &value) { - return QVariant::fromValue(Globals::File{value.toString(property.read(this).value().fileName)}); -} - -QVariant BaseWidget::readFontProperty(const QMetaProperty &property, const QJsonValue &value) { - QFont font; - font.fromString(value.toString(property.read(this).value())); - - return QVariant::fromValue(font); -} - -QVariant BaseWidget::readStringProperty(const QMetaProperty &property, const QJsonValue &value) { - return value.toString(property.read(this).toString()); -} - -QVariant BaseWidget::readShapeProperty(const QMetaProperty &property, const QJsonValue &value) { - return QVariant::fromValue(Globals::shapeNameMap.value( - value.toString( - Globals::shapeNameMap.key(property.read(this).value())))); -} - -QVariant BaseWidget::readTopicListProperty(const QMetaProperty &property, const QJsonValue &value) { - QJsonArray array = value.toArray({}); - QList list{}; - - for (QJsonValueRef ref : array) { - QJsonObject obj = ref.toObject(); - Globals::NumberTopic topic{ - obj.value("name").toString(), - (TopicTypes) obj.value("type").toInt() - }; - - list.append(topic); - } - - return QVariant::fromValue>(list); -} - -QVariant BaseWidget::readXAxisProperty(const QMetaProperty &property, const QJsonValue &value) { - QJsonObject obj = value.toObject({}); - Globals::GraphXAxis current = property.read(this).value(); - - return QVariant::fromValue( - Globals::GraphXAxis{ - obj.value("useTime").toBool(current.useTime), - obj.value("topic").toString(current.topic) - }); -} - -QVariant BaseWidget::readNumberTopicColorMapProperty(const QMetaProperty &property, const QJsonValue &value) { - QJsonArray array = value.toArray(); - - QHash map{}; - - for (const QJsonValueRef &ref : array) { - QJsonObject obj = ref.toObject(); - QJsonObject topicObj = obj.value("topic").toObject(); - Globals::NumberTopic topic { - topicObj.value("name").toString(), - (TopicTypes) topicObj.value("type").toInt() - }; - - QColor color = obj.value("color").toString(""); - - map.insert(topic, color); - } - - return QVariant::fromValue(map); -} - -QVariant BaseWidget::readDoubleArrayTopicProperty(const QMetaProperty &property, const QJsonValue &value) { - Globals::DoubleArrayTopic topic(value.toString()); - - return QVariant::fromValue(topic); -} - - -// Write -QJsonValue BaseWidget::writeDoubleProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).toDouble()); -} - -QJsonValue BaseWidget::writeIntProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).toInt()); -} - -QJsonValue BaseWidget::writeBoolProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).toBool()); -} - -QJsonValue BaseWidget::writeColorProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).value().name()); -} - -QJsonValue BaseWidget::writeMapProperty(const QMetaProperty &property) { - return QJsonValue( - QJsonObject::fromVariantMap( - property.read(this).toMap())); -} - -QJsonValue BaseWidget::writeListProperty(const QMetaProperty &property) { - return QJsonValue( - QJsonArray::fromVariantList( - property.read(this).toList())); -} - -QJsonValue BaseWidget::writeFileProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).value().fileName); -} - -QJsonValue BaseWidget::writeFontProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).value().toString()); -} - -QJsonValue BaseWidget::writeStringProperty(const QMetaProperty &property) { - return QJsonValue(property.read(this).toString()); -} - -QJsonValue BaseWidget::writeShapeProperty(const QMetaProperty &property) { - return QJsonValue(Globals::shapeNameMap.key(property.read(this).value())); -} - -QJsonValue BaseWidget::writeTopicListProperty(const QMetaProperty &property) { - QJsonArray array{}; - QList list = property.read(this).value>(); - - for (const Globals::NumberTopic &topic : list) { - array.append( - QJsonObject{ - {"name", topic.Name}, - {"type", (int) topic.Type} - }); - } - - return array; -} - -QJsonValue BaseWidget::writeXAxisProperty(const QMetaProperty &property) { - Globals::GraphXAxis xAxis = property.read(this).value(); - - return QJsonObject( - { - {"useTime", xAxis.useTime}, - {"topic", xAxis.topic} - } - ); -} - -QJsonValue BaseWidget::writeNumberTopicColorMapProperty(const QMetaProperty &property) { - QJsonArray array{}; - QHash map = property.read(this).value>(); - - QHashIterator iter(map); - - while (iter.hasNext()) { - iter.next(); - - Globals::NumberTopic topic = iter.key(); - QColor color = iter.value(); - - QJsonObject obj{}; - QJsonObject topicObj{ - {"name", topic.Name}, - {"type", (int) topic.Type} - }; - - obj.insert("topic", topicObj); - obj.insert("color", color.name()); - array.append(obj); - } - - return array; -} - -QJsonValue BaseWidget::writeDoubleArrayTopicProperty(const QMetaProperty &property) { - QJsonValue value{property.read(this).value().Name}; - - return value; -} - -// etc - -void BaseWidget::mouseMoveEvent(QMouseEvent *event) { - QRect rect = this->rect(); - QRect left(rect.topLeft(), rect.bottomLeft()); - QRect right(rect.topRight(), rect.bottomRight()); - QRect top(rect.topRight(), rect.topLeft()); - QRect bottom(rect.bottomRight(), rect.bottomLeft()); - - QRect pointerRect = QRect(event->position().toPoint() - QPoint(10, 10), QSize(20, 20)); - - QCursor qcursor; - Qt::CursorShape shape = Qt::ArrowCursor; - - ResizeDirection direction = NONE; - -#define ONE_EDGE(edge, edgeDirection) if (edge.intersects(pointerRect)) { direction |= edgeDirection; } - - ONE_EDGE(left, LEFT) - ONE_EDGE(right, RIGHT) - ONE_EDGE(top, TOP) - ONE_EDGE(bottom, BOTTOM) - -#undef ONE_EDGE - - switch (direction) { - case LEFT: - case RIGHT: - shape = Qt::SizeHorCursor; - break; - case TOP: - case BOTTOM: - shape = Qt::SizeVerCursor; - break; - case TOP | RIGHT: - case BOTTOM | LEFT: - shape = Qt::SizeBDiagCursor; - break; - case TOP | LEFT: - case BOTTOM | RIGHT: { - shape = Qt::SizeFDiagCursor; - break; - } - default: {} - } - - if (shape != qcursor.shape()) { - m_resize = direction; - qcursor.setShape(shape); - setCursor(qcursor); - } else { - m_resize = NONE; - } - - event->ignore(); -} - -void BaseWidget::mousePressEvent(QMouseEvent *event) { - QWidget::mousePressEvent(event); - mouseMoveEvent(event); - event->ignore(); -} diff --git a/lib/src/widgets/BooleanCheckboxWidget.cpp b/lib/src/widgets/BooleanCheckboxWidget.cpp deleted file mode 100644 index 36587fef..00000000 --- a/lib/src/widgets/BooleanCheckboxWidget.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "widgets/BooleanCheckboxWidget.h" -#include "stores/TopicStore.h" - -BooleanCheckboxWidget::BooleanCheckboxWidget(const QString &topic, const bool &defaultValue, const QString &title) : BaseWidget(WidgetTypes::BooleanCheckbox, title, topic) -{ - setTopic(topic); - m_value = defaultValue; - - m_checkbox = new QCheckBox(this); - m_checkbox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - m_checkbox->setStyleSheet("QCheckBox::indicator:unchecked { width: 30px; height: 30px; } QCheckBox::indicator:checked { width: 30px; height: 30px; }"); - - m_layout->addWidget(m_checkbox, 1, 0, 3, 1, Qt::AlignHCenter); - - connect(m_checkbox, &QCheckBox::stateChanged, this, [this](int state) { - m_entry.SetBoolean(state == Qt::Checked); - m_value = state == Qt::Checked; - }); - - setReady(true); -} - -BooleanCheckboxWidget::~BooleanCheckboxWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -int BooleanCheckboxWidget::checkboxSize() { - return m_checkboxSize; -} - -void BooleanCheckboxWidget::setCheckboxSize(int size) { - m_checkboxSize = size; - - m_checkbox->setStyleSheet(QString("QCheckBox::indicator:unchecked { width: %1px; height: %1px; } QCheckBox::indicator:checked { width: %1px; height: %1px; }").arg(size)); -} - -void BooleanCheckboxWidget::setTopic(const QString &topic) { - - - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_BOOLEAN); -} - -void BooleanCheckboxWidget::setValue(const nt::Value &value, QString label, bool force) { - m_value = value.GetBoolean(); - - m_checkbox->setCheckState(m_value ? Qt::Checked : Qt::Unchecked); -} diff --git a/lib/src/widgets/BooleanDisplayWidget.cpp b/lib/src/widgets/BooleanDisplayWidget.cpp deleted file mode 100644 index c3b472fa..00000000 --- a/lib/src/widgets/BooleanDisplayWidget.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "widgets/BooleanDisplayWidget.h" -#include "stores/TopicStore.h" - -BooleanDisplayWidget::BooleanDisplayWidget(const QString &topic, const bool &defaultValue, const QString &title) : BaseWidget(WidgetTypes::BooleanDisplay, title, topic) -{ - setTopic(topic); - - m_value = defaultValue; - m_colorWidget = new ShapedFrame(Globals::FrameShape::Rectangle, this); - - m_layout->addWidget(m_colorWidget, 1, 0, 3, 1); - setReady(true); -} - -BooleanDisplayWidget::~BooleanDisplayWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -QColor BooleanDisplayWidget::trueColor() { - return m_trueColor; -} - -void BooleanDisplayWidget::setTrueColor(const QColor &color) { - m_trueColor = color; -} - -QColor BooleanDisplayWidget::falseColor() { - return m_falseColor; -} - -void BooleanDisplayWidget::setFalseColor(const QColor &color) { - m_falseColor = color; -} - -Globals::FrameShape BooleanDisplayWidget::shape() { - return m_shape; -} - -void BooleanDisplayWidget::setShape(Globals::FrameShape shape) { - m_shape = shape; - m_colorWidget->setShape(shape); -} - -void BooleanDisplayWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_BOOLEAN); -} - -void BooleanDisplayWidget::setValue(const nt::Value &value, QString label, bool force) { - m_value = value.GetBoolean(); - - m_colorWidget->setColor(m_value ? m_trueColor : m_falseColor); -} diff --git a/lib/src/widgets/CameraViewWidget.cpp b/lib/src/widgets/CameraViewWidget.cpp deleted file mode 100644 index 9815f8b7..00000000 --- a/lib/src/widgets/CameraViewWidget.cpp +++ /dev/null @@ -1,59 +0,0 @@ - #include "widgets/CameraViewWidget.h" - -#include -#include - -CameraViewWidget::CameraViewWidget(const QString &title, const QUrl &url) : BaseWidget(WidgetTypes::CameraView, title, "", true) -{ - m_videoWidget = new QVideoWidget(this); - - m_player = new QMediaPlayer(this); - m_player->setVideoOutput(m_videoWidget); - - setUrl(url); - - m_layout->addWidget(m_videoWidget, 1, 0, 4, 1); - setReady(true); -} - -CameraViewWidget::~CameraViewWidget() { - m_player->stop(); -} - -QUrl CameraViewWidget::url() { - return m_player->source(); -} - -void CameraViewWidget::setUrl(const QUrl &url) { - m_url = url; - - m_player->stop(); - - QThread::msleep(100); - - m_player->setSource(url); - m_player->play(); -} - -void CameraViewWidget::forceUpdate() { - m_player->play(); -} - -void CameraViewWidget::setConnected(bool connected) { - if (connected) setUrl(m_url); - else m_player->stop(); -} - -QMenu *CameraViewWidget::constructContextMenu(WidgetData data) { - QMenu *menu = BaseWidget::constructContextMenu(data); - - QAction *reconnect = new QAction("Reconnect", menu); - - connect(reconnect, &QAction::triggered, this, [this] { - setUrl(m_url); - }); - - menu->addAction(reconnect); - - return menu; -} diff --git a/lib/src/widgets/CommandWidget.cpp b/lib/src/widgets/CommandWidget.cpp deleted file mode 100644 index 78e911fb..00000000 --- a/lib/src/widgets/CommandWidget.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "widgets/CommandWidget.h" -#include "Globals.h" -#include "stores/TopicStore.h" - -#include - -CommandWidget::CommandWidget(const QString &topic, const QString &title) : BaseWidget(WidgetTypes::Command, title, topic, true) -{ - setTopic(topic); - - m_button = new QPushButton(QString::fromStdString(m_name.GetString("Command")), this); - - connect(m_button, &QPushButton::clicked, this, [this](bool) { - bool value = m_running.GetBoolean(false); - m_running.SetBoolean(!value); - }); - - m_layout->addWidget(m_button, 1, 0, 3, 1); - - setReady(true); -} - -CommandWidget::~CommandWidget() { - TopicStore::unsubscribe(m_name, this); - TopicStore::unsubscribe(m_running, this); -} - -void CommandWidget::setTopic(const QString &topic) { - m_topic = topic; - - TopicStore::unsubscribe(topic.toStdString() + "/.name", this); - TopicStore::unsubscribe(topic.toStdString() + "/.running", this); - - m_name = TopicStore::subscribe(topic.toStdString() + "/.name", this, NT_STRING, "Name"); - m_running = TopicStore::subscribe(topic.toStdString() + "/running", this, NT_BOOLEAN, "Running", true); -} - -void CommandWidget::setValue(const nt::Value &value, QString label, bool force) { - if (force) { - QMap map{}; - map.insert("/.name", "Name"); - - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - TopicStore::updateTopic(m_topic.toStdString() + iter.key(), this, iter.value()); - } - - return; - } - - if (label == "Name") { - m_button->setText(QString::fromStdString(std::string{value.GetString()})); - } -} diff --git a/lib/src/widgets/DoubleDialWidget.cpp b/lib/src/widgets/DoubleDialWidget.cpp deleted file mode 100644 index e8305209..00000000 --- a/lib/src/widgets/DoubleDialWidget.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "widgets/DoubleDialWidget.h" -#include "stores/TopicStore.h" - -#include -#include - -DoubleDialWidget::DoubleDialWidget(const QString &topic, const double &defaultValue, const QString &title) : DoubleDisplayWidget(topic, defaultValue, title, false) { - setTopic(topic); - - m_dial = new BetterDial(this); - m_type = WidgetTypes::DoubleDial; - - m_fakeValue = defaultValue * 100; - - m_dial->setMin(0); - m_dial->setMax(36000); - - m_layout->removeWidget(m_text); - - m_layout->addWidget(m_dial, 1, 0, 2, 1); - m_layout->addWidget(m_text, 3, 0); - - connect(m_dial, &BetterDial::sliderMoved, this, [this](int position) { - m_entry.SetDouble(position / 100.); - m_text->setText(QString::number(position / 100.)); - }); - setReady(true); -} - -DoubleDialWidget::~DoubleDialWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -double DoubleDialWidget::min() { - return m_min; -} - -void DoubleDialWidget::setMin(double min) { - m_min = min; - m_dial->setMin(min * 100.); -} - -double DoubleDialWidget::max() { - return m_max; -} - -void DoubleDialWidget::setMax(double max) { - m_max = max; - m_dial->setMax(max * 100.); -} - -double DoubleDialWidget::startingAngle() { - return m_startingAngle; -} - -void DoubleDialWidget::setStartingAngle(double angle) { - m_startingAngle = angle; - m_dial->setStartingAngle(angle * M_PI / 180.); -} - -void DoubleDialWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_DOUBLE); -} - -void DoubleDialWidget::setValue(const nt::Value &value, QString label, bool force) { - if (!m_text->hasFocus()) { - m_value = value.GetDouble(); - - m_fakeValue = m_value * 100; - setText(QString::number(m_value)); - - if (m_dial == nullptr) return; - if (!m_dial->isDragging()) m_dial->setValue(m_fakeValue); - } -} - -void DoubleDialWidget::keyPressEvent(QKeyEvent *event) { - if (m_text->hasFocus()) { - m_entry.SetDouble(m_text->text().toDouble()); - m_value = m_text->text().toDouble(); - m_fakeValue = m_value * 100.; - - m_dial->setValue(m_fakeValue); - } else { - event->ignore(); - } -} diff --git a/lib/src/widgets/DoubleDisplayWidget.cpp b/lib/src/widgets/DoubleDisplayWidget.cpp deleted file mode 100644 index 29b3c2c9..00000000 --- a/lib/src/widgets/DoubleDisplayWidget.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "widgets/DoubleDisplayWidget.h" -#include "stores/TopicStore.h" - -#include -#include - -DoubleDisplayWidget::DoubleDisplayWidget(const QString &topic, const double &defaultValue, const QString &title, const bool &ready) : TextWidget(WidgetTypes::DoubleDisplay, topic, QString::number(defaultValue), title) -{ - setTopic(topic); - - m_value = defaultValue; - setReady(ready); -} - -DoubleDisplayWidget::~DoubleDisplayWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -void DoubleDisplayWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_DOUBLE); -} - -void DoubleDisplayWidget::setValue(const nt::Value &value, QString label, bool force) { - if (!m_text->hasFocus()) { - m_value = value.GetDouble(); - setText(QString::number(m_value)); - } -} - -void DoubleDisplayWidget::keyPressEvent(QKeyEvent *event) { - if (m_text->hasFocus()) { - m_entry.SetDouble(m_text->text().toDouble()); - m_value = m_text->text().toDouble(); - } else { - event->ignore(); - } -} - diff --git a/lib/src/widgets/EnumWidget.cpp b/lib/src/widgets/EnumWidget.cpp deleted file mode 100644 index c6adec23..00000000 --- a/lib/src/widgets/EnumWidget.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "widgets/EnumWidget.h" -#include "stores/TopicStore.h" - -EnumWidget::EnumWidget(const QString &topic, const QString &defaultValue, const QString &title) : BaseWidget(WidgetTypes::EnumWidget, title, topic) -{ - setTopic(topic); - - m_value = defaultValue; - m_colorWidget = new ShapedFrame(Globals::FrameShape::Rectangle, this); - - m_layout->addWidget(m_colorWidget, 1, 0, 3, 1); - setReady(true); -} - -EnumWidget::~EnumWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -QVariantMap EnumWidget::colors() { - return m_colors; -} - -void EnumWidget::setColors(QVariantMap colors) { - m_colors = colors; -} - -Globals::FrameShape EnumWidget::shape() { - return m_shape; -} - -void EnumWidget::setShape(Globals::FrameShape shape) { - m_shape = shape; - m_colorWidget->setShape(shape); -} - -void EnumWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_STRING); -} - -void EnumWidget::setValue(const nt::Value &value, QString label, bool force) { - m_value = QString::fromStdString(std::string(value.GetString())); - - if (m_colors.contains(m_value)) m_colorWidget->setColor(m_colors.value(m_value).value()); -} diff --git a/lib/src/widgets/FMSInfoWidget.cpp b/lib/src/widgets/FMSInfoWidget.cpp deleted file mode 100644 index e514c7bf..00000000 --- a/lib/src/widgets/FMSInfoWidget.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "widgets/FMSInfoWidget.h" - -#include "stores/TopicStore.h" - -QMap MatchTypeNames = { - {MatchType::Elimination, "Elimination"}, - {MatchType::None, "Unknown"}, - {MatchType::Practice, "Practice"}, - {MatchType::Qualification, "Qual"} -}; - -QMap StationNames = { - {AllianceStation::Invalid, "None"}, - {AllianceStation::Red1, "Red 1"}, - {AllianceStation::Red2, "Red 2"}, - {AllianceStation::Red3, "Red 3"}, - {AllianceStation::Blue1, "Blue 1"}, - {AllianceStation::Blue2, "Blue 2"}, - {AllianceStation::Blue3, "Blue 3"} -}; - -FMSInfoWidget::FMSInfoWidget(const QString &table, const QString &title) - : BaseWidget(WidgetTypes::FMSInfo, title, table, true) -{ - setTopic(table); - - m_matchLabel = new QLabel("Match: Unknown", this); - m_stationLabel = new QLabel("Station: Unknown", this); - m_eventLabel = new QLabel("Event: Unknown", this); - m_gsmLabel = new QLabel("Game Specific Message: Unknown", this); - - m_dsIconLabel = new QLabel(this); - m_dsLabel = new QLabel("DS Disconnected", this); - - m_fmsIconLabel = new QLabel(this); - m_fmsLabel = new QLabel("FMS Disconnected", this); - - m_controlLabel = new QLabel("Robot State: Unknown", this); - - for (QLabel *label : QList{ - m_matchLabel, - m_stationLabel, - m_eventLabel, - m_gsmLabel, - m_dsLabel, - m_dsLabel, - m_controlLabel - }) { - label->setScaledContents(true); - label->setWordWrap(true); - } - - m_layout->addWidget(m_title, 0, 0, 1, 2); - - m_layout->addWidget(m_matchLabel, 1, 0, 1, 2); - m_layout->addWidget(m_stationLabel, 2, 0, 1, 2); - m_layout->addWidget(m_eventLabel, 3, 0, 1, 2); - m_layout->addWidget(m_gsmLabel, 4, 0, 1, 2); - - m_layout->addWidget(m_dsIconLabel, 5, 0); - m_layout->addWidget(m_dsLabel, 5, 1); - - m_layout->addWidget(m_fmsIconLabel, 6, 0); - m_layout->addWidget(m_fmsLabel, 6, 1); - - m_layout->addWidget(m_controlLabel, 7, 0, 1, 2); - - m_layout->setColumnStretch(0, 0); - m_layout->setColumnStretch(1, 1); - - setReady(true); -} - -FMSInfoWidget::~FMSInfoWidget() { - TopicStore::unsubscribe(m_event, this); - TopicStore::unsubscribe(m_controlWord, this); - TopicStore::unsubscribe(m_gameSpecificMessage, this); - TopicStore::unsubscribe(m_redAlliance, this); - TopicStore::unsubscribe(m_matchNumber, this); - TopicStore::unsubscribe(m_matchType, this); - TopicStore::unsubscribe(m_allianceStation, this); -} - -void FMSInfoWidget::setTopic(const QString &topic) { - m_topic = topic; - - TopicStore::unsubscribe(topic.toStdString() + "/EventName", this); - TopicStore::unsubscribe(topic.toStdString() + "/FMSControlData", this); - TopicStore::unsubscribe(topic.toStdString() + "/GameSpecificMessage", this); - TopicStore::unsubscribe(topic.toStdString() + "/IsRedAlliance", this); - TopicStore::unsubscribe(topic.toStdString() + "/MatchNumber", this); - TopicStore::unsubscribe(topic.toStdString() + "/MatchType", this); - TopicStore::unsubscribe(topic.toStdString() + "/StationNumber", this); - - m_event = TopicStore::subscribe(topic.toStdString() + "/EventName", this, NT_STRING, "Event"); - m_controlWord = TopicStore::subscribe(topic.toStdString() + "/FMSControlData", this, NT_INTEGER, "Control"); - m_gameSpecificMessage = TopicStore::subscribe(topic.toStdString() + "/GameSpecificMessage", this, NT_STRING, "GSM"); - - m_redAlliance = TopicStore::subscribe(topic.toStdString() + "/IsRedAlliance", this, NT_BOOLEAN, "Red Alliance"); - m_matchNumber = TopicStore::subscribe(topic.toStdString() + "/MatchNumber", this, NT_INTEGER, "Match Number"); - m_matchType = TopicStore::subscribe(topic.toStdString() + "/MatchType", this, NT_INTEGER, "Match Type"); - m_allianceStation = TopicStore::subscribe(topic.toStdString() + "/StationNumber", this, NT_INTEGER, "Station Number"); -} - -void FMSInfoWidget::setValue(const nt::Value &value, QString label, bool force) { - if (force) { - QMap map{}; - map.insert("/EventName", "Event"); - map.insert("/FMSControlData", "Control"); - map.insert("/GameSpecificMessage", "GSM"); - map.insert("/IsRedAlliance", "Red Alliance"); - map.insert("/MatchNumber", "Match Number"); - map.insert("/MatchType", "Match Type"); - map.insert("/StationNumber", "StationNumber"); - - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - TopicStore::updateTopic(m_topic.toStdString() + iter.key(), this, iter.value()); - } - - return; - } - - if (label == "Event") { - m_eventString = QString::fromStdString(m_event.GetString("Unknown")); - m_eventLabel->setText("Event: " + m_eventString); - } - - if (label == "GSM") { - m_gsm = QString::fromStdString(m_gameSpecificMessage.GetString("Unknown")); - m_gsmLabel->setText("Game Specific Message: " + m_gsm); - } - - if (label == "Station Number" || label == "Red Alliance") { - int station = m_allianceStation.GetInteger(1); - if (!m_redAlliance.GetBoolean(false)) { - station += 3; - } - - m_station = (AllianceStation) station; - m_stationLabel->setText("Station: " + StationNames.value(m_station)); - } - - if (label == "Match Number" || label == "Match Type") { - m_number = m_matchNumber.GetInteger(0); - m_type = (MatchType) m_matchType.GetInteger(0); - - m_matchLabel->setText("Match: " + - MatchTypeNames.value(m_type) + - " Match " + - QString::number(m_number)); - } - - if (label == "Control") { - m_word = (ControlWord) m_controlWord.GetInteger(0); - ControlFlags flags(m_word); - - QString state = "Robot State: "; - - if (flags & ControlWord::Auto) { - state += "Autonomous"; - } - else if (flags & ControlWord::Test) { - state += "Testing"; - } else { - state += "Teleop"; - } - - if (flags & ControlWord::Enabled) { - state += " Enabled"; - } else if (flags & ControlWord::EStop) { - state += " E-Stopped"; - } else { - state += " Disabled"; - } - - m_controlLabel->setText(state); - - if (flags & ControlWord::FMSAttached) { - m_fmsLabel->setText("FMS Attached"); - m_fmsIconLabel->setPixmap(QIcon(":/icons/check.svg").pixmap(20, 20)); - } else { - m_fmsLabel->setText("FMS Disconnected"); - m_fmsIconLabel->setPixmap(QIcon(":/icons/xmark.svg").pixmap(20, 20)); - } - - if (flags & ControlWord::DSAttached) { - m_dsLabel->setText("DS Attached"); - m_dsIconLabel->setPixmap(QIcon(":/icons/check.svg").pixmap(20, 20)); - } else { - m_dsLabel->setText("DS Disconnected"); - m_dsIconLabel->setPixmap(QIcon(":/icons/xmark.svg").pixmap(20, 20)); - } - } -} diff --git a/lib/src/widgets/FieldWidget.cpp b/lib/src/widgets/FieldWidget.cpp deleted file mode 100644 index 39343b07..00000000 --- a/lib/src/widgets/FieldWidget.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "widgets/FieldWidget.h" -#include "stores/TopicStore.h" - -#include -#include - -FieldWidget::FieldWidget(const QString &topic, QVariantList defaultValue, const QString &title) - : BaseWidget(WidgetTypes::Field, title, topic) -{ - setTopic(topic); - - m_value = defaultValue; - - m_imageLabel = new FieldImage(this); - m_imageLabel->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Expanding); - - m_layout->addWidget(m_imageLabel, 1, 0, 3, 1); - - setImage(m_image); - - setReady(true); -} - -FieldWidget::~FieldWidget() {} - -double FieldWidget::robotWidth() { - return m_width; -} - -void FieldWidget::setRobotWidth(double width) { - m_width = width; - m_imageLabel->setRobotWidth(width); -} - -double FieldWidget::robotLength() { - return m_length; -} - -void FieldWidget::setRobotLength(double length) { - m_length = length; - m_imageLabel->setRobotLength(length); -} - -Globals::File &FieldWidget::image() { - return m_image; -} - -void FieldWidget::setImage(Globals::File image) { - m_image = image; - m_imageLabel->setImage(image); -} - -void FieldWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_DOUBLE_ARRAY); -} - -void FieldWidget::setValue(const nt::Value &value, QString label, bool force) { - m_value = decltype(m_value)(value.GetDoubleArray().begin(), value.GetDoubleArray().end()); - - m_imageLabel->setValue(value.GetDoubleArray()); -} - -void FieldWidget::resizeEvent(QResizeEvent *event) { - BaseWidget::resizeEvent(event); - - int w = event->size().width(); - int h = event->size().height() * 4. / 5.; - - m_imageLabel->setPixmap(m_imageLabel->pixmap().scaled(w, h, Qt::KeepAspectRatio)); -} diff --git a/lib/src/widgets/GraphWidget.cpp b/lib/src/widgets/GraphWidget.cpp deleted file mode 100644 index 5f75ff35..00000000 --- a/lib/src/widgets/GraphWidget.cpp +++ /dev/null @@ -1,270 +0,0 @@ -#include "widgets/GraphWidget.h" -#include "stores/TopicStore.h" -#include "stores/FilterStore.h" -#include "misc/ChartView.h" - -#include -#include -#include -#include -#include - -GraphWidget::GraphWidget(const QString &topic, const QString &title) - : BaseWidget(WidgetType, title, topic, true) { - - QChart *chart = new QChart(); - m_timer = new QTimer(this); - m_timer->setTimerType(Qt::TimerType::PreciseTimer); - m_chart = new ChartView(chart, this); - m_elapsed.start(); - - connect(m_timer, &QTimer::timeout, this, &GraphWidget::updateGraph); - - m_xAxis = new QValueAxis(this); - m_xAxis->setTitleText("Time"); - - m_yAxis = new QValueAxis(this); - m_yAxis->setTitleText("Value"); - - m_chart->chart()->addAxis(m_xAxis, Qt::AlignBottom); - m_chart->chart()->addAxis(m_yAxis, Qt::AlignLeft); - - setMaxTimeScale(30.); - - setMaxYValue(10.0); - setMinYValue(-10.0); - - m_layout->addWidget(m_chart, 1, 0, 3, 1); -} - -QHash GraphWidget::topics() { - QHash newHash{}; - - QHashIterator iter(m_topics); - while(iter.hasNext()) { - iter.next(); - Globals::NumberTopic t; - t = iter.key(); - newHash.insert(t, iter.value()); - } - return newHash; -} - -void GraphWidget::setTopics(QHash topics) { - QHash newHash{}; - - QHashIterator t_iter(topics); - while(t_iter.hasNext()) { - t_iter.next(); - newHash.insert(t_iter.key(), t_iter.value()); - } - - QHashIterator iter(m_topics); - - while (iter.hasNext()) { - iter.next(); - Globals::Topic topic = iter.key(); - - if (!newHash.contains(topic)) { - TopicStore::unsubscribe(topic.Name.toStdString(), this); - m_entryMap.remove(topic); - - QLineSeries *series = m_seriesMap.take(topic); - m_chart->chart()->removeSeries(series); - delete series; - } - } - - iter = QHashIterator(newHash); - - while (iter.hasNext()) { - iter.next(); - Globals::Topic topic = iter.key(); - - if (!m_topics.contains(topic)) { - nt::NetworkTableEntry entry = TopicStore::subscribe(topic.Name.toStdString(), this, NT_UNASSIGNED, topic.Name, true); - - m_entryMap.insert(topic, entry); - - QLineSeries *series = new QLineSeries; - series->setName(topic.Name); - series->append(m_elapsed.elapsed() / 1000., TopicStore::getDoubleFromEntry(entry)); - series->setColor(iter.value()); - - m_chart->chart()->addSeries(series); - series->attachAxis(m_xAxis); - series->attachAxis(m_yAxis); - series->setMarkerSize(100); - - m_seriesMap.insert(topic, series); - } else { - QLineSeries *series = m_seriesMap.value(topic); - series->setColor(iter.value()); - } - } - - m_topics = newHash; -} - - -double GraphWidget::maxTimeScale() { - return m_maxTimeScale; -} - -void GraphWidget::setMaxTimeScale(double maxTimeScale) { - m_maxTimeScale = maxTimeScale; -} - -double GraphWidget::updateFrequency() { - return m_updateFrequency; -} - -void GraphWidget::setUpdateFrequency(double frequency) { - m_updateFrequency = frequency; - - m_timer->stop(); - m_timer->start(frequency * 1000.); -} - - -double GraphWidget::maxYValue() { - return m_maxYValue; -} - -void GraphWidget::setMaxYValue(double max) { - m_maxYValue = max; - m_yAxis->setMax(max); -} - -double GraphWidget::minYValue() { - return m_minYValue; -} - -void GraphWidget::setMinYValue(double min) { - m_minYValue = min; - m_yAxis->setMin(min); -} - -Globals::GraphXAxis GraphWidget::xAxisData() { - return m_xAxisData; -} - -void GraphWidget::setXAxisData(const Globals::GraphXAxis &axis) { - if (m_xAxisData != axis) { - m_xAxis->setTitleText(axis.useTime ? "Time" : axis.topic); - - if (axis.useTime) { - m_elapsed.restart(); - } - - TopicStore::unsubscribe(m_xAxisData.topic.toStdString(), this); - m_xAxisEntry = TopicStore::subscribe(axis.topic.toStdString(), this, NT_UNASSIGNED, axis.topic, true); - - for (QLineSeries *series : m_seriesMap.values()) series->clear(); - - // reset values to NaN - m_minXValue = std::nan("0"); - m_maxXValue = std::nan("1"); - - m_xAxisData = axis; - } -} - -double GraphWidget::getCurrentXAxis() { - if (m_xAxisData.useTime) { - return m_elapsed.elapsed() / 1000.; - } - return TopicStore::getDoubleFromEntry(m_xAxisEntry); -} - -void GraphWidget::updateGraph() { - if (!m_ready) return; - - double xValue = getCurrentXAxis(), lowerBound; - if (m_xAxisData.useTime) { - lowerBound = xValue - m_maxTimeScale; - if (lowerBound < 0) lowerBound = 0; - - m_minXValue = lowerBound; - m_maxXValue = xValue; - } - else { - if (std::isnan(m_maxXValue) || xValue >= m_maxXValue) m_maxXValue = xValue + 0.1; - if (std::isnan(m_minXValue) || xValue <= m_minXValue) m_minXValue = xValue - 0.1; - } - - m_xAxis->setRange(m_minXValue, m_maxXValue); - - QHashIterator iter(m_topics); - - while (iter.hasNext()) { - iter.next(); - Globals::Topic topic = iter.key(); - - nt::NetworkTableEntry entry = m_entryMap.value(topic); - QLineSeries *series = m_seriesMap.value(topic); - - double value = TopicStore::getDoubleFromEntry(entry); - - series->append(xValue, value); - } - - update(); -} - -QMenu *GraphWidget::constructContextMenu(WidgetData data) { - QMenu *menu = BaseWidget::constructContextMenu(data); - - QAction *csvAction = new QAction("Export CSV...", menu); - menu->addAction(csvAction); - - connect(csvAction, &QAction::triggered, this, &GraphWidget::exportCSVPopup); - - return menu; -} - - -void GraphWidget::mouseReleaseEvent(QMouseEvent *event) { - return event->ignore(); -} - -void GraphWidget::exportCSVPopup() { - QString fileString = QFileDialog::getSaveFileName(this, "Export CSV To...", QDir::homePath(), "CSV Files (*.csv);;All Files (*)"); - - exportCSV(fileString); -} - -void GraphWidget::exportCSV(const QString &fileName) { - QFile file(fileName); - - if (file.open(QIODevice::WriteOnly)) { - QTextStream stream(&file); - QString xLabel = m_xAxisData.useTime ? "Time" : m_xAxisData.topic; - stream << xLabel << ',' << FilterStore::topicNames(m_seriesMap.keys()).join(',') << Qt::endl; - - // i hate CSV - QMap xyValueMap{}; - for (const QLineSeries *series : m_seriesMap.values()) { - for (const QPointF &point : series->points()) { - QStringList list{}; - if (xyValueMap.contains(point.x())) { - list = xyValueMap.value(point.x()); - } - list << QString::number(point.y()); - xyValueMap.insert(point.x(), list); - } - } - - QMapIterator iter(xyValueMap); - - while (iter.hasNext()) { - iter.next(); - stream << iter.key() << ',' << iter.value().join(',') << Qt::endl; - } - - stream.flush(); - file.close(); - } else { - QMessageBox::critical(this, "Failed to Open File For Reading", "Failed to open file for reading. Directory may not exist, or you may lack permissions to write here."); - } -} diff --git a/lib/src/widgets/IntegerDialWidget.cpp b/lib/src/widgets/IntegerDialWidget.cpp deleted file mode 100644 index 014e6064..00000000 --- a/lib/src/widgets/IntegerDialWidget.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "widgets/IntegerDialWidget.h" -#include "stores/TopicStore.h" - -#include -#include - -IntegerDialWidget::IntegerDialWidget(const QString &topic, const int &defaultValue, const QString &title) : IntegerDisplayWidget(topic, defaultValue, title, false) { - setTopic(topic); - m_dial = new BetterDial(this); - m_type = WidgetTypes::IntegerDial; - - m_dial->setMin(0); - m_dial->setMax(1000); - - m_layout->removeWidget(m_text); - - m_layout->addWidget(m_dial, 1, 0, 2, 1); - m_layout->addWidget(m_text, 3, 0); - - connect(m_dial, &BetterDial::sliderMoved, this, [this](int position) { - m_entry.SetInteger(position); - m_value = position; - m_text->setText(QString::number(position)); - }); - setReady(true); -} - -IntegerDialWidget::~IntegerDialWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -int IntegerDialWidget::min() { - return m_min; -} - -void IntegerDialWidget::setMin(int min) { - m_min = min; - m_dial->setMin(min); -} - -int IntegerDialWidget::max() { - return m_max; -} - -void IntegerDialWidget::setMax(int max) { - m_max = max; - m_dial->setMax(max); -} - -double IntegerDialWidget::startingAngle() { - return m_startingAngle; -} - -void IntegerDialWidget::setStartingAngle(double angle) { - m_startingAngle = angle; - m_dial->setStartingAngle(angle * M_PI / 180.); -} - -void IntegerDialWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_INTEGER); -} - -void IntegerDialWidget::setValue(const nt::Value &value, QString label, bool force) { - if (!m_text->hasFocus()) { - m_value = value.GetInteger(); - setText(QString::number(m_value)); - - if (!m_dial->isDragging()) m_dial->setValue(m_value); - } -} - -void IntegerDialWidget::keyPressEvent(QKeyEvent *event) { - if (m_text->hasFocus()) { - m_entry.SetInteger(m_text->text().toInt()); - m_value = m_text->text().toInt(); - - m_dial->setValue(m_value); - } else { - event->ignore(); - } -} diff --git a/lib/src/widgets/IntegerDisplayWidget.cpp b/lib/src/widgets/IntegerDisplayWidget.cpp deleted file mode 100644 index c2915fa7..00000000 --- a/lib/src/widgets/IntegerDisplayWidget.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "widgets/IntegerDisplayWidget.h" -#include "stores/TopicStore.h" - -#include -#include - -IntegerDisplayWidget::IntegerDisplayWidget(const QString &topic, const int &defaultValue, const QString &title, const bool &ready) : TextWidget(WidgetTypes::IntegerDisplay, topic, QString::number(defaultValue), title) -{ - setTopic(topic); - - m_value = defaultValue; - setReady(ready); -} - -IntegerDisplayWidget::~IntegerDisplayWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -void IntegerDisplayWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_INTEGER); -} - -void IntegerDisplayWidget::setValue(const nt::Value &value, QString label, bool force) { - if (!m_text->hasFocus()) { - m_value = value.GetInteger(); - setText(QString::number(m_value)); - } -} - -void IntegerDisplayWidget::keyPressEvent(QKeyEvent *event) { - if (m_text->hasFocus()) { - m_entry.SetInteger(m_text->text().toInt()); - m_value = m_text->text().toInt(); - } else { - event->ignore(); - } -} - diff --git a/lib/src/widgets/SendableFieldWidget.cpp b/lib/src/widgets/SendableFieldWidget.cpp deleted file mode 100644 index 069f1e3f..00000000 --- a/lib/src/widgets/SendableFieldWidget.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "widgets/SendableFieldWidget.h" - -#include "stores/TopicStore.h" - -SendableFieldWidget::~SendableFieldWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} diff --git a/lib/src/widgets/StringChooserWidget.cpp b/lib/src/widgets/StringChooserWidget.cpp deleted file mode 100644 index 2eca0099..00000000 --- a/lib/src/widgets/StringChooserWidget.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "widgets/StringChooserWidget.h" -#include "Globals.h" -#include "qtimer.h" -#include "stores/TopicStore.h" - -#include - -StringChooserWidget::StringChooserWidget(const QString &topic, const QString &defaultValue, const QString &title) : BaseWidget(WidgetTypes::SendableChooser, title, topic, true) -{ - setTopic(topic); - - m_chooser = new QComboBox(this); - m_layout->addWidget(m_chooser, 1, 0); - - connect(m_chooser, &QComboBox::currentTextChanged, this, &StringChooserWidget::updateSelected); - - m_layout->setColumnStretch(0, -1); - setReady(true); -} - -StringChooserWidget::~StringChooserWidget() { - TopicStore::unsubscribe(m_active, this); - TopicStore::unsubscribe(m_default, this); - TopicStore::unsubscribe(m_choices, this); - TopicStore::unsubscribe(m_selected, this); -} - -void StringChooserWidget::setTopic(const QString &topic) { - m_topic = topic; - - TopicStore::unsubscribe(topic.toStdString() + "/active", this); - TopicStore::unsubscribe(topic.toStdString() + "/default", this); - TopicStore::unsubscribe(topic.toStdString() + "/options", this); - TopicStore::unsubscribe(topic.toStdString() + "/selected", this); - - m_active = TopicStore::subscribe(topic.toStdString() + "/active", this, NT_STRING, "Active"); - m_default = TopicStore::subscribe(topic.toStdString() + "/default", this, NT_STRING, "Default", true); - m_choices = TopicStore::subscribe(topic.toStdString() + "/options", this, NT_STRING_ARRAY, "Choices"); - m_selected = TopicStore::subscribe(topic.toStdString() + "/selected", this, NT_STRING, "Selected", true); -} - -void StringChooserWidget::setValue(const nt::Value &value, QString label, bool force) { - if (force) { - qDebug() << "Bro"; - QMap map{}; - map.insert("/active", "Active"); - map.insert("/options", "Choices"); - - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - TopicStore::updateTopic(m_topic.toStdString() + iter.key(), this, iter.value()); - } - - return; - } - - else { - if (label == "Active") { - if (!m_readyToUpdate) { - m_readyToUpdate = true; - return; - } - - QString activeValue = m_chooser->currentText(); - std::string activeValueStd = activeValue.toStdString(); - - m_chooser->setCurrentText(QString::fromStdString(std::string{value.GetString()})); - - m_lastSelected = m_chooser->currentText(); - } - - // this is an interesting way to do things - else if (label == "Choices") { - QString selected = m_lastSelected; - std::span choices = value.GetStringArray(); - - m_chooser->clear(); - - QStringList qchoices{}; - - for (const std::string &choice : choices) { - qchoices << QString::fromStdString(choice); - } - - qchoices.sort(); - m_chooser->addItems(qchoices); - - updateSelected(selected); - } - } -} - -void StringChooserWidget::updateSelected(const QString text) { - if (text.isEmpty() || text.isNull()) return; - m_selected.SetString(text.toStdString()); - - m_lastSelected = text; - - m_chooser->setCurrentText(text); - - QTimer *timer = new QTimer; - timer->callOnTimeout([this, timer] { - if (m_active.GetString(m_lastSelected.toStdString()) != m_lastSelected.toStdString()) { - if (m_flashCounter == 0) { - setStyleSheet("BaseWidget { background-color: red; }"); - } - - if (m_flashCounter == 5) { - setStyleSheet("BaseWidget { background-color: " + qApp->palette().color(QPalette::ColorRole::Base).darker(150).name() + "; border: 1px solid white; color: white; }"); - } - - if (m_flashCounter == 10) { - m_flashCounter = -1; - } - - ++m_flashCounter; - } else { - setStyleSheet("BaseWidget { background-color: " + qApp->palette().color(QPalette::ColorRole::Base).darker(150).name() + "; border: 1px solid white; color: white; }"); - - timer->stop(); - timer->deleteLater(); - m_flashCounter = 0; - } - }); - - timer->start(100); -} - -void StringChooserWidget::setConnected(bool connected) { - BaseWidget::setConnected(connected); - m_connected = connected; - - if (connected) { - m_readyToUpdate = false; - - updateSelected(m_lastSelected); - } -} diff --git a/lib/src/widgets/StringDisplayWidget.cpp b/lib/src/widgets/StringDisplayWidget.cpp deleted file mode 100644 index 85483214..00000000 --- a/lib/src/widgets/StringDisplayWidget.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "widgets/StringDisplayWidget.h" -#include "qevent.h" -#include "stores/TopicStore.h" - -#include - -StringDisplayWidget::StringDisplayWidget(const QString &topic, const QString &defaultValue, const QString &title) : TextWidget(WidgetTypes::StringDisplay, topic, defaultValue, title) -{ - setTopic(topic); - - m_value = defaultValue; - setReady(true); -} - -StringDisplayWidget::~StringDisplayWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -void StringDisplayWidget::setTopic(const QString &topic) { - m_topic = topic; - TopicStore::unsubscribe(m_topic, this); - m_entry = TopicStore::subscribe(topic.toStdString(), this, NT_STRING); -} - -void StringDisplayWidget::setValue(const nt::Value &value, QString label, bool force) { - if (!m_text->hasFocus()) { - m_value = QString::fromStdString(std::string(value.GetString())); - setText(m_value); - } -} - -void StringDisplayWidget::keyPressEvent(QKeyEvent *event) { - if (m_text->hasFocus()) { - m_entry.SetString(m_text->text().toStdString()); - m_value = m_text->text(); - } else { - event->ignore(); - } -} diff --git a/lib/src/widgets/SwerveWidget.cpp b/lib/src/widgets/SwerveWidget.cpp deleted file mode 100644 index e3066fbd..00000000 --- a/lib/src/widgets/SwerveWidget.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "widgets/SwerveWidget.h" - -#include "stores/TopicStore.h" - -#include - -SwerveWidget::SwerveWidget(const QString &topic, const QString &title) - : BaseWidget(WidgetType, topic, title, false) { - m_topic = ""; - - m_train = new SwerveTrain(this); - m_train->setLocations({1, 1, -1, 1, 1, -1, -1, -1}); - m_train->setStates({0, 0, 0, 0, 0, 0, 0, 0}); - - m_layout->addWidget(m_train, 1, 0); - m_layout->setRowStretch(1, 1); - - setLocationTopic(Globals::DoubleArrayTopic{""}); - setStatesTopic(Globals::DoubleArrayTopic{""}); -} - -SwerveWidget::~SwerveWidget() { - TopicStore::unsubscribe(m_locEntry, this); - TopicStore::unsubscribe(m_stateEntry, this); -} - -void SwerveWidget::setLocationTopic(const Globals::DoubleArrayTopic &topic) { - TopicStore::unsubscribe(m_locTopic.toStdString(), this); - - m_locEntry = TopicStore::subscribe(topic.Name.toStdString(), this, NT_DOUBLE_ARRAY, "Locations"); - m_locTopic = topic.Name; - - QTimer::singleShot(1000, this, &SwerveWidget::forceUpdate); -} - -Globals::DoubleArrayTopic SwerveWidget::locationTopic() { - return m_locTopic; -} - -void SwerveWidget::setStatesTopic(const Globals::DoubleArrayTopic &topic) { - TopicStore::unsubscribe(m_stateTopic.toStdString(), this); - - m_stateEntry = TopicStore::subscribe(topic.Name.toStdString(), this, NT_DOUBLE_ARRAY, "States"); - m_stateTopic = topic.Name; - - QTimer::singleShot(1000, this, &SwerveWidget::forceUpdate); -} - -Globals::DoubleArrayTopic SwerveWidget::statesTopic() { - return m_stateTopic; -} - -void SwerveWidget::setValue(const nt::Value &value, QString label, bool force) { - if (force) { - QMap map{}; - map.insert(m_locTopic.toStdString(), "Locations"); - map.insert(m_stateTopic.toStdString(), "States"); - - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - TopicStore::updateTopic(iter.key(), this, iter.value()); - } - - return; - } - - if (label == "States") { - QList states; - for (const double state : value.GetDoubleArray()) { - states.append(state); - } - m_train->setStates(states); - } - - if (label == "Locations") { - QList locations; - for (const double location : value.GetDoubleArray()) { - locations.append(location); - } - m_train->setLocations(locations); - } - - m_train->update(); -} diff --git a/lib/src/widgets/TabWidget.cpp b/lib/src/widgets/TabWidget.cpp deleted file mode 100644 index b9754bdf..00000000 --- a/lib/src/widgets/TabWidget.cpp +++ /dev/null @@ -1,429 +0,0 @@ -#include "widgets/TabWidget.h" - -#include -#include -#include -#include - -TabWidget::TabWidget(const QPoint &maxSize, QWidget *parent) : QWidget(parent) -{ - setFocusPolicy(Qt::ClickFocus); - - m_layout = new QGridLayout(this); - m_gridLine = new GridLineWidget(this); - m_gridLine->lower(); - - m_layout->addWidget(m_gridLine, 0, 0, -1, -1); - - // shortcuts - { - new QShortcut(QKeySequence(Qt::Key_Escape), this, [this] { - cancelDrags(); - m_gridLine->setHasSelection(false); - m_gridLine->setValidSelection(false); - m_gridLine->update(); - - emit dragCancelled(m_draggedWidget); - }); - } - - setMaxSize(maxSize); -} - -TabWidget::~TabWidget() {} - -QList TabWidget::widgets() { - return m_widgets; -} - -void TabWidget::addWidget(BaseWidget *widget, WidgetData data) { - if (m_widgets.contains(widget)) { - m_layout->removeWidget(widget); - } else { - m_widgets.append(widget); - widget->setParent(this); - } - m_layout->addWidget(widget, data.row, data.col, data.rowSpan, data.colSpan); - m_gridLine->lower(); -} - -void TabWidget::deleteWidget(BaseWidget *widget) { - if (m_widgets.contains(widget)) { - m_widgets.removeAll(widget); - m_layout->removeWidget(widget); - widget->deleteLater(); - } -} - -void TabWidget::forceUpdateWidgets() { - for (BaseWidget *widget : m_widgets) { - widget->forceUpdate(); - } -} - -WidgetData TabWidget::widgetData(BaseWidget *widget) { - int idx = m_layout->indexOf(widget); - if (idx == -1) { - return WidgetData{0, 0, 0, 0}; - } - - WidgetData data; - - m_layout->getItemPosition(idx, - &data.row, - &data.col, - &data.rowSpan, - &data.colSpan); - - return data; -} - -bool TabWidget::widgetAtPoint(WidgetData data) { - // this is mild cancer but idrc - - for (int i = 0; i < m_layout->count(); ++i) { - QLayoutItem *item = m_layout->itemAt(i); - if (item->widget() == m_gridLine) continue; - - int row, col, rowSpan, colSpan; - m_layout->getItemPosition(i, &row, &col, &rowSpan, &colSpan); - - QRect dataRect = QRect(data.row, data.col, data.rowSpan, data.colSpan); - QRect itemRect = QRect(row, col, rowSpan, colSpan); - - if (dataRect.intersects(itemRect)) { - return true; - } - } - - return false; -} - -QGridLayout *TabWidget::layout() { - return m_layout; -} - -QString TabWidget::name() const -{ - return m_name; -} - -void TabWidget::setName(const QString &newName) -{ - m_name = newName; -} - -QPoint TabWidget::maxSize() { - return m_maxSize; -} - -void TabWidget::setMaxSize(const QPoint &maxSize) { - QPoint lastMax = m_maxSize; - m_maxSize = maxSize; - - m_gridLine->setSize(maxSize); - - if (lastMax.x() > maxSize.x()) { - for (int x = maxSize.x(); x < lastMax.x(); ++x) { - m_layout->setColumnStretch(x, 0); - } - } else { - for (int x = 0; x < m_maxSize.x(); ++x) { - m_layout->setColumnStretch(x, 1); - } - } - - if (lastMax.y() > maxSize.y()) { - for (int y = maxSize.y(); y < lastMax.y(); ++y) { - m_layout->setRowStretch(y, 0); - } - } else { - for (int y = 0; y < m_maxSize.y(); ++y) { - m_layout->setRowStretch(y, 1); - } - } -} - -QJsonObject TabWidget::saveObject() { - QJsonObject object{}; - object.insert("tabName", m_name); - - QPoint maxSize = m_maxSize; - object.insert("maxSize", QJsonArray{ maxSize.x(), maxSize.y() }); - - QJsonArray widgets{}; - - for (BaseWidget *widget : m_widgets) { - QJsonObject widgetObj = widget->saveObject(); - WidgetData data = widgetData(widget); - - widgetObj.insert("geometry", QJsonArray({data.row, data.col, data.rowSpan, data.colSpan})); - - widgets.append(widgetObj); - } - - object.insert("widgets", widgets); - return object; -} - -void TabWidget::loadObject(const QJsonObject &object) { - QString name = object.value("tabName").toString(); - m_name = name; - - QJsonArray maxSize = object.value("maxSize").toArray(); - setMaxSize(QPoint(maxSize.at(0).toInt(3), maxSize.at(1).toInt(3))); - - QJsonArray widgets = object.value("widgets").toArray(); - - for (QJsonValueRef wref : widgets) { - QJsonObject widgetObject = wref.toObject(); - - QString topic = widgetObject.value("Topic").toString(""); - - WidgetTypes type = (WidgetTypes) widgetObject.value("widgetType").toInt(); - BaseWidget *widget = BaseWidget::defaultWidgetFromTopic(topic, type); - - WidgetData data = widget->fromJson(widgetObject); - - addWidget(widget, data); - } // widgets -} - -/* DRAG AND DROP */ - -void TabWidget::mousePressEvent(QMouseEvent *event) { - QWidget::mousePressEvent(event); - - BaseWidget *widgetPressed = nullptr; - - for (BaseWidget *widget : widgets()) - { - // map to tab widget as the base widget's geometry is relative to the tab widget - if (widget->geometry().contains(mapFromGlobal(event->globalPosition()).toPoint())) { - widgetPressed = widget; - break; - } - } - - if (!widgetPressed) { - - return; - } - if (event->button() == Qt::RightButton) { - QMenu *menu = widgetPressed->constructContextMenu(widgetData(widgetPressed)); - - connect(widgetPressed, &BaseWidget::reconfigRequested, this, [this](BaseWidget *widget, WidgetData data) { - addWidget(widget, data); - }); - - connect(widgetPressed, &BaseWidget::deleteRequested, this, [this, widgetPressed] { - deleteWidget(widgetPressed); - }); - - menu->popup(event->globalPosition().toPoint()); - return; - } - - if (event->button() != Qt::LeftButton) { - return; - } - - setDragData(widgetPressed, widgetData(widgetPressed)); - - m_dragStart = event->pos(); - widgetPressed->raise(); - - if (widgetPressed->resizing() == NONE) { - dragStart(event->pos(), event->pos()); - } else { - resizeStart(event->pos()); - } -} - -void TabWidget::mouseMoveEvent(QMouseEvent *event) { - if ((event->pos() - m_dragStart).manhattanLength() - < QApplication::startDragDistance()) - return; - - QPoint point = mapFromGlobal(event->globalPosition()).toPoint(); - - if (m_dragging) { - dragMove(point); - } else if (m_resizing) { - resizeMove(point); - } -} - -void TabWidget::mouseReleaseEvent(QMouseEvent *event) { - if (!m_gridLine->hasSelection()) goto end; - - if (m_gridLine->isValidSelection()) { - m_draggedWidgetData = m_gridLine->selection(); - } - - if (m_dragging) { - emit dragDone(m_draggedWidget, m_draggedWidgetData); - } else if (!m_resizing) { - goto end; - } - - addWidget(m_draggedWidget, - m_draggedWidgetData); - -end: - cancelDrags(); -} - -void TabWidget::setDragData(BaseWidget *widget, WidgetData data) { - cancelDrags(); - emit dragCancelled(m_draggedWidget); - - m_draggedWidget = widget; - m_draggedWidgetData = data; -} - -void TabWidget::cancelDrags() { - // bunch of ways to ensure the widget is actually there - if ( - (m_dragging || m_resizing) && - (hasWidget(m_draggedWidget) || m_draggedWidget->isVisible())) { - addWidget(m_draggedWidget, - m_draggedWidgetData); - } - - m_dragging = false; - m_resizing = false; - - m_draggedWidget = nullptr; - - m_gridLine->setHasSelection(false); -} - -void TabWidget::dragStart(QPoint point, QPoint offset) { - if (offset == point) { - m_dragOffset = offset - m_draggedWidget->pos(); - } else { - m_dragOffset = offset; - } - m_dragging = true; -} - -void TabWidget::dragMove(QPoint point) { - if (m_draggedWidget && m_draggedWidget->isVisible() && m_draggedWidget->isEnabled()) { - QPoint offset = point - m_dragOffset; - m_draggedWidget->move(offset); - - if (!m_gridLine->hasSelection()) { - m_layout->removeWidget(m_draggedWidget); - m_widgets.removeAll(m_draggedWidget); - } - } - - int col = std::floor(point.x() / (width() / maxSize().x())); - int row = std::floor(point.y() / (height() / maxSize().y())); - - int colSpan = m_draggedWidgetData.colSpan; - int rowSpan = m_draggedWidgetData.rowSpan; - - WidgetData data{row, col, rowSpan, colSpan}; - - m_gridLine->setValidSelection(!widgetAtPoint(data) && - (row + rowSpan <= m_maxSize.y()) && - (row >= 0) && - (col + colSpan <= m_maxSize.x()) && - (col >= 0)); - - m_gridLine->setSelection(data); -} - -void TabWidget::resizeStart(QPoint point) { - m_currentResize = m_draggedWidget->resizing(); - m_initialSize = m_draggedWidget->geometry(); - - setCursor(m_draggedWidget->cursor()); - m_resizing = true; -} - -void TabWidget::resizeMove(QPoint point) { - if (m_initialSize == QRect()) - return; - - if (!m_gridLine->hasSelection()) { - layout()->removeWidget(m_draggedWidget); - m_widgets.removeAll(m_draggedWidget); - } - - QPoint offset = point - m_dragStart; - ResizeDirection dir = m_currentResize; - - int dx = 0; - int dy = 0; - int dh = 0; - int dw = 0; - - if (dir & LEFT) { - dx = offset.x(); - dw = -offset.x(); - } - if (dir & RIGHT) { - dw = offset.x(); - } - if (dir & TOP) { - dy = offset.y(); - dh = -offset.y(); - } - if (dir & BOTTOM) { - dh = offset.y(); - } - - int colF = std::floor(point.x() / (width() / m_maxSize.x())); - int rowF = std::floor(point.y() / (height() / m_maxSize.y())); - - int colSpanI = m_draggedWidgetData.colSpan; - int rowSpanI = m_draggedWidgetData.rowSpan; - - int colI = m_draggedWidgetData.col; - int rowI = m_draggedWidgetData.row; - - if (dir & LEFT) colI += colSpanI - 1; - if (dir & TOP) rowI += rowSpanI - 1; - - int col = dir & LEFT ? std::min(colF, colI) : colI; - int row = dir & TOP ? std::min(rowF, rowI) : rowI; - - int colSpan = (dir & LEFT || dir & RIGHT) ? (dir & RIGHT ? std::abs(colF - colI) + 1 : std::abs(colI - colF) + 1) : colSpanI; - int rowSpan = (dir & TOP || dir & BOTTOM) ? (dir & BOTTOM ? std::abs(rowF - rowI) + 1 : std::abs(rowI - rowF) + 1) : rowSpanI; - - if (colSpan < 1) colSpan = 1; - if (rowSpan < 1) rowSpan = 1; - - if (dw <= -m_initialSize.width()) { - dw = 0; - colSpan = 1; - - } - if (dh <= -m_initialSize.height()) { - dh = 0; - rowSpan = 1; - } - - m_draggedWidget->setGeometry( - m_initialSize.x() + dx, - m_initialSize.y() + dy, - m_initialSize.width() + dw, - m_initialSize.height() + dh); - - WidgetData data{row, col, rowSpan, colSpan}; - - m_gridLine->setSelection(data); - - m_gridLine->setValidSelection(!widgetAtPoint(data) && - (row + rowSpan <= m_maxSize.y()) && - (row >= 0) && - (col + colSpan <= m_maxSize.x()) && - (col >= 0)); -} - -bool TabWidget::hasWidget(BaseWidget *widget) { - return (widgetData(widget) != WidgetData{0, 0, 0, 0}); -} diff --git a/lib/src/widgets/TextWidget.cpp b/lib/src/widgets/TextWidget.cpp deleted file mode 100644 index aa99ce9a..00000000 --- a/lib/src/widgets/TextWidget.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "widgets/TextWidget.h" -#include "stores/TopicStore.h" - -#include -#include -#include - -TextWidget::TextWidget(const WidgetTypes &type, const QString &topic, const QString &defaultText, const QString &title) : BaseWidget::BaseWidget(type, title, topic) -{ - m_text = new QLineEdit(defaultText, this); - - m_layout->addWidget(m_text, 1, 0, 3, 1); - - m_text->setStyleSheet("border: none; border-bottom: 1px solid white;"); - m_text->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); -} - -TextWidget::~TextWidget() { - TopicStore::unsubscribe(m_topic.toStdString(), this); -} - -QString TextWidget::text() { - return m_text->text(); -} - -void TextWidget::setText(const QString &text) { - m_text->setText(text); -} - -QFont TextWidget::font() { - return m_text->font(); -} - -void TextWidget::setFont(const QFont &font) { - m_text->setFont(font); - m_text->setMinimumWidth(m_text->fontMetrics().maxWidth()); -} - -QMenu *TextWidget::constructContextMenu(WidgetData data) { - QMenu *menu = BaseWidget::constructContextMenu(data); - - QAction *textFontAction = new QAction("Text Font", menu); - menu->addAction(textFontAction); - - connect(textFontAction, &QAction::triggered, this, [this](bool) { - setFont(QFontDialog::getFont(0, font(), this, "Set Text Font")); - }); - - return menu; -} diff --git a/lib/theme/qrc/breeze.qrc b/lib/theme/qrc/breeze.qrc deleted file mode 100644 index 4d7063be..00000000 --- a/lib/theme/qrc/breeze.qrc +++ /dev/null @@ -1,352 +0,0 @@ - - - dark-purple/branch_closed.svg - dark-purple/branch_closed_hover.svg - dark-purple/branch_end.svg - dark-purple/branch_end_arrow.svg - dark-purple/branch_more.svg - dark-purple/branch_more_arrow.svg - dark-purple/branch_open.svg - dark-purple/branch_open_hover.svg - dark-purple/calendar_next.svg - dark-purple/calendar_previous.svg - dark-purple/checkbox_checked.svg - dark-purple/checkbox_checked_disabled.svg - dark-purple/checkbox_indeterminate.svg - dark-purple/checkbox_indeterminate_disabled.svg - dark-purple/checkbox_unchecked.svg - dark-purple/checkbox_unchecked_disabled.svg - dark-purple/clear_text.svg - dark-purple/close.svg - dark-purple/close_hover.svg - dark-purple/close_pressed.svg - dark-purple/computer.svg - dark-purple/desktop.svg - dark-purple/dialog_cancel.svg - dark-purple/dialog_close.svg - dark-purple/dialog_discard.svg - dark-purple/dialog_help.svg - dark-purple/dialog_no.svg - dark-purple/dialog_ok.svg - dark-purple/dialog_open.svg - dark-purple/dialog_reset.svg - dark-purple/dialog_save.svg - dark-purple/disc_drive.svg - dark-purple/down_arrow.svg - dark-purple/down_arrow_disabled.svg - dark-purple/down_arrow_hover.svg - dark-purple/file.svg - dark-purple/file_dialog_contents.svg - dark-purple/file_dialog_detailed.svg - dark-purple/file_dialog_end.svg - dark-purple/file_dialog_info.svg - dark-purple/file_dialog_list.svg - dark-purple/file_dialog_start.svg - dark-purple/file_link.svg - dark-purple/floppy_drive.svg - dark-purple/folder.svg - dark-purple/folder_link.svg - dark-purple/folder_open.svg - dark-purple/hard_drive.svg - dark-purple/help.svg - dark-purple/hmovetoolbar.svg - dark-purple/home_directory.svg - dark-purple/hseptoolbar.svg - dark-purple/left_arrow.svg - dark-purple/left_arrow_disabled.svg - dark-purple/left_arrow_hover.svg - dark-purple/maximize.svg - dark-purple/menu.svg - dark-purple/message_critical.svg - dark-purple/message_information.svg - dark-purple/message_question.svg - dark-purple/message_warning.svg - dark-purple/minimize.svg - dark-purple/network_drive.svg - dark-purple/radio_checked.svg - dark-purple/radio_checked_disabled.svg - dark-purple/radio_unchecked.svg - dark-purple/radio_unchecked_disabled.svg - dark-purple/restore.svg - dark-purple/right_arrow.svg - dark-purple/right_arrow_disabled.svg - dark-purple/right_arrow_hover.svg - dark-purple/shade.svg - dark-purple/sizegrip.svg - dark-purple/stylesheet.qss - dark-purple/transparent.svg - dark-purple/trash.svg - dark-purple/undock.svg - dark-purple/undock_hover.svg - dark-purple/undock_hover_pressed.svg - dark-purple/unshade.svg - dark-purple/up_arrow.svg - dark-purple/up_arrow_disabled.svg - dark-purple/up_arrow_hover.svg - dark-purple/vline.svg - dark-purple/vmovetoolbar.svg - dark-purple/vseptoolbar.svg - dark-purple/window_close.svg - dark/branch_closed.svg - dark/branch_closed_hover.svg - dark/branch_end.svg - dark/branch_end_arrow.svg - dark/branch_more.svg - dark/branch_more_arrow.svg - dark/branch_open.svg - dark/branch_open_hover.svg - dark/calendar_next.svg - dark/calendar_previous.svg - dark/checkbox_checked.svg - dark/checkbox_checked_disabled.svg - dark/checkbox_indeterminate.svg - dark/checkbox_indeterminate_disabled.svg - dark/checkbox_unchecked.svg - dark/checkbox_unchecked_disabled.svg - dark/clear_text.svg - dark/close.svg - dark/close_hover.svg - dark/close_pressed.svg - dark/computer.svg - dark/desktop.svg - dark/dialog_cancel.svg - dark/dialog_close.svg - dark/dialog_discard.svg - dark/dialog_help.svg - dark/dialog_no.svg - dark/dialog_ok.svg - dark/dialog_open.svg - dark/dialog_reset.svg - dark/dialog_save.svg - dark/disc_drive.svg - dark/down_arrow.svg - dark/down_arrow_disabled.svg - dark/down_arrow_hover.svg - dark/file.svg - dark/file_dialog_contents.svg - dark/file_dialog_detailed.svg - dark/file_dialog_end.svg - dark/file_dialog_info.svg - dark/file_dialog_list.svg - dark/file_dialog_start.svg - dark/file_link.svg - dark/floppy_drive.svg - dark/folder.svg - dark/folder_link.svg - dark/folder_open.svg - dark/hard_drive.svg - dark/help.svg - dark/hmovetoolbar.svg - dark/home_directory.svg - dark/hseptoolbar.svg - dark/left_arrow.svg - dark/left_arrow_disabled.svg - dark/left_arrow_hover.svg - dark/maximize.svg - dark/menu.svg - dark/message_critical.svg - dark/message_information.svg - dark/message_question.svg - dark/message_warning.svg - dark/minimize.svg - dark/network_drive.svg - dark/radio_checked.svg - dark/radio_checked_disabled.svg - dark/radio_unchecked.svg - dark/radio_unchecked_disabled.svg - dark/restore.svg - dark/right_arrow.svg - dark/right_arrow_disabled.svg - dark/right_arrow_hover.svg - dark/shade.svg - dark/sizegrip.svg - dark/stylesheet.qss - dark/transparent.svg - dark/trash.svg - dark/undock.svg - dark/undock_hover.svg - dark/undock_hover_pressed.svg - dark/unshade.svg - dark/up_arrow.svg - dark/up_arrow_disabled.svg - dark/up_arrow_hover.svg - dark/vline.svg - dark/vmovetoolbar.svg - dark/vseptoolbar.svg - dark/window_close.svg - light-purple/branch_closed.svg - light-purple/branch_closed_hover.svg - light-purple/branch_end.svg - light-purple/branch_end_arrow.svg - light-purple/branch_more.svg - light-purple/branch_more_arrow.svg - light-purple/branch_open.svg - light-purple/branch_open_hover.svg - light-purple/calendar_next.svg - light-purple/calendar_previous.svg - light-purple/checkbox_checked.svg - light-purple/checkbox_checked_disabled.svg - light-purple/checkbox_indeterminate.svg - light-purple/checkbox_indeterminate_disabled.svg - light-purple/checkbox_unchecked.svg - light-purple/checkbox_unchecked_disabled.svg - light-purple/clear_text.svg - light-purple/close.svg - light-purple/close_hover.svg - light-purple/close_pressed.svg - light-purple/computer.svg - light-purple/desktop.svg - light-purple/dialog_cancel.svg - light-purple/dialog_close.svg - light-purple/dialog_discard.svg - light-purple/dialog_help.svg - light-purple/dialog_no.svg - light-purple/dialog_ok.svg - light-purple/dialog_open.svg - light-purple/dialog_reset.svg - light-purple/dialog_save.svg - light-purple/disc_drive.svg - light-purple/down_arrow.svg - light-purple/down_arrow_disabled.svg - light-purple/down_arrow_hover.svg - light-purple/file.svg - light-purple/file_dialog_contents.svg - light-purple/file_dialog_detailed.svg - light-purple/file_dialog_end.svg - light-purple/file_dialog_info.svg - light-purple/file_dialog_list.svg - light-purple/file_dialog_start.svg - light-purple/file_link.svg - light-purple/floppy_drive.svg - light-purple/folder.svg - light-purple/folder_link.svg - light-purple/folder_open.svg - light-purple/hard_drive.svg - light-purple/help.svg - light-purple/hmovetoolbar.svg - light-purple/home_directory.svg - light-purple/hseptoolbar.svg - light-purple/left_arrow.svg - light-purple/left_arrow_disabled.svg - light-purple/left_arrow_hover.svg - light-purple/maximize.svg - light-purple/menu.svg - light-purple/message_critical.svg - light-purple/message_information.svg - light-purple/message_question.svg - light-purple/message_warning.svg - light-purple/minimize.svg - light-purple/network_drive.svg - light-purple/radio_checked.svg - light-purple/radio_checked_disabled.svg - light-purple/radio_unchecked.svg - light-purple/radio_unchecked_disabled.svg - light-purple/restore.svg - light-purple/right_arrow.svg - light-purple/right_arrow_disabled.svg - light-purple/right_arrow_hover.svg - light-purple/shade.svg - light-purple/sizegrip.svg - light-purple/stylesheet.qss - light-purple/transparent.svg - light-purple/trash.svg - light-purple/undock.svg - light-purple/undock_hover.svg - light-purple/undock_hover_pressed.svg - light-purple/unshade.svg - light-purple/up_arrow.svg - light-purple/up_arrow_disabled.svg - light-purple/up_arrow_hover.svg - light-purple/vline.svg - light-purple/vmovetoolbar.svg - light-purple/vseptoolbar.svg - light-purple/window_close.svg - light/branch_closed.svg - light/branch_closed_hover.svg - light/branch_end.svg - light/branch_end_arrow.svg - light/branch_more.svg - light/branch_more_arrow.svg - light/branch_open.svg - light/branch_open_hover.svg - light/calendar_next.svg - light/calendar_previous.svg - light/checkbox_checked.svg - light/checkbox_checked_disabled.svg - light/checkbox_indeterminate.svg - light/checkbox_indeterminate_disabled.svg - light/checkbox_unchecked.svg - light/checkbox_unchecked_disabled.svg - light/clear_text.svg - light/close.svg - light/close_hover.svg - light/close_pressed.svg - light/computer.svg - light/desktop.svg - light/dialog_cancel.svg - light/dialog_close.svg - light/dialog_discard.svg - light/dialog_help.svg - light/dialog_no.svg - light/dialog_ok.svg - light/dialog_open.svg - light/dialog_reset.svg - light/dialog_save.svg - light/disc_drive.svg - light/down_arrow.svg - light/down_arrow_disabled.svg - light/down_arrow_hover.svg - light/file.svg - light/file_dialog_contents.svg - light/file_dialog_detailed.svg - light/file_dialog_end.svg - light/file_dialog_info.svg - light/file_dialog_list.svg - light/file_dialog_start.svg - light/file_link.svg - light/floppy_drive.svg - light/folder.svg - light/folder_link.svg - light/folder_open.svg - light/hard_drive.svg - light/help.svg - light/hmovetoolbar.svg - light/home_directory.svg - light/hseptoolbar.svg - light/left_arrow.svg - light/left_arrow_disabled.svg - light/left_arrow_hover.svg - light/maximize.svg - light/menu.svg - light/message_critical.svg - light/message_information.svg - light/message_question.svg - light/message_warning.svg - light/minimize.svg - light/network_drive.svg - light/radio_checked.svg - light/radio_checked_disabled.svg - light/radio_unchecked.svg - light/radio_unchecked_disabled.svg - light/restore.svg - light/right_arrow.svg - light/right_arrow_disabled.svg - light/right_arrow_hover.svg - light/shade.svg - light/sizegrip.svg - light/stylesheet.qss - light/transparent.svg - light/trash.svg - light/undock.svg - light/undock_hover.svg - light/undock_hover_pressed.svg - light/unshade.svg - light/up_arrow.svg - light/up_arrow_disabled.svg - light/up_arrow_hover.svg - light/vline.svg - light/vmovetoolbar.svg - light/vseptoolbar.svg - light/window_close.svg - - diff --git a/lib/theme/qrc/dark-purple/branch_closed.svg b/lib/theme/qrc/dark-purple/branch_closed.svg deleted file mode 100644 index e8e8d91d..00000000 --- a/lib/theme/qrc/dark-purple/branch_closed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_closed_hover.svg b/lib/theme/qrc/dark-purple/branch_closed_hover.svg deleted file mode 100644 index af701bbf..00000000 --- a/lib/theme/qrc/dark-purple/branch_closed_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_end.svg b/lib/theme/qrc/dark-purple/branch_end.svg deleted file mode 100644 index 7e8888ed..00000000 --- a/lib/theme/qrc/dark-purple/branch_end.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_end_arrow.svg b/lib/theme/qrc/dark-purple/branch_end_arrow.svg deleted file mode 100644 index d5aaabde..00000000 --- a/lib/theme/qrc/dark-purple/branch_end_arrow.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_more.svg b/lib/theme/qrc/dark-purple/branch_more.svg deleted file mode 100644 index a6d2b6e7..00000000 --- a/lib/theme/qrc/dark-purple/branch_more.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_more_arrow.svg b/lib/theme/qrc/dark-purple/branch_more_arrow.svg deleted file mode 100644 index 6c4935a5..00000000 --- a/lib/theme/qrc/dark-purple/branch_more_arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_open.svg b/lib/theme/qrc/dark-purple/branch_open.svg deleted file mode 100644 index 031aa44b..00000000 --- a/lib/theme/qrc/dark-purple/branch_open.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/branch_open_hover.svg b/lib/theme/qrc/dark-purple/branch_open_hover.svg deleted file mode 100644 index ee4f1142..00000000 --- a/lib/theme/qrc/dark-purple/branch_open_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/calendar_next.svg b/lib/theme/qrc/dark-purple/calendar_next.svg deleted file mode 100644 index 61318930..00000000 --- a/lib/theme/qrc/dark-purple/calendar_next.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/calendar_previous.svg b/lib/theme/qrc/dark-purple/calendar_previous.svg deleted file mode 100644 index 8382adf0..00000000 --- a/lib/theme/qrc/dark-purple/calendar_previous.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/checkbox_checked.svg b/lib/theme/qrc/dark-purple/checkbox_checked.svg deleted file mode 100644 index 6f4c0919..00000000 --- a/lib/theme/qrc/dark-purple/checkbox_checked.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark-purple/checkbox_checked_disabled.svg b/lib/theme/qrc/dark-purple/checkbox_checked_disabled.svg deleted file mode 100644 index 0508e928..00000000 --- a/lib/theme/qrc/dark-purple/checkbox_checked_disabled.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark-purple/checkbox_indeterminate.svg b/lib/theme/qrc/dark-purple/checkbox_indeterminate.svg deleted file mode 100644 index 3bd1c707..00000000 --- a/lib/theme/qrc/dark-purple/checkbox_indeterminate.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/checkbox_indeterminate_disabled.svg b/lib/theme/qrc/dark-purple/checkbox_indeterminate_disabled.svg deleted file mode 100644 index 2ba0edf7..00000000 --- a/lib/theme/qrc/dark-purple/checkbox_indeterminate_disabled.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/checkbox_unchecked.svg b/lib/theme/qrc/dark-purple/checkbox_unchecked.svg deleted file mode 100644 index db9e6d45..00000000 --- a/lib/theme/qrc/dark-purple/checkbox_unchecked.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/checkbox_unchecked_disabled.svg b/lib/theme/qrc/dark-purple/checkbox_unchecked_disabled.svg deleted file mode 100644 index bedb0fd4..00000000 --- a/lib/theme/qrc/dark-purple/checkbox_unchecked_disabled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/clear_text.svg b/lib/theme/qrc/dark-purple/clear_text.svg deleted file mode 100644 index 9fbc2fe7..00000000 --- a/lib/theme/qrc/dark-purple/clear_text.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/close.svg b/lib/theme/qrc/dark-purple/close.svg deleted file mode 100644 index df9dde2d..00000000 --- a/lib/theme/qrc/dark-purple/close.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/close_hover.svg b/lib/theme/qrc/dark-purple/close_hover.svg deleted file mode 100644 index cc627287..00000000 --- a/lib/theme/qrc/dark-purple/close_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/close_pressed.svg b/lib/theme/qrc/dark-purple/close_pressed.svg deleted file mode 100644 index 721f4a49..00000000 --- a/lib/theme/qrc/dark-purple/close_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/computer.svg b/lib/theme/qrc/dark-purple/computer.svg deleted file mode 100644 index 0d912807..00000000 --- a/lib/theme/qrc/dark-purple/computer.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/desktop.svg b/lib/theme/qrc/dark-purple/desktop.svg deleted file mode 100644 index fcfa8a96..00000000 --- a/lib/theme/qrc/dark-purple/desktop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_cancel.svg b/lib/theme/qrc/dark-purple/dialog_cancel.svg deleted file mode 100644 index 226499fc..00000000 --- a/lib/theme/qrc/dark-purple/dialog_cancel.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_close.svg b/lib/theme/qrc/dark-purple/dialog_close.svg deleted file mode 100644 index c2174c23..00000000 --- a/lib/theme/qrc/dark-purple/dialog_close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_discard.svg b/lib/theme/qrc/dark-purple/dialog_discard.svg deleted file mode 100644 index b711b61b..00000000 --- a/lib/theme/qrc/dark-purple/dialog_discard.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_help.svg b/lib/theme/qrc/dark-purple/dialog_help.svg deleted file mode 100644 index 46153b67..00000000 --- a/lib/theme/qrc/dark-purple/dialog_help.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_no.svg b/lib/theme/qrc/dark-purple/dialog_no.svg deleted file mode 100644 index e9f5245b..00000000 --- a/lib/theme/qrc/dark-purple/dialog_no.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_ok.svg b/lib/theme/qrc/dark-purple/dialog_ok.svg deleted file mode 100644 index d236e77a..00000000 --- a/lib/theme/qrc/dark-purple/dialog_ok.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_open.svg b/lib/theme/qrc/dark-purple/dialog_open.svg deleted file mode 100644 index 5fa68c35..00000000 --- a/lib/theme/qrc/dark-purple/dialog_open.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_reset.svg b/lib/theme/qrc/dark-purple/dialog_reset.svg deleted file mode 100644 index bb5839ab..00000000 --- a/lib/theme/qrc/dark-purple/dialog_reset.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/dialog_save.svg b/lib/theme/qrc/dark-purple/dialog_save.svg deleted file mode 100644 index e85535fe..00000000 --- a/lib/theme/qrc/dark-purple/dialog_save.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/disc_drive.svg b/lib/theme/qrc/dark-purple/disc_drive.svg deleted file mode 100644 index a5a0b407..00000000 --- a/lib/theme/qrc/dark-purple/disc_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/down_arrow.svg b/lib/theme/qrc/dark-purple/down_arrow.svg deleted file mode 100644 index 358a36f8..00000000 --- a/lib/theme/qrc/dark-purple/down_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/down_arrow_disabled.svg b/lib/theme/qrc/dark-purple/down_arrow_disabled.svg deleted file mode 100644 index 6e3f6501..00000000 --- a/lib/theme/qrc/dark-purple/down_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/down_arrow_hover.svg b/lib/theme/qrc/dark-purple/down_arrow_hover.svg deleted file mode 100644 index 33d21956..00000000 --- a/lib/theme/qrc/dark-purple/down_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/file.svg b/lib/theme/qrc/dark-purple/file.svg deleted file mode 100644 index f400c7d7..00000000 --- a/lib/theme/qrc/dark-purple/file.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/file_dialog_contents.svg b/lib/theme/qrc/dark-purple/file_dialog_contents.svg deleted file mode 100644 index d05fe1e2..00000000 --- a/lib/theme/qrc/dark-purple/file_dialog_contents.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark-purple/file_dialog_detailed.svg b/lib/theme/qrc/dark-purple/file_dialog_detailed.svg deleted file mode 100644 index 37a33c3a..00000000 --- a/lib/theme/qrc/dark-purple/file_dialog_detailed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/file_dialog_end.svg b/lib/theme/qrc/dark-purple/file_dialog_end.svg deleted file mode 100644 index a33ae903..00000000 --- a/lib/theme/qrc/dark-purple/file_dialog_end.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/file_dialog_info.svg b/lib/theme/qrc/dark-purple/file_dialog_info.svg deleted file mode 100644 index c0e79390..00000000 --- a/lib/theme/qrc/dark-purple/file_dialog_info.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/lib/theme/qrc/dark-purple/file_dialog_list.svg b/lib/theme/qrc/dark-purple/file_dialog_list.svg deleted file mode 100644 index 9ef3ac38..00000000 --- a/lib/theme/qrc/dark-purple/file_dialog_list.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/file_dialog_start.svg b/lib/theme/qrc/dark-purple/file_dialog_start.svg deleted file mode 100644 index 6b529a1a..00000000 --- a/lib/theme/qrc/dark-purple/file_dialog_start.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/file_link.svg b/lib/theme/qrc/dark-purple/file_link.svg deleted file mode 100644 index 1602812e..00000000 --- a/lib/theme/qrc/dark-purple/file_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/floppy_drive.svg b/lib/theme/qrc/dark-purple/floppy_drive.svg deleted file mode 100644 index df46c001..00000000 --- a/lib/theme/qrc/dark-purple/floppy_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/folder.svg b/lib/theme/qrc/dark-purple/folder.svg deleted file mode 100644 index be0596d8..00000000 --- a/lib/theme/qrc/dark-purple/folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/folder_link.svg b/lib/theme/qrc/dark-purple/folder_link.svg deleted file mode 100644 index c76c09de..00000000 --- a/lib/theme/qrc/dark-purple/folder_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/folder_open.svg b/lib/theme/qrc/dark-purple/folder_open.svg deleted file mode 100644 index 1da2dcda..00000000 --- a/lib/theme/qrc/dark-purple/folder_open.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/hard_drive.svg b/lib/theme/qrc/dark-purple/hard_drive.svg deleted file mode 100644 index 548e7145..00000000 --- a/lib/theme/qrc/dark-purple/hard_drive.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/help.svg b/lib/theme/qrc/dark-purple/help.svg deleted file mode 100644 index afffda9e..00000000 --- a/lib/theme/qrc/dark-purple/help.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/hmovetoolbar.svg b/lib/theme/qrc/dark-purple/hmovetoolbar.svg deleted file mode 100644 index e28bea6a..00000000 --- a/lib/theme/qrc/dark-purple/hmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark-purple/home_directory.svg b/lib/theme/qrc/dark-purple/home_directory.svg deleted file mode 100644 index f6bf95f5..00000000 --- a/lib/theme/qrc/dark-purple/home_directory.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/hseptoolbar.svg b/lib/theme/qrc/dark-purple/hseptoolbar.svg deleted file mode 100644 index 6962c39a..00000000 --- a/lib/theme/qrc/dark-purple/hseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/left_arrow.svg b/lib/theme/qrc/dark-purple/left_arrow.svg deleted file mode 100644 index d14b6388..00000000 --- a/lib/theme/qrc/dark-purple/left_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/left_arrow_disabled.svg b/lib/theme/qrc/dark-purple/left_arrow_disabled.svg deleted file mode 100644 index 7b21edfe..00000000 --- a/lib/theme/qrc/dark-purple/left_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/left_arrow_hover.svg b/lib/theme/qrc/dark-purple/left_arrow_hover.svg deleted file mode 100644 index ff2f949c..00000000 --- a/lib/theme/qrc/dark-purple/left_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/maximize.svg b/lib/theme/qrc/dark-purple/maximize.svg deleted file mode 100644 index 6be858e7..00000000 --- a/lib/theme/qrc/dark-purple/maximize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/menu.svg b/lib/theme/qrc/dark-purple/menu.svg deleted file mode 100644 index 7faa270a..00000000 --- a/lib/theme/qrc/dark-purple/menu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/message_critical.svg b/lib/theme/qrc/dark-purple/message_critical.svg deleted file mode 100644 index 2cab82b9..00000000 --- a/lib/theme/qrc/dark-purple/message_critical.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/message_information.svg b/lib/theme/qrc/dark-purple/message_information.svg deleted file mode 100644 index 3394b282..00000000 --- a/lib/theme/qrc/dark-purple/message_information.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/message_question.svg b/lib/theme/qrc/dark-purple/message_question.svg deleted file mode 100644 index 96b4e332..00000000 --- a/lib/theme/qrc/dark-purple/message_question.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/message_warning.svg b/lib/theme/qrc/dark-purple/message_warning.svg deleted file mode 100644 index 0675a665..00000000 --- a/lib/theme/qrc/dark-purple/message_warning.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/lib/theme/qrc/dark-purple/minimize.svg b/lib/theme/qrc/dark-purple/minimize.svg deleted file mode 100644 index 3bad637c..00000000 --- a/lib/theme/qrc/dark-purple/minimize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/network_drive.svg b/lib/theme/qrc/dark-purple/network_drive.svg deleted file mode 100644 index 53bff64b..00000000 --- a/lib/theme/qrc/dark-purple/network_drive.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark-purple/radio_checked.svg b/lib/theme/qrc/dark-purple/radio_checked.svg deleted file mode 100644 index e0dbdc94..00000000 --- a/lib/theme/qrc/dark-purple/radio_checked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark-purple/radio_checked_disabled.svg b/lib/theme/qrc/dark-purple/radio_checked_disabled.svg deleted file mode 100644 index f6449bcb..00000000 --- a/lib/theme/qrc/dark-purple/radio_checked_disabled.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark-purple/radio_unchecked.svg b/lib/theme/qrc/dark-purple/radio_unchecked.svg deleted file mode 100644 index 8d8969a0..00000000 --- a/lib/theme/qrc/dark-purple/radio_unchecked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/radio_unchecked_disabled.svg b/lib/theme/qrc/dark-purple/radio_unchecked_disabled.svg deleted file mode 100644 index 14602a11..00000000 --- a/lib/theme/qrc/dark-purple/radio_unchecked_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/restore.svg b/lib/theme/qrc/dark-purple/restore.svg deleted file mode 100644 index 6e590f3e..00000000 --- a/lib/theme/qrc/dark-purple/restore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/right_arrow.svg b/lib/theme/qrc/dark-purple/right_arrow.svg deleted file mode 100644 index 7744f293..00000000 --- a/lib/theme/qrc/dark-purple/right_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/right_arrow_disabled.svg b/lib/theme/qrc/dark-purple/right_arrow_disabled.svg deleted file mode 100644 index 714e70b1..00000000 --- a/lib/theme/qrc/dark-purple/right_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/right_arrow_hover.svg b/lib/theme/qrc/dark-purple/right_arrow_hover.svg deleted file mode 100644 index b9553654..00000000 --- a/lib/theme/qrc/dark-purple/right_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/shade.svg b/lib/theme/qrc/dark-purple/shade.svg deleted file mode 100644 index de46a0e8..00000000 --- a/lib/theme/qrc/dark-purple/shade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/sizegrip.svg b/lib/theme/qrc/dark-purple/sizegrip.svg deleted file mode 100644 index 415c874d..00000000 --- a/lib/theme/qrc/dark-purple/sizegrip.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/stylesheet.qss b/lib/theme/qrc/dark-purple/stylesheet.qss deleted file mode 100644 index 3edc5200..00000000 --- a/lib/theme/qrc/dark-purple/stylesheet.qss +++ /dev/null @@ -1,2510 +0,0 @@ -/* - * BreezeDark stylesheet. - * - * :author: Colin Duquesnoy - * :editor: Alex Huszagh - * :license: MIT, see LICENSE.md - * - * This is originally a fork of QDarkStyleSheet, and is based on Breeze/ - * BreezeDark color scheme, but is in no way affiliated with KDE. - * - * --------------------------------------------------------------------- - * The MIT License (MIT) - * - * Copyright (c) <2013-2014> - * Copyright (c) <2015-2021> - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * --------------------------------------------------------------------- - */ - -/** - * MAIN STYLESHEET - * --------------- - */ - -QToolTip -{ - /* 0.2ex is the smallest value that's not ignored on Windows. */ - border: 0.04em solid #eff0f1; - background-image: none; - background-color: #31363b; - alternate-background-color: #31363b; - color: #eff0f1; - padding: 0.1em; - opacity: 200; -} - -QWidget -{ - color: #eff0f1; - background-color: #31363b; - selection-background-color: #a74bb8; - selection-color: #eff0f1; - background-clip: border; - border-image: none; - - /* QDialogButtonBox icons */ - dialog-cancel-icon: url(:/dark-purple/dialog_cancel.svg); - dialog-close-icon: url(:/dark-purple/dialog_close.svg); - dialog-ok-icon: url(:/dark-purple/dialog_ok.svg); - dialog-open-icon: url(:/dark-purple/dialog_open.svg); - dialog-reset-icon: url(:/dark-purple/dialog_reset.svg); - dialog-save-icon: url(:/dark-purple/dialog_save.svg); - dialog-yes-icon: url(:/dark-purple/dialog_ok.svg); - dialog-help-icon: url(:/dark-purple/dialog_help.svg); - dialog-no-icon: url(:/dark-purple/dialog_no.svg); - dialog-apply-icon: url(:/dark-purple/dialog_ok.svg); - dialog-discard-icon: url(:/dark-purple/dialog_discard.svg); - - /* File icons */ - filedialog-backward-icon: url(:/dark-purple/left_arrow.svg); - filedialog-contentsview-icon: url(:/dark-purple/file_dialog_contents.svg); - filedialog-detailedview-icon: url(:/dark-purple/file_dialog_detailed.svg); - filedialog-end-icon: url(:/dark-purple/file_dialog_end.svg); - filedialog-infoview-icon: url(:/dark-purple/file_dialog_info.svg); - filedialog-listview-icon: url(:/dark-purple/file_dialog_list.svg); - filedialog-new-directory-icon: url(:/dark-purple/folder.svg); - filedialog-parent-directory-icon: url(:/dark-purple/up_arrow.svg); - filedialog-start-icon: url(:/dark-purple/file_dialog_start.svg); - directory-closed-icon: url(:/dark-purple/folder.svg); - directory-icon: url(:/dark-purple/folder.svg); - directory-link-icon: url(:/dark-purple/folder_link.svg); - directory-open-icon: url(:/dark-purple/folder_open.svg); - file-icon: url(:/dark-purple/file.svg); - file-link-icon: url(:/dark-purple/file_link.svg); - home-icon: url(:/dark-purple/home_directory.svg); - - /* QMessageBox icons */ - messagebox-critical-icon: url(:/dark-purple/message_critical.svg); - messagebox-information-icon: url(:/dark-purple/message_information.svg); - messagebox-question-icon: url(:/dark-purple/message_question.svg); - messagebox-warning-icon: url(:/dark-purple/message_warning.svg); - - /* Computer icons */ - computer-icon: url(:/dark-purple/computer.svg); - desktop-icon: url(:/dark-purple/desktop.svg); - cd-icon: url(:/dark-purple/disc_drive.svg); - dvd-icon: url(:/dark-purple/disc_drive.svg); - floppy-icon: url(:/dark-purple/floppy_drive.svg); - harddisk-icon: url(:/dark-purple/hard_drive.svg); - network-icon: url(:/dark-purple/network_drive.svg); - trash-icon: url(:/dark-purple/trash.svg); - - /* Arrow icons */ - uparrow-icon: url(:/dark-purple/up_arrow.svg); - downarrow-icon: url(:/dark-purple/down_arrow.svg); - leftarrow-icon: url(:/dark-purple/left_arrow.svg); - rightarrow-icon: url(:/dark-purple/right_arrow.svg); - backward-icon: url(:/dark-purple/left_arrow.svg); - forward-icon: url(:/dark-purple/right_arrow.svg); - - /* Titlebar icons */ - titlebar-close-icon: url(:/dark-purple/window_close.svg); - titlebar-contexthelp-icon: url(:/dark-purple/help.svg); - titlebar-maximize-icon: url(:/dark-purple/maximize.svg); - titlebar-menu-icon: url(:/dark-purple/menu.svg); - titlebar-minimize-icon: url(:/dark-purple/minimize.svg); - titlebar-normal-icon: url(:/dark-purple/restore.svg); - titlebar-shade-icon: url(:/dark-purple/shade.svg); - titlebar-unshade-icon: url(:/dark-purple/unshade.svg); - - /* Other icons */ - dockwidget-close-icon: url(:/dark-purple/close.svg); - /** - * Only available in Qt6, and causes other issues. See #62. - * lineedit-clear-button-icon: url(:/dark-purple/clear_text.svg); - */ -} - -QWidget:disabled -{ - color: #454545; - background-color: #31363b; -} - -QCheckBox -{ - spacing: 0.23em; - color: #eff0f1; - margin-bottom: 0.09em; - opacity: 200; -} - -QCheckBox:disabled -{ - color: #b0b0b0; -} - -QGroupBox -{ - /* Need to make sure the groupbox doesn't compress below the title. */ - min-height: 1.2em; - border: 0.04em solid #76797c; - border-radius: 0.09em; - /** - * This gives us enough space at the top to ensure we can move the - * title to be inside the guidelines, and the padding at the top - * ensures we have space below the title. - */ - margin-top: 0.5em; - padding-top: 1em; -} - -QGroupBox:focus -{ - border: 0.04em solid #76797c; - border-radius: 0.09em; -} - -QGroupBox::title -{ - /* We need to move 0.6em up to be inside the lines, +1em for padding. */ - top: -1.6em; - subcontrol-origin: content; - subcontrol-position: top center; - background: #31363b; - padding-left: 0.2em; - padding-right: 0.2em; -} - -QGroupBox:flat -{ - border-top: 0.04em solid #626568; - border-left: 0.04em transparent #76797c; - border-right: 0.04em transparent #76797c; - border-bottom: 0.04em transparent #76797c; -} - -QCheckBox::indicator, -QTreeView::indicator, -QGroupBox::indicator -{ - width: 1em; - height: 1em; -} - -QGroupBox::indicator:unchecked, -QGroupBox::indicator:unchecked:focus, -QCheckBox::indicator:unchecked, -QCheckBox::indicator:unchecked:focus, -QTreeView::indicator:unchecked, -QTreeView::indicator:unchecked:focus -{ - border-image: url(:/dark-purple/checkbox_unchecked_disabled.svg); -} - -QGroupBox::indicator:unchecked, -QCheckBox::indicator:unchecked:hover, -QCheckBox::indicator:unchecked:pressed, -QTreeView::indicator:unchecked:hover, -QTreeView::indicator:unchecked:pressed, -QGroupBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:pressed -{ - border: none; - border-image: url(:/dark-purple/checkbox_unchecked.svg); -} - -QCheckBox::indicator:checked, -QTreeView::indicator:checked, -QGroupBox::indicator:checked -{ - border-image: url(:/dark-purple/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QCheckBox::indicator:checked:focus, -QCheckBox::indicator:checked:pressed, -QTreeView::indicator:checked:hover, -QTreeView::indicator:checked:focus, -QTreeView::indicator:checked:pressed, -QGroupBox::indicator:checked:hover, -QGroupBox::indicator:checked:focus, -QGroupBox::indicator:checked:pressed -{ - border: none; - border-image: url(:/dark-purple/checkbox_checked.svg); -} - -QCheckBox::indicator:indeterminate, -QTreeView::indicator:indeterminate -{ - border-image: url(:/dark-purple/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:focus, -QCheckBox::indicator:indeterminate:hover, -QCheckBox::indicator:indeterminate:pressed, -QTreeView::indicator:indeterminate:focus, -QTreeView::indicator:indeterminate:hover, -QTreeView::indicator:indeterminate:pressed -{ - border-image: url(:/dark-purple/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:disabled, -QTreeView::indicator:indeterminate:disabled -{ - border-image: url(:/dark-purple/checkbox_indeterminate_disabled.svg); -} - -QCheckBox::indicator:checked:disabled, -QTreeView::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled -{ - border-image: url(:/dark-purple/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QTreeView::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled -{ - border-image: url(:/dark-purple/checkbox_unchecked_disabled.svg); -} - -QRadioButton -{ - spacing: 0.23em; - outline: none; - color: #eff0f1; - margin-bottom: 0.09em; -} - -QRadioButton:disabled -{ - color: #76797c; -} - -QRadioButton::indicator -{ - width: 1em; - height: 1em; -} - -QRadioButton::indicator:unchecked, -QRadioButton::indicator:unchecked:focus -{ - border-image: url(:/dark-purple/radio_unchecked_disabled.svg); -} - -QRadioButton::indicator:unchecked:hover, -QRadioButton::indicator:unchecked:pressed -{ - border: none; - outline: none; - border-image: url(:/dark-purple/radio_unchecked.svg); -} - -QRadioButton::indicator:checked -{ - border: none; - outline: none; - border-image: url(:/dark-purple/radio_checked.svg); -} - -QRadioButton::indicator:checked:hover, -QRadioButton::indicator:checked:focus, -QRadioButton::indicator:checked:pressed -{ - border: none; - outline: none; - border-image: url(:/dark-purple/radio_checked.svg); -} - -QRadioButton::indicator:checked:disabled -{ - outline: none; - border-image: url(:/dark-purple/radio_checked_disabled.svg); -} - -QRadioButton::indicator:unchecked:disabled -{ - border-image: url(:/dark-purple/radio_unchecked_disabled.svg); -} - -QMenuBar -{ - background-color: #31363b; - color: #eff0f1; -} - -QMenuBar::item -{ - background: transparent; -} - -QMenuBar::item:selected -{ - background: transparent; - border: 0.04em solid #a74bb8; -} - -QMenuBar::item:disabled -{ - color: #76797c; -} - -QMenuBar::item:pressed -{ - background-color: #a74bb8; - color: #eff0f1; - margin-bottom: -0.09em; - padding-bottom: 0.09em; -} - -QMenu -{ - color: #eff0f1; - margin: 0.09em; -} - -QMenu::icon -{ - margin: 0.23em; -} - -QMenu::item -{ - /* Add extra padding on the right for the QMenu arrow */ - padding: 0.23em 1.5em 0.23em 1.3em; - border: 0.09em solid transparent; - background: transparent; -} - -QMenu::item:selected -{ - color: #eff0f1; - background-color: #a74bb8; -} - -QMenu::item:selected:disabled -{ - background-color: #31363b; -} - -QMenu::item:disabled -{ - color: #76797c; -} - -QMenu::indicator -{ - width: 0.8em; - height: 0.8em; - /* To align with QMenu::icon, which has a 0.23em margin. */ - margin-left: 0.3em; - subcontrol-position: center left; -} - -QMenu::indicator:non-exclusive:unchecked -{ - border-image: url(:/dark-purple/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:unchecked:selected -{ - border-image: url(:/dark-purple/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:checked -{ - border-image: url(:/dark-purple/checkbox_checked.svg); -} - -QMenu::indicator:non-exclusive:checked:selected -{ - border-image: url(:/dark-purple/checkbox_checked.svg); -} - -QMenu::indicator:exclusive:unchecked -{ - border-image: url(:/dark-purple/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:unchecked:selected -{ - border-image: url(:/dark-purple/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:checked -{ - border-image: url(:/dark-purple/radio_checked.svg); -} - -QMenu::indicator:exclusive:checked:selected -{ - border-image: url(:/dark-purple/radio_checked.svg); -} - -QMenu::right-arrow -{ - margin: 0.23em; - border-image: url(:/dark-purple/right_arrow.svg); - width: 0.5em; - height: 0.8em; -} - -QMenu::right-arrow:disabled -{ - border-image: url(:/dark-purple/right_arrow_disabled.svg); -} - -QAbstractItemView -{ - alternate-background-color: #31363b; - color: #eff0f1; - border: 0.09em solid #31363b; - border-radius: 0.09em; -} - -QTabWidget:focus, -QCheckBox:focus, -QRadioButton:focus, -QSlider:focus -{ - border: none; -} - -QLineEdit -{ - background-color: #1d2023; - padding: 0.23em; - border-style: solid; - border: 0.04em solid #76797c; - border-radius: 0.09em; - color: #eff0f1; -} - -QAbstractScrollArea -{ - border-radius: 0.09em; - border: 0.09em solid #76797c; - background-color: transparent; -} - -/** - * This is the background for the box in the bottom-right corner - * whene both scrollbars are active. - */ -QAbstractScrollArea::corner -{ - background: #31363b; -} - -/** - * Can't do the KDE style of where the scrollbar handle - * becomes light on the hover, and only when the handle - * is hovered does it become stylized. This is because - * both the handle and the background events are treated - * together. - */ -QScrollBar:horizontal -{ - background-color: #1d2023; - height: 0.65em; - margin: 0.13em 0.65em 0.13em 0.65em; - border: 0.04em transparent #1d2023; - border-radius: 0.17em; -} - -QScrollBar:horizontal:hover -{ - background-color: #76797c; -} - -QScrollBar::handle:horizontal -{ - background-color: #a74bb8; - border: 0.04em solid #a74bb8; - min-width: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:horizontal:hover -{ - background-color: #a74bb8; - border: 0.04em solid #a74bb8; -} - -QScrollBar::add-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/dark-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/dark-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover, -QScrollBar::add-line:horizontal:on -{ - border-image: url(:/dark-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal:hover, -QScrollBar::sub-line:horizontal:on -{ - border-image: url(:/dark-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, -QScrollBar::down-arrow:horizontal -{ - background: none; -} - -QScrollBar::add-page:horizontal, -QScrollBar::sub-page:horizontal -{ - background: none; -} - -QScrollBar:vertical -{ - background-color: #1d2023; - width: 0.65em; - margin: 0.65em 0.13em 0.65em 0.13em; - border: 0.04em transparent #1d2023; - border-radius: 0.17em; -} - -QScrollBar:vertical:hover -{ - background-color: #76797c; -} - -QScrollBar::handle:vertical -{ - background-color: #a74bb8; - border: 0.04em solid #a74bb8; - min-height: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:vertical:hover -{ - background-color: #a74bb8; - border: 0.04em solid #a74bb8; -} - -QScrollBar::sub-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/dark-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/dark-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover, -QScrollBar::sub-line:vertical:on -{ - border-image: url(:/dark-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical:hover, -QScrollBar::add-line:vertical:on -{ - border-image: url(:/dark-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:vertical, -QScrollBar::down-arrow:vertical -{ - background: none; -} - -QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical -{ - background: none; -} - -QTextEdit -{ - background-color: #1d2023; - color: #eff0f1; - border: 0.04em solid #76797c; -} - -QPlainTextEdit -{ - background-color: #1d2023; - color: #eff0f1; - border-radius: 0.09em; - border: 0.04em solid #76797c; -} - -QSizeGrip -{ - border-image: url(:/dark-purple/sizegrip.svg); - width: 0.5em; - height: 0.5em; -} - -/** - * Set the separator to be transparent, since the dock has a border. - * On PyQt6, neither the border nor the background seem to be respected. - */ -QMainWindow::separator -{ - border: 0.09em transparent #76797c; - background: transparent; -} - -QMenu::separator -{ - height: 0.09em; - background-color: #76797c; - padding-left: 0.2em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-left: 0.41em; - margin-right: 0.41em; -} - -QFrame[frameShape="2"], /* QFrame::Panel == 0x0003 */ -QFrame[frameShape="3"], /* QFrame::WinPanel == 0x0003 */ -QFrame[frameShape="4"], /* QFrame::HLine == 0x0004 */ -QFrame[frameShape="5"], /* QFrame::VLine == 0x0005 */ -QFrame[frameShape="6"] /* QFrame::StyledPanel == 0x0006 */ -{ - border-width: 0.04em; - padding: 0.09em; - border-style: solid; - border-color: #31363b; - background-color: #76797c; - border-radius: 0.23em; -} - -/* Provide highlighting for frame objects. */ -QFrame[frameShape="2"]:hover, -QFrame[frameShape="3"]:hover, -QFrame[frameShape="4"]:hover, -QFrame[frameShape="5"]:hover, -QFrame[frameShape="6"]:hover -{ - border: 0.04em solid #a74bb8; -} - -/* Don't provide an outline if we have a widget that takes up the space. */ -QFrame[frameShape] QAbstractItemView:hover -{ - border: 0em solid black; -} - -/** - * Note: I can't really change the background of the toolbars - * independently, since KDE Breeze has different colors for the - * window bar and the rest of the UI. The top toolbar uses - * the window style, and the rest use the application style, - * which we can't do. - */ -QToolBar -{ - font-weight: bold; -} - -QToolBar:horizontal -{ - background: 0.09em solid #31363b; -} - -QToolBar:vertical -{ - background: 0.09em solid #31363b; -} - -QToolBar::handle:horizontal -{ - border-image: url(:/dark-purple/hmovetoolbar.svg); -} - -QToolBar::handle:vertical -{ - border-image: url(:/dark-purple/vmovetoolbar.svg); -} - -QToolBar::separator:horizontal -{ - border-image: url(:/dark-purple/hseptoolbar.svg); -} - -QToolBar::separator:vertical -{ - border-image: url(:/dark-purple/vseptoolbar.svg); -} - -QToolBar QToolButton -{ - font-weight: bold; - border: 0.04em transparent black; - padding-left: 0.2em; - padding-right: 0.3em; -} - -QToolBar QToolButton:hover -{ - border: 0.04em solid #a74bb8; -} - -QToolBar QToolButton:pressed -{ - border: 0.04em solid #a74bb8; - /* The padding doesn't inherit from `QToolBar QToolButton`, so leave it in. */ - padding-left: 0.2em; - padding-right: 0.3em; -} - -/** - * Special rules for a QFileDialog. - * - * Due to the widgets, we get rid of the min sizes to allow them - * to pack closer together, and ensure we have enough padding for - * the drop-down menu in the popup. - */ -QDialog QToolBar QToolButton[popupMode="0"], -QDialog QToolBar QToolButton[popupMode="1"] -{ - padding-left: 0.1em; - padding-right: 0.1em; -} - -QDialog QToolBar QToolButton[popupMode="2"] -{ - padding-left: 0.1em; - padding-right: 0.7em; -} - -QPushButton -{ - color: #eff0f1; - background-color: #31363b; - border: 0.04em solid #76797c; - padding: 0.23em; - border-radius: 0.09em; - outline: none; - min-height: 1.1em; -} - -QPushButton:flat, -QPushButton:flat:hover -{ - border: 0.04em transparent #76797c; -} - -QComboBox:open, -QPushButton:open -{ - border-width: 0.04em; - border-color: #76797c; -} - -QComboBox:closed, -QPushButton:closed -{ - border-width: 0.04em; - border-color: #76797c; -} - -QPushButton:disabled -{ - background-color: #31363b; - border-width: 0.04em; - border-color: #76797c; - border-style: solid; - padding-top: 0.23em; - padding-bottom: 0.23em; - padding-left: 1ex; - padding-right: 1ex; - border-radius: 0.04em; - color: #454545; -} - -QPushButton:focus -{ - color: #eff0f1; -} - -QPushButton:pressed -{ - background-color: #454a4f; - padding-top: -0.65em; - padding-bottom: -0.74em; - color: #eff0f1; -} - -QComboBox -{ - border: 0.04em solid #76797c; - border-radius: 0.09em; - padding: 0.23em; - min-width: 2.5em; -} - -QComboBox:editable -{ - background-color: #1d2023; -} - -QPushButton:checked -{ - background-color: #626568; - border: 0.04em solid #76797c; - color: #eff0f1; -} - -QPushButton:hover -{ - background-color: #31363b; - border: 0.04em solid #a74bb8; - color: #eff0f1; -} - -QPushButton:checked:hover -{ - background-color: #626568; - border: 0.04em solid #a74bb8; - color: #eff0f1; -} - -QComboBox:hover, -QComboBox:focus, -QAbstractSpinBox:hover, -QAbstractSpinBox:focus, -QLineEdit:hover, -QLineEdit:focus, -QTextEdit:hover, -QTextEdit:focus, -QPlainTextEdit:hover, -QPlainTextEdit:focus, -QAbstractView:hover, -QTreeView:hover, -QTreeView:focus -{ - border: 0.04em solid #a74bb8; - color: #eff0f1; -} - -QComboBox:hover:pressed:!editable, -QPushButton:hover:pressed, -QAbstractSpinBox:hover:pressed, -QLineEdit:hover:pressed, -QTextEdit:hover:pressed, -QPlainTextEdit:hover:pressed, -QAbstractView:hover:pressed, -QTreeView:hover:pressed -{ - background-color: #31363b; -} - -QColumnView -{ - border: 0.04em transparent #31363b; -} - -QColumnViewGrip -{ - border-image: url(:/dark-purple/sizegrip.svg); -} - -/* Each column in the view is a QAbstractItemView. */ -QColumnView QAbstractItemView -{ - border: 0.04em transparent #a74bb8; -} - -/** - * In order to set consistency between Qt5 and Qt6, we need - * to ensure that we do the following steps: - * 1. Set a consistent `max-height` in the item. Anything - * below `0.8em` will cause clipping, so set that value - * to ensure the icon isn't larger. - * 2. Set padding to ensure the item is properly padded. - * 3. Set `0.2em` margins on the top and bottom of the arrows, - * and `0.1em` on the left and right to ensure the arrows - * are properly padded and have the same size. - * - * The size consistency only works if both the `::item` subcontrol - * `max-height` and the `::*-arrow` subcontrol `margin` is set. - */ -QColumnView QAbstractItemView::item -{ - padding: 0.23em; - max-width: 0.5em; - max-height: 0.8em; -} - -QColumnView QAbstractItemView::right-arrow -{ - image: url(:/dark-purple/right_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::right-arrow:selected, -QColumnView QAbstractItemView::right-arrow:hover -{ - image: url(:/dark-purple/right_arrow_hover.svg); -} - -QColumnView QAbstractItemView::left-arrow -{ - image: url(:/dark-purple/left_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::left-arrow:selected, -QColumnView QAbstractItemView::left-arrow:hover -{ - image: url(:/dark-purple/left_arrow_hover.svg); -} - -QComboBox:hover:pressed:editable -{ - background-color: #1d2023; -} - -QComboBox QAbstractItemView -{ - /* This happens for the drop-down menu always, whether editable or not.*/ - background-color: #1d2023; - selection-background-color: #872a99; - outline-color: 0em; - border-radius: 0.09em; -} - -QComboBox::drop-down -{ - subcontrol-origin: padding; - subcontrol-position: top right; - width: 0.65em; - - border-left-width: 0em; - border-left-style: solid; - border-top-right-radius: 0.13em; - border-bottom-right-radius: 0.13em; -} - -QComboBox::down-arrow -{ - border-image: url(:/dark-purple/down_arrow_disabled.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QComboBox::down-arrow:on, -QComboBox::down-arrow:hover, -QComboBox::down-arrow:focus -{ - border-image: url(:/dark-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QAbstractSpinBox -{ - padding: 0.23em; - border: 0.09em solid #76797c; - background-color: #1d2023; - color: #eff0f1; - border-radius: 0.09em; - min-width: 3em; - min-height: 1em; -} - -QAbstractSpinBox:hover -{ - border: 0.09em solid #a74bb8; -} - -QAbstractSpinBox:up-button, -QAbstractSpinBox:up-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center right; - padding-right: 0.1em; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox:down-button, -QAbstractSpinBox:down-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center left; - padding-left: 0.1em; - width: 0.8em; - height: 0.5em; -} - -/** - * Bug fixes for elongated items in QSpinBoxes. - * By default, the items are bounded by `down-button` - * and `up-button`, so this doesn't actually affect the styling. - * - * This does however affect some custom styling using - * QStyle.CC_ComboBox, which affects QDateEdit. This cannot - * be selected using QDateEdit, since it uses a global style. - * This sounds nonsensical, because CC_ComboBox isn't a spin box, - * but through trial and error, this is in fact the case. - * - * Affects #40. - */ -QAbstractSpinBox::up-arrow, -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off, -QAbstractSpinBox::up-arrow:!off:!disabled:hover, -QAbstractSpinBox::down-arrow, -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off, -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - border-image: none; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox::up-arrow -{ - image: url(:/dark-purple/up_arrow.svg); -} - -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off -{ - image: url(:/dark-purple/up_arrow_disabled.svg); -} - -QAbstractSpinBox::up-arrow:hover -{ - image: url(:/dark-purple/up_arrow_hover.svg); -} - -QAbstractSpinBox::down-arrow -{ - image: url(:/dark-purple/down_arrow.svg); -} - -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off -{ - image: url(:/dark-purple/down_arrow_disabled.svg); -} - -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - image: url(:/dark-purple/down_arrow_hover.svg); -} - -QDoubleSpinBox -{ - min-width: 4em; -} - -/** - * `QCalendarWidget QAbstractItemView:enabled` sets the color, background - * color, and selection color for active dates in the view. - * `QCalendarWidget QAbstractItemView:enabled` sets the disabled dates. - */ -QCalendarWidget QAbstractItemView:enabled -{ - color: #eff0f1; - selection-color: #eff0f1; - selection-background-color: #a74bb8; -} - -/* Won't take hover events. */ -QPrevNextCalButton -{ - min-width: 0.8em; - min-height: 1.2em; - qproperty-iconSize: 0px 0px; -} - -QPrevNextCalButton#qt_calendar_nextmonth -{ - image: url(:/dark-purple/calendar_next.svg); -} - -QPrevNextCalButton#qt_calendar_prevmonth -{ - image: url(:/dark-purple/calendar_previous.svg); -} - -/** - * Setting for the month and year displays and drop-down menu for the - * month select. We style this separately because we want a drop-down - * indicator in the bottom right, unlike the normal QToolButton. - */ -QCalendarWidget QToolButton -{ - background-color: transparent; - border: 0.04em solid #76797c; - border-radius: 0.09em; - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; - min-height: 1.1em; -} - -QCalendarWidget QToolButton:hover -{ - border: 0.04em solid #a74bb8; -} - -QCalendarWidget QToolButton:checked, -QCalendarWidget QToolButton:pressed -{ - background-color: #a74bb8; - padding: 0.23em; - padding-right: 1.2em; - min-height: 1.3em; - outline: none; -} - -/** - * The QCalendarWidget for QDateTimeEdit seems to improperly - * style the year QToolButton, which has an object name - * `qt_datetimedit_calendar`, so ensure we style it as well. - */ -QCalendarWidget QToolButton::menu-indicator, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-indicator -{ - border-image: none; - image: url(:/dark-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - top: -0.7ex; - left: -0.09em; - padding-right: -1.11em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -QCalendarWidget QToolButton::menu-arrow, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-arrow -{ - border-image: none; - image: url(:/dark-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -/** - * Setting for the year button. Both the month select and the year - * select are QToolButtons, and both are auto-raised. The year - * button however has the popup mode set to `DelayedPopup`. - */ -QCalendarWidget QToolButton[autoRaise="true"][popupMode="0"] -{ - padding: 0.23em; -} - -QCalendarWidget QSpinBox -{ - max-height: 1.5em; - min-width: 3.5em; - margin: 0em; - margin-top: 0.2em; - padding: 0em; - outline: 0em; - padding-left: 0.5em; -} - -QLabel -{ - border: 0em solid black; -} - -/* BORDERS */ -QTabWidget::pane -{ - padding: 0.23em; - margin: 0.04em; -} - -QTabWidget::pane:top -{ - border: 0.04em solid #76797c; - top: -0.04em; -} - -QTabWidget::pane:bottom -{ - border: 0.04em solid #76797c; - bottom: -0.04em; -} - -QTabWidget::pane:left -{ - border: 0.04em solid #76797c; - left: -0.04em; -} - -QTabWidget::pane:right -{ - border: 0.04em solid #76797c; - right: -0.04em; -} - -QTabBar -{ - qproperty-drawBase: 0; - left: 0.23em; - border-radius: 0.13em; - /** - * Note: this is the underline for each tab title. It's not - * documented, and this took forever to track down. At least - * 10 hours have been wasted trying to turn off this line, - * do not deleted this comment. - */ - selection-color: transparent; -} - -QTabBar:focus -{ - border: 0em transparent black; -} - -QTabBar::close-button -{ - /* Doesn't seem possible to resize these buttons */ - border-image: url(:/dark-purple/transparent.svg); - image: url(:/dark-purple/close.svg); - background: transparent; -} - -QTabBar::close-button:hover -{ - image: url(:/dark-purple/close_hover.svg); -} - -QTabBar::close-button:pressed -{ - image: url(:/dark-purple/close_pressed.svg); -} - -/* TOP TABS */ -QTabBar::tab:top, -QTabBar::tab:top:last, -QTabBar::tab:top:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-left: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-top: 0.09em solid #a74bb8; - background-color: #31363b; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-right: 0.04em solid #76797c; - border-bottom: 0.04em solid #76797c; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:next-selected -{ - border-right: 0.04em transparent #2c3034; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:first:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* BOTTOM TABS */ -QTabBar::tab:bottom, -QTabBar::tab:bottom:last, -QTabBar::tab:bottom:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-left: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-bottom: 0.09em solid #a74bb8; - background-color: #31363b; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-top: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:next-selected -{ - border-right: 0.04em transparent #2c3034; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:first:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -/* LEFT TABS */ -QTabBar::tab:left, -QTabBar::tab:left:last, -QTabBar::tab:left:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-top: 0.09em solid #a74bb8; - border-bottom: 0.04em solid #76797c; - border-left: 0.04em solid #76797c; - background-color: #31363b; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-top: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:previous-selected -{ - border-top: 0.04em transparent #2c3034; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:first:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* RIGHT TABS */ -QTabBar::tab:right, -QTabBar::tab:right:last, -QTabBar::tab:right:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-top: 0.09em solid #a74bb8; - border-bottom: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - background-color: #31363b; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-top: 0.04em solid #76797c; - border-left: 0.04em solid #76797c; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:previous-selected -{ - border-top: 0.04em transparent #2c3034; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:first:hover -{ - background-color: rgba(204, 61, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -/** - * Special styles for triangular QTabWidgets. - * These ignore the border attributes, and the border and - * text color seems to be set via the `QTabBar::tab` color - * property. This seemingly cannot be changed. - * - * The rounded shapes are 0-3, and the triangular ones are 4-7. - * - * The QTabBar outline doesn't respect on QTabBar::tab: - * border-color - * outline-color - */ -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - /* Need a dark color without alpha channel since it affects the text. */ - color: #a74bb8; - background-color: #31363b; - padding: 0.23em; -} - -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one -{ - min-width: 50px; -} - -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - min-height: 50px; -} - -QTabBar[shape="4"]::tab:!selected, -QTabBar[shape="5"]::tab:!selected, -QTabBar[shape="6"]::tab:!selected, -QTabBar[shape="7"]::tab:!selected -{ - color: #eff0f1; - background-color: #2c3034; -} - -/** - * Increase padding on the opposite side of the icon to avoid text clipping. - * - * BUG: The padding works for North, West, and East in Qt5, South does not - * work. All tab positions work for triangular tabs in Qt6. - */ -QTabBar[shape="4"][tabsClosable="true"]::tab, -QTabBar[shape="5"][tabsClosable="true"]::tab -{ - padding-left: 0.5em; -} - -QTabBar[shape="6"][tabsClosable="true"]::tab -{ - padding-bottom: 0.5em; -} - -QTabBar[shape="7"][tabsClosable="true"]::tab -{ - padding-top: 0.5em; -} - -/** -* Undo the padding for the tab. -* -* Enumerated values are North, South, West, East in that order, -* from 4-7. -* -* NOTE: Any higher padding will clip the icon. -*/ -QTabBar[shape="4"]::close-button, -QTabBar[shape="5"]::close-button -{ - padding-left: -0.12em; -} - -QTabBar[shape="6"]::close-button -{ - padding-bottom: -0.18em; -} - -QTabBar[shape="7"]::close-button -{ - padding-top: -0.18em; -} - -QDockWidget -{ - background: #31363b; - /** - * It doesn't seem possible to change the border of the - * QDockWidget without changing the content margins. - */ - /** - * This is a bug fix so we can handle hover, pressed, and other events. - * Reference: https://stackoverflow.com/questions/32145080/qdockwidget-float-close-button-hover-images - */ - titlebar-close-icon: url(:/dark-purple/transparent.svg); - titlebar-normal-icon: url(:/dark-purple/transparent.svg); -} - -/** - * Don't style the title, since it gives a weird, missing border - * around the rest of the dock widget, which the remaining border - * cannot be removed. - * - * There is a bug in non-Breeze styles, where the icons are small. It - * doesn't change if we use `image` instead of `border-image`, nor if - * we use `qproperty-icon`, etc. The icon seem to be half the size - * of our desired values. - */ -QDockWidget::close-button, -QDockWidget::float-button -{ - border: 0.04em solid transparent; - border-radius: 0.09em; - background: transparent; - /* Maximum icon size for buttons */ - icon-size: 14px; -} - -QDockWidget::float-button -{ - border-image: url(:/dark-purple/transparent.svg); - image: url(:/dark-purple/undock.svg); -} - -QDockWidget::float-button:hover -{ - image: url(:/dark-purple/undock_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::float-button:pressed -{ - image: url(:/dark-purple/undock_hover.svg); -} - -QDockWidget::close-button -{ - border-image: url(:/dark-purple/transparent.svg); - image: url(:/dark-purple/close.svg); -} - -QDockWidget::close-button:hover -{ - image: url(:/dark-purple/close_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::close-button:pressed -{ - image: url(:/dark-purple/close_pressed.svg); -} - -QTreeView, -QListView -{ - background-color: #1d2023; - border: 0em solid black; -} - -QTreeView:selected, -QTreeView:!selected, -QListView:selected, -QListView:!selected -{ - border: 0em solid black; -} - -QTreeView::branch:has-siblings -{ - border-image: url(:/dark-purple/vline.svg); - image: none; -} - -/* These branch indicators don't scale */ -QTreeView::branch:!has-siblings -{ - border-image: none; - image: none; -} - -QTreeView::branch:has-siblings:adjoins-item -{ - border-image: url(:/dark-purple/branch_more.svg); -} - -QTreeView::branch:!has-children:!has-siblings:adjoins-item -{ - border-image: url(:/dark-purple/branch_end.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:closed:has-children:has-siblings -{ - image: url(:/dark-purple/branch_closed.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed:hover, -QTreeView::branch:closed:has-children:has-siblings:hover -{ - image: url(:/dark-purple/branch_closed_hover.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:open:has-children:!has-siblings -{ - border-image: url(:/dark-purple/branch_end_arrow.svg); -} - -QTreeView::branch:closed:has-children:has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - border-image: url(:/dark-purple/branch_more_arrow.svg); -} - -QTreeView::branch:open:has-children:!has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - image: url(:/dark-purple/branch_open.svg); -} - -QTreeView::branch:open:has-children:!has-siblings:hover, -QTreeView::branch:open:has-children:has-siblings:hover -{ - image: url(:/dark-purple/branch_open_hover.svg); -} - -QListView -{ - /* Give space for elements aligned left or right. */ - padding: 0.2em; -} - -QTableView::item, -QListView::item, -QTreeView::item -{ - padding: 0.13em; - color: #eff0f1; -} - -QTreeView::item -{ - /** - * Need to set the background color in Qt6, or else - * the QTreeView indicators use the style defaults, - * along with the box model, which conflicts with our - * theme (except with hover/focus/selected pseudostates). - * - * Affects issue #51. - */ - background-color: #1d2023; - outline: 0; -} - -QTableView::item:!selected:hover, -QListView::item:!selected:hover, -QTreeView::item:!selected:hover -{ - background-color: rgba(204, 61, 232, 0.1); - outline: 0; - color: #eff0f1; - padding: 0.13em; -} - -QAbstractItemView::item QLineEdit -{ - border: 0em transparent black; - /* - * The top/bottom padding causes the editable widget to conceal text. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/69 - */ - padding: 0em; -} - -QSlider::handle:horizontal, -QSlider::handle:vertical -{ - background: #1d2023; - border: 0.04em solid #626568; - width: 0.7em; - height: 0.7em; - border-radius: 0.35em; -} - -QSlider:horizontal -{ - height: 2em; -} - -QSlider:vertical -{ - width: 2em; -} - -QSlider::handle:horizontal -{ - margin: -0.23em 0; -} - -QSlider::handle:vertical -{ - margin: 0 -0.23em; -} - -QSlider::groove:horizontal, -QSlider::groove:vertical -{ - background: #2c3034; - border: 0em solid #31363b; - border-radius: 0.19em; -} - -QSlider::groove:horizontal -{ - height: 0.4em; -} - -QSlider::groove:vertical -{ - width: 0.4em; -} - -QSlider::handle:horizontal:hover, -QSlider::handle:horizontal:focus, -QSlider::handle:vertical:hover, -QSlider::handle:vertical:focus -{ - border: 0.04em solid #a74bb8; -} - -QSlider::handle:horizontal:!focus:!hover, -QSlider::handle:vertical:!focus:!hover -{ - border: 0.04em solid #626568; -} - -QSlider::sub-page:horizontal, -QSlider::add-page:vertical -{ - background: #a74bb8; - border-radius: 0.19em; -} - -QSlider::add-page:horizontal, -QSlider::sub-page:vertical -{ - background: #626568; - border-radius: 0.19em; -} - -/* QToolButton */ -/** - * QToolButton's that have a push button need to be styled differently, - * depending on whether there are actions (a menu) associated with it. - * This is signaled by a drop-down arrow on the right of the push button. - * Unfortunately, there's no good property to determine this. The property - * we need is `QWidget::actions`, however, it's a method and not a - * property.Note that the drop-down menu is not signaled by any of the - * following: - * popupMode: any pop-up mode does not affect the right arrow style. - * arrowType: only replaces the icon. - * toolButtonStyle: this is almost always set to icon only, even with text. - * text: can have a drop-down menu with or without text. - * - * Notably, we need to ensure we don't pad the widgets in the following - * cases: - * 1. If the QToolButton is auto-raised. - * This adds undesired padding in`QFileDialog`. These widgets - * have text, even though no text is visible. This is not the - * default, so it won't affect most situations. - * 2. If the QToolButton does not have text. - * Normally, text-less buttons do not have a menu, and this - * is required for #47, since the padding affects the scroll - * bar icons in QTabBar. This causes major issues in the - * UI, so disable the padding by default. - * - * The padding can affect the placement of icons and other things - * inside the toolbutton: near the menu-button in QFileDialog, - * the clear text icon is misplaced vertically, making it nearly - * illegible. - * - * We provide special styles for a custom, dynamic property to - * override the padding decisions with or without a menu. - * To force styling as if there is a menu, set the `hasMenu` property - * to true. Setting `hasMenu` to false will style as if there is no menu. - * You can use `QWidget::setProperty` to set this property dynamically. - * - * The affected issues are #22, #28, #47. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/22 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/28 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/47 - */ - -/** - * Use an overly specific selector here to ensure no margins, - * or for the default QToolButton. We must have `autoRaise="false"` - * and `text` to have padding, so just add a `hasMenu="false"` to - * undo the padding in that case. Also add selectors for QDialog - * if a menu is explicitly forbidden. - */ -QToolButton, -QToolButton[hasMenu="false"][autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="2"] -{ - margin: 0em; - padding: 0em; -} - -QToolButton[autoRaise="false"] -{ - background-color: #31363b; - border: 0.04em solid #76797c; - border-radius: 0.09em; -} - -QToolButton[autoRaise="true"] -{ - background-color: #31363b; - border: 0.04em solid transparent; -} - -/* Add selectors for the QDialog if a menu is explicitly requested. */ -QToolButton[hasMenu="true"], -QToolButton[autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="2"] -{ - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; -} - -QToolButton:hover -{ - border: 0.04em solid #a74bb8; -} - -QToolButton:checked, -QToolButton:pressed -{ - border: 0.04em solid #a74bb8; - background-color: #a74bb8; -} - -QToolButton::right-arrow, -QToolButton::left-arrow, -QToolButton::up-arrow, -QToolButton::down-arrow -{ - /** - * Do not set the arrow width/height here. It causes - * small icons in Qt6, and doesn't affect the styling - * in Qt5. Both look ideal without manually specified sizes. - */ - subcontrol-origin: content; - subcontrol-position: center; - margin: 0em; - padding: 0em; -} - -QToolButton::right-arrow:enabled -{ - image: url(:/dark-purple/right_arrow.svg); -} - -QToolButton::left-arrow:enabled -{ - image: url(:/dark-purple/left_arrow.svg); -} - -QToolButton::up-arrow:enabled -{ - image: url(:/dark-purple/up_arrow.svg); -} - -QToolButton::down-arrow:enabled -{ - image: url(:/dark-purple/down_arrow.svg); -} - -QToolButton::right-arrow:disabled -{ - image: url(:/dark-purple/right_arrow_disabled.svg); -} - -QToolButton::left-arrow:disabled -{ - image: url(:/dark-purple/left_arrow_disabled.svg); -} - -QToolButton::up-arrow:disabled -{ - image: url(:/dark-purple/up_arrow_disabled.svg); -} - -QToolButton::down-arrow:disabled -{ - image: url(:/dark-purple/down_arrow_disabled.svg); -} - -QToolButton::menu-indicator -{ - border-image: none; - image: url(:/dark-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - left: -0.09em; - /* -1.2em + 0.09em */ - padding-right: -1.11em; - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: right; -} - -/** - * Special rule for the drop-down indicator in a QFileDialog. - * We want these to be more compact, hence the smaller padding. - */ -QDialog QToolBar QToolButton[popupMode="2"]::menu-indicator -{ - padding-right: -0.7em; -} - -QToolButton::menu-arrow -{ - border-image: none; - image: url(:/dark-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button -{ - border-top-right-radius: 0.5em; - border-bottom-right-radius: 0.5em; - /* 1ex width + 0.4ex for border + no text = 2ex allocated above */ - width: 1.3em; - padding: 0.23em; - outline: none; -} - -QToolButton::menu-button::menu-arrow -{ - left: -0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button:hover -{ - background-color: transparent; -} - -QToolButton::menu-button:pressed -{ - background-color: transparent; - padding: 0.23em; - outline: none; -} - -QTableView -{ - border: 0em solid black; - gridline-color: #31363b; - background-color: #1d2023; -} - -QTableView:!selected, -QTableView:selected -{ - border: 0em solid black; -} - -QTableView -{ - border-radius: 0em; -} - -QAbstractItemView::item -{ - color: #eff0f1; -} - -QAbstractItemView::item:pressed -{ - background: #872a99; - color: #eff0f1; -} - -QAbstractItemView::item:selected:!active -{ - background: rgba(204, 61, 232, 0.1); -} - -/* Use background with qlineargradient to avoid ugly border on widget. */ -QAbstractItemView::item:selected:active -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 #872a99, - stop: 1 #872a99 - ); - color: #eff0f1; -} - -QAbstractItemView::item:selected:hover -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 #851f99, - stop: 1 #851f99 - ); - color: #eff0f1; -} - -QHeaderView -{ - background-color: #31363b; - border: 0.04em transparent; - border-radius: 0em; - margin: 0em; - padding: 0em; -} - -QHeaderView::section -{ - background-color: #31363b; - border: 0.04em solid #76797c; - color: #eff0f1; - border-radius: 0em; - padding: 0em 0.23em 0em 0.23em; - text-align: center; -} - -QHeaderView::section::vertical::first, -QHeaderView::section::vertical::only-one -{ - border-top: 0.04em solid #76797c; -} - -QHeaderView::section::vertical -{ - border-top: transparent; -} - -QHeaderView::section::horizontal::first, -QHeaderView::section::horizontal::only-one -{ - border-left: 0.04em solid #76797c; -} - -QHeaderView::section::horizontal -{ - border-left: transparent; -} - -QHeaderView[showSortIndicator="true"]::section::horizontal -{ - /* Same as the width of the arrow subcontrols below. */ - padding-right: 0.8em; -} - -QHeaderView::section:checked -{ - color: #ffffff; - background-color: #57335e; -} - -/* Note that this doesn't work for QTreeView unless the header is clickable */ -QHeaderView::section:hover, -QHeaderView::section::horizontal::first:hover, -QHeaderView::section::horizontal::only-one:hover, -QHeaderView::section::vertical::first:hover, -QHeaderView::section::vertical::only-one:hover -{ - border: 0.04em solid #a74bb8; -} - -QHeaderView[showSortIndicator="true"]::down-arrow -{ - image: url(:/dark-purple/down_arrow.svg); - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - /** - * Qt5 and Qt6 have different ideas of the padding of the - * arrow subcontrols: using `padding-left` to ensure that - * the width is undone fixes the padding of the content by - * an extra `0.8em` in Qt6, but doesn't affect Qt5. - */ - padding-right: 0.09em; - padding-left: -0.8em; -} - -QHeaderView[showSortIndicator="true"]::up-arrow -{ - image: url(:/dark-purple/up_arrow.svg); - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - padding-left: -0.8em; -} - -QTableView QTableCornerButton::section -{ - background-color: #31363b; - border: 0.04em transparent #76797c; - border-top: 0.04em solid #76797c; - border-left: 0.04em solid #76797c; - border-radius: 0em; -} - -/* No hover event */ -QTableView QTableCornerButton:hover -{ - border: 0.04em transparent #76797c; -} - -QTableView QTableCornerButton::section:pressed -{ - border: 0.04em solid #a74bb8; - border-radius: 0em; -} - -QToolBox -{ - padding: 0.23em; - border: 0.09em transparent black; -} - -QToolBox::tab -{ - border-bottom: 0.09em solid #76797c; - margin-left: 1.5em; -} - -QToolBox::tab:selected, -QToolBox::tab:hover -{ - border-bottom: 0.09em solid #a74bb8; -} - -QSplitter::handle -{ - border: 0.09em solid #2c3034; - background: -0.5em solid #2c3034; - max-width: 0em; - max-height: 0em; -} - -/** - * It's not possible to get satisfactory rounded borders here. - * If you set the border to be negative, while adjusting the - * widths, you get an asymmetrical curve which produces an - * unappealing border. - */ -QProgressBar:horizontal, -QProgressBar:vertical -{ - background-color: #626568; - border: 0.9em solid #31363b; - border-radius: 0.13em; - padding: 0em; -} - -QProgressBar:horizontal -{ - height: 0.2em; - min-width: 6em; - text-align: right; - padding-left: -0.03em; - padding-right: -0.03em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-right: 1.3em; -} - -QProgressBar:vertical -{ - width: 0.2em; - min-height: 6em; - text-align: bottom; - padding-top: -0.03em; - padding-bottom: -0.03em; - margin-left: 0.2em; - margin-right: 0.2em; - margin-bottom: 0.41em; -} - -QProgressBar::chunk:horizontal, -QProgressBar::chunk:vertical -{ - background-color: #a74bb8; - border: 0.9em transparent; - border-radius: 0.08em; -} - -QScrollArea, -QScrollArea:focus, -QScrollArea:hover -{ - border: 0em solid black; -} - -/* ICONS */ -/** - * Qt's built-in icons can look pretty bad if the system theme - * is a different color than the current one. For example, when - * using a dark theme, with a light UI, the `Ok` button is greyed - * out for an about dialog. - * - * QDialogButtonBox will apply for all standard buttons in all standard - * widgets, such as QMessageBox, etc. However, we do need to override - * standard icons elsewhere. - * - * The rest of the icons make little sense to implement: - * Qt uses native window decorations. - * Qt normally uses native file dialogs, which look nicer. - * Media controls are used in custom widgets, which aren't standard. - */ -QDialogButtonBox -{ - dialogbuttonbox-buttons-have-icons: true; - - dialog-cancel-icon: url(:/dark-purple/dialog_cancel.svg); - dialog-close-icon: url(:/dark-purple/dialog_close.svg); - dialog-ok-icon: url(:/dark-purple/dialog_ok.svg); - dialog-open-icon: url(:/dark-purple/dialog_open.svg); - dialog-reset-icon: url(:/dark-purple/dialog_reset.svg); - dialog-save-icon: url(:/dark-purple/dialog_save.svg); - /** - * No support yet for overriding saveall. - * dialog-saveall-icon: url(:/dark-purple/dialog_saveall.svg); - */ - dialog-yes-icon: url(:/dark-purple/dialog_ok.svg); - dialog-help-icon: url(:/dark-purple/dialog_help.svg); - dialog-no-icon: url(:/dark-purple/dialog_no.svg); - dialog-apply-icon: url(:/dark-purple/dialog_ok.svg); - dialog-discard-icon: url(:/dark-purple/dialog_discard.svg); -} - -/* Set some styles for these custom dialog buttons */ -QDialogButtonBox QPushButton, -QMessageBox QPushButton -{ - min-height: 1.1em; - min-width: 5em; -} - -/** - * Special rules for creating a custom titlebar. This can only work - * when setting the Qt property `isTitlebar` to `true`. - */ -QWidget[isTitlebar="true"], -QWidget[isTitlebar="true"] * -{ - background-color: #2c3034; -} - -/** - * Special rules for creating a border around a top-level frame of a window. - * This can only work when setting the Qt property `isWindow` to `true`. - * We've manually enumerated border widths from 1-5 below. - */ -QFrame[isWindow="true"], -QFrame[frameShape][isWindow="true"] -{ - border: 0px transparent #2c3034; -} - -QFrame[isWindow="true"][windowFrame="1"], -QFrame[frameShape][isWindow="true"][windowFrame="1"] -{ - border: 1px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="2"], -QFrame[frameShape][isWindow="true"][windowFrame="2"] -{ - border: 2px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="3"], -QFrame[frameShape][isWindow="true"][windowFrame="3"] -{ - border: 3px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="4"], -QFrame[frameShape][isWindow="true"][windowFrame="4"] -{ - border: 4px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="5"], -QFrame[frameShape][isWindow="true"][windowFrame="5"] -{ - border: 5px solid #2c3034; - border-radius: 3px; -} diff --git a/lib/theme/qrc/dark-purple/transparent.svg b/lib/theme/qrc/dark-purple/transparent.svg deleted file mode 100644 index 3a8ca5cf..00000000 --- a/lib/theme/qrc/dark-purple/transparent.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/theme/qrc/dark-purple/trash.svg b/lib/theme/qrc/dark-purple/trash.svg deleted file mode 100644 index d20d3d8e..00000000 --- a/lib/theme/qrc/dark-purple/trash.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/undock.svg b/lib/theme/qrc/dark-purple/undock.svg deleted file mode 100644 index 886196c8..00000000 --- a/lib/theme/qrc/dark-purple/undock.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/undock_hover.svg b/lib/theme/qrc/dark-purple/undock_hover.svg deleted file mode 100644 index 5f1c72c6..00000000 --- a/lib/theme/qrc/dark-purple/undock_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/undock_hover_pressed.svg b/lib/theme/qrc/dark-purple/undock_hover_pressed.svg deleted file mode 100644 index f6fc8d21..00000000 --- a/lib/theme/qrc/dark-purple/undock_hover_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/unshade.svg b/lib/theme/qrc/dark-purple/unshade.svg deleted file mode 100644 index bc0c9a87..00000000 --- a/lib/theme/qrc/dark-purple/unshade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/up_arrow.svg b/lib/theme/qrc/dark-purple/up_arrow.svg deleted file mode 100644 index b6ec6185..00000000 --- a/lib/theme/qrc/dark-purple/up_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/up_arrow_disabled.svg b/lib/theme/qrc/dark-purple/up_arrow_disabled.svg deleted file mode 100644 index de13ac29..00000000 --- a/lib/theme/qrc/dark-purple/up_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/up_arrow_hover.svg b/lib/theme/qrc/dark-purple/up_arrow_hover.svg deleted file mode 100644 index e73c6227..00000000 --- a/lib/theme/qrc/dark-purple/up_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/vline.svg b/lib/theme/qrc/dark-purple/vline.svg deleted file mode 100644 index 2ecc5cee..00000000 --- a/lib/theme/qrc/dark-purple/vline.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark-purple/vmovetoolbar.svg b/lib/theme/qrc/dark-purple/vmovetoolbar.svg deleted file mode 100644 index e626a335..00000000 --- a/lib/theme/qrc/dark-purple/vmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark-purple/vseptoolbar.svg b/lib/theme/qrc/dark-purple/vseptoolbar.svg deleted file mode 100644 index 3ab1a7b2..00000000 --- a/lib/theme/qrc/dark-purple/vseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark-purple/window_close.svg b/lib/theme/qrc/dark-purple/window_close.svg deleted file mode 100644 index 3649be5d..00000000 --- a/lib/theme/qrc/dark-purple/window_close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/branch_closed.svg b/lib/theme/qrc/dark/branch_closed.svg deleted file mode 100644 index e8e8d91d..00000000 --- a/lib/theme/qrc/dark/branch_closed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/branch_closed_hover.svg b/lib/theme/qrc/dark/branch_closed_hover.svg deleted file mode 100644 index 71873dc2..00000000 --- a/lib/theme/qrc/dark/branch_closed_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/branch_end.svg b/lib/theme/qrc/dark/branch_end.svg deleted file mode 100644 index 7e8888ed..00000000 --- a/lib/theme/qrc/dark/branch_end.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/branch_end_arrow.svg b/lib/theme/qrc/dark/branch_end_arrow.svg deleted file mode 100644 index d5aaabde..00000000 --- a/lib/theme/qrc/dark/branch_end_arrow.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/branch_more.svg b/lib/theme/qrc/dark/branch_more.svg deleted file mode 100644 index a6d2b6e7..00000000 --- a/lib/theme/qrc/dark/branch_more.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/branch_more_arrow.svg b/lib/theme/qrc/dark/branch_more_arrow.svg deleted file mode 100644 index 6c4935a5..00000000 --- a/lib/theme/qrc/dark/branch_more_arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark/branch_open.svg b/lib/theme/qrc/dark/branch_open.svg deleted file mode 100644 index 031aa44b..00000000 --- a/lib/theme/qrc/dark/branch_open.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/branch_open_hover.svg b/lib/theme/qrc/dark/branch_open_hover.svg deleted file mode 100644 index 30815214..00000000 --- a/lib/theme/qrc/dark/branch_open_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/calendar_next.svg b/lib/theme/qrc/dark/calendar_next.svg deleted file mode 100644 index 61318930..00000000 --- a/lib/theme/qrc/dark/calendar_next.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/calendar_previous.svg b/lib/theme/qrc/dark/calendar_previous.svg deleted file mode 100644 index 8382adf0..00000000 --- a/lib/theme/qrc/dark/calendar_previous.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/checkbox_checked.svg b/lib/theme/qrc/dark/checkbox_checked.svg deleted file mode 100644 index bbee00e9..00000000 --- a/lib/theme/qrc/dark/checkbox_checked.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark/checkbox_checked_disabled.svg b/lib/theme/qrc/dark/checkbox_checked_disabled.svg deleted file mode 100644 index 0508e928..00000000 --- a/lib/theme/qrc/dark/checkbox_checked_disabled.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark/checkbox_indeterminate.svg b/lib/theme/qrc/dark/checkbox_indeterminate.svg deleted file mode 100644 index d891422c..00000000 --- a/lib/theme/qrc/dark/checkbox_indeterminate.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/checkbox_indeterminate_disabled.svg b/lib/theme/qrc/dark/checkbox_indeterminate_disabled.svg deleted file mode 100644 index 2ba0edf7..00000000 --- a/lib/theme/qrc/dark/checkbox_indeterminate_disabled.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/checkbox_unchecked.svg b/lib/theme/qrc/dark/checkbox_unchecked.svg deleted file mode 100644 index ec56272c..00000000 --- a/lib/theme/qrc/dark/checkbox_unchecked.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/checkbox_unchecked_disabled.svg b/lib/theme/qrc/dark/checkbox_unchecked_disabled.svg deleted file mode 100644 index bedb0fd4..00000000 --- a/lib/theme/qrc/dark/checkbox_unchecked_disabled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/clear_text.svg b/lib/theme/qrc/dark/clear_text.svg deleted file mode 100644 index 9fbc2fe7..00000000 --- a/lib/theme/qrc/dark/clear_text.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/close.svg b/lib/theme/qrc/dark/close.svg deleted file mode 100644 index df9dde2d..00000000 --- a/lib/theme/qrc/dark/close.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/close_hover.svg b/lib/theme/qrc/dark/close_hover.svg deleted file mode 100644 index cc627287..00000000 --- a/lib/theme/qrc/dark/close_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/close_pressed.svg b/lib/theme/qrc/dark/close_pressed.svg deleted file mode 100644 index 1d4dc43c..00000000 --- a/lib/theme/qrc/dark/close_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/computer.svg b/lib/theme/qrc/dark/computer.svg deleted file mode 100644 index 0d912807..00000000 --- a/lib/theme/qrc/dark/computer.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/desktop.svg b/lib/theme/qrc/dark/desktop.svg deleted file mode 100644 index fcfa8a96..00000000 --- a/lib/theme/qrc/dark/desktop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark/dialog_cancel.svg b/lib/theme/qrc/dark/dialog_cancel.svg deleted file mode 100644 index 226499fc..00000000 --- a/lib/theme/qrc/dark/dialog_cancel.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_close.svg b/lib/theme/qrc/dark/dialog_close.svg deleted file mode 100644 index c2174c23..00000000 --- a/lib/theme/qrc/dark/dialog_close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_discard.svg b/lib/theme/qrc/dark/dialog_discard.svg deleted file mode 100644 index b711b61b..00000000 --- a/lib/theme/qrc/dark/dialog_discard.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_help.svg b/lib/theme/qrc/dark/dialog_help.svg deleted file mode 100644 index 46153b67..00000000 --- a/lib/theme/qrc/dark/dialog_help.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_no.svg b/lib/theme/qrc/dark/dialog_no.svg deleted file mode 100644 index e9f5245b..00000000 --- a/lib/theme/qrc/dark/dialog_no.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_ok.svg b/lib/theme/qrc/dark/dialog_ok.svg deleted file mode 100644 index d236e77a..00000000 --- a/lib/theme/qrc/dark/dialog_ok.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_open.svg b/lib/theme/qrc/dark/dialog_open.svg deleted file mode 100644 index 5fa68c35..00000000 --- a/lib/theme/qrc/dark/dialog_open.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_reset.svg b/lib/theme/qrc/dark/dialog_reset.svg deleted file mode 100644 index bb5839ab..00000000 --- a/lib/theme/qrc/dark/dialog_reset.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/dialog_save.svg b/lib/theme/qrc/dark/dialog_save.svg deleted file mode 100644 index e85535fe..00000000 --- a/lib/theme/qrc/dark/dialog_save.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/disc_drive.svg b/lib/theme/qrc/dark/disc_drive.svg deleted file mode 100644 index a5a0b407..00000000 --- a/lib/theme/qrc/dark/disc_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/down_arrow.svg b/lib/theme/qrc/dark/down_arrow.svg deleted file mode 100644 index 358a36f8..00000000 --- a/lib/theme/qrc/dark/down_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/down_arrow_disabled.svg b/lib/theme/qrc/dark/down_arrow_disabled.svg deleted file mode 100644 index 6e3f6501..00000000 --- a/lib/theme/qrc/dark/down_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/down_arrow_hover.svg b/lib/theme/qrc/dark/down_arrow_hover.svg deleted file mode 100644 index 3a1c09a3..00000000 --- a/lib/theme/qrc/dark/down_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/file.svg b/lib/theme/qrc/dark/file.svg deleted file mode 100644 index f400c7d7..00000000 --- a/lib/theme/qrc/dark/file.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/file_dialog_contents.svg b/lib/theme/qrc/dark/file_dialog_contents.svg deleted file mode 100644 index 41605587..00000000 --- a/lib/theme/qrc/dark/file_dialog_contents.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/dark/file_dialog_detailed.svg b/lib/theme/qrc/dark/file_dialog_detailed.svg deleted file mode 100644 index 37a33c3a..00000000 --- a/lib/theme/qrc/dark/file_dialog_detailed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/dark/file_dialog_end.svg b/lib/theme/qrc/dark/file_dialog_end.svg deleted file mode 100644 index a33ae903..00000000 --- a/lib/theme/qrc/dark/file_dialog_end.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/file_dialog_info.svg b/lib/theme/qrc/dark/file_dialog_info.svg deleted file mode 100644 index c0e79390..00000000 --- a/lib/theme/qrc/dark/file_dialog_info.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/lib/theme/qrc/dark/file_dialog_list.svg b/lib/theme/qrc/dark/file_dialog_list.svg deleted file mode 100644 index 9ef3ac38..00000000 --- a/lib/theme/qrc/dark/file_dialog_list.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/file_dialog_start.svg b/lib/theme/qrc/dark/file_dialog_start.svg deleted file mode 100644 index 6b529a1a..00000000 --- a/lib/theme/qrc/dark/file_dialog_start.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/dark/file_link.svg b/lib/theme/qrc/dark/file_link.svg deleted file mode 100644 index 1602812e..00000000 --- a/lib/theme/qrc/dark/file_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/floppy_drive.svg b/lib/theme/qrc/dark/floppy_drive.svg deleted file mode 100644 index df46c001..00000000 --- a/lib/theme/qrc/dark/floppy_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/folder.svg b/lib/theme/qrc/dark/folder.svg deleted file mode 100644 index be0596d8..00000000 --- a/lib/theme/qrc/dark/folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/folder_link.svg b/lib/theme/qrc/dark/folder_link.svg deleted file mode 100644 index c76c09de..00000000 --- a/lib/theme/qrc/dark/folder_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/folder_open.svg b/lib/theme/qrc/dark/folder_open.svg deleted file mode 100644 index 1da2dcda..00000000 --- a/lib/theme/qrc/dark/folder_open.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/hard_drive.svg b/lib/theme/qrc/dark/hard_drive.svg deleted file mode 100644 index 548e7145..00000000 --- a/lib/theme/qrc/dark/hard_drive.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/lib/theme/qrc/dark/help.svg b/lib/theme/qrc/dark/help.svg deleted file mode 100644 index afffda9e..00000000 --- a/lib/theme/qrc/dark/help.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/hmovetoolbar.svg b/lib/theme/qrc/dark/hmovetoolbar.svg deleted file mode 100644 index e28bea6a..00000000 --- a/lib/theme/qrc/dark/hmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark/home_directory.svg b/lib/theme/qrc/dark/home_directory.svg deleted file mode 100644 index f6bf95f5..00000000 --- a/lib/theme/qrc/dark/home_directory.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/hseptoolbar.svg b/lib/theme/qrc/dark/hseptoolbar.svg deleted file mode 100644 index 6962c39a..00000000 --- a/lib/theme/qrc/dark/hseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/left_arrow.svg b/lib/theme/qrc/dark/left_arrow.svg deleted file mode 100644 index d14b6388..00000000 --- a/lib/theme/qrc/dark/left_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/left_arrow_disabled.svg b/lib/theme/qrc/dark/left_arrow_disabled.svg deleted file mode 100644 index 7b21edfe..00000000 --- a/lib/theme/qrc/dark/left_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/left_arrow_hover.svg b/lib/theme/qrc/dark/left_arrow_hover.svg deleted file mode 100644 index 143ef685..00000000 --- a/lib/theme/qrc/dark/left_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/maximize.svg b/lib/theme/qrc/dark/maximize.svg deleted file mode 100644 index 6be858e7..00000000 --- a/lib/theme/qrc/dark/maximize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/menu.svg b/lib/theme/qrc/dark/menu.svg deleted file mode 100644 index 7faa270a..00000000 --- a/lib/theme/qrc/dark/menu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/message_critical.svg b/lib/theme/qrc/dark/message_critical.svg deleted file mode 100644 index 2cab82b9..00000000 --- a/lib/theme/qrc/dark/message_critical.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/message_information.svg b/lib/theme/qrc/dark/message_information.svg deleted file mode 100644 index 3394b282..00000000 --- a/lib/theme/qrc/dark/message_information.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/dark/message_question.svg b/lib/theme/qrc/dark/message_question.svg deleted file mode 100644 index 96b4e332..00000000 --- a/lib/theme/qrc/dark/message_question.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/dark/message_warning.svg b/lib/theme/qrc/dark/message_warning.svg deleted file mode 100644 index 0675a665..00000000 --- a/lib/theme/qrc/dark/message_warning.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/lib/theme/qrc/dark/minimize.svg b/lib/theme/qrc/dark/minimize.svg deleted file mode 100644 index 3bad637c..00000000 --- a/lib/theme/qrc/dark/minimize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/network_drive.svg b/lib/theme/qrc/dark/network_drive.svg deleted file mode 100644 index 53bff64b..00000000 --- a/lib/theme/qrc/dark/network_drive.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/dark/radio_checked.svg b/lib/theme/qrc/dark/radio_checked.svg deleted file mode 100644 index 5b1ebecc..00000000 --- a/lib/theme/qrc/dark/radio_checked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark/radio_checked_disabled.svg b/lib/theme/qrc/dark/radio_checked_disabled.svg deleted file mode 100644 index f6449bcb..00000000 --- a/lib/theme/qrc/dark/radio_checked_disabled.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark/radio_unchecked.svg b/lib/theme/qrc/dark/radio_unchecked.svg deleted file mode 100644 index a84b48f1..00000000 --- a/lib/theme/qrc/dark/radio_unchecked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/radio_unchecked_disabled.svg b/lib/theme/qrc/dark/radio_unchecked_disabled.svg deleted file mode 100644 index 14602a11..00000000 --- a/lib/theme/qrc/dark/radio_unchecked_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/restore.svg b/lib/theme/qrc/dark/restore.svg deleted file mode 100644 index 6e590f3e..00000000 --- a/lib/theme/qrc/dark/restore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/right_arrow.svg b/lib/theme/qrc/dark/right_arrow.svg deleted file mode 100644 index 7744f293..00000000 --- a/lib/theme/qrc/dark/right_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/right_arrow_disabled.svg b/lib/theme/qrc/dark/right_arrow_disabled.svg deleted file mode 100644 index 714e70b1..00000000 --- a/lib/theme/qrc/dark/right_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/right_arrow_hover.svg b/lib/theme/qrc/dark/right_arrow_hover.svg deleted file mode 100644 index df3f76c0..00000000 --- a/lib/theme/qrc/dark/right_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/shade.svg b/lib/theme/qrc/dark/shade.svg deleted file mode 100644 index de46a0e8..00000000 --- a/lib/theme/qrc/dark/shade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/sizegrip.svg b/lib/theme/qrc/dark/sizegrip.svg deleted file mode 100644 index 415c874d..00000000 --- a/lib/theme/qrc/dark/sizegrip.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/stylesheet.qss b/lib/theme/qrc/dark/stylesheet.qss deleted file mode 100644 index 51fdad0f..00000000 --- a/lib/theme/qrc/dark/stylesheet.qss +++ /dev/null @@ -1,2510 +0,0 @@ -/* - * BreezeDark stylesheet. - * - * :author: Colin Duquesnoy - * :editor: Alex Huszagh - * :license: MIT, see LICENSE.md - * - * This is originally a fork of QDarkStyleSheet, and is based on Breeze/ - * BreezeDark color scheme, but is in no way affiliated with KDE. - * - * --------------------------------------------------------------------- - * The MIT License (MIT) - * - * Copyright (c) <2013-2014> - * Copyright (c) <2015-2021> - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * --------------------------------------------------------------------- - */ - -/** - * MAIN STYLESHEET - * --------------- - */ - -QToolTip -{ - /* 0.2ex is the smallest value that's not ignored on Windows. */ - border: 0.04em solid #eff0f1; - background-image: none; - background-color: #31363b; - alternate-background-color: #31363b; - color: #eff0f1; - padding: 0.1em; - opacity: 200; -} - -QWidget -{ - color: #eff0f1; - background-color: #31363b; - selection-background-color: #3daee9; - selection-color: #eff0f1; - background-clip: border; - border-image: none; - - /* QDialogButtonBox icons */ - dialog-cancel-icon: url(:/dark/dialog_cancel.svg); - dialog-close-icon: url(:/dark/dialog_close.svg); - dialog-ok-icon: url(:/dark/dialog_ok.svg); - dialog-open-icon: url(:/dark/dialog_open.svg); - dialog-reset-icon: url(:/dark/dialog_reset.svg); - dialog-save-icon: url(:/dark/dialog_save.svg); - dialog-yes-icon: url(:/dark/dialog_ok.svg); - dialog-help-icon: url(:/dark/dialog_help.svg); - dialog-no-icon: url(:/dark/dialog_no.svg); - dialog-apply-icon: url(:/dark/dialog_ok.svg); - dialog-discard-icon: url(:/dark/dialog_discard.svg); - - /* File icons */ - filedialog-backward-icon: url(:/dark/left_arrow.svg); - filedialog-contentsview-icon: url(:/dark/file_dialog_contents.svg); - filedialog-detailedview-icon: url(:/dark/file_dialog_detailed.svg); - filedialog-end-icon: url(:/dark/file_dialog_end.svg); - filedialog-infoview-icon: url(:/dark/file_dialog_info.svg); - filedialog-listview-icon: url(:/dark/file_dialog_list.svg); - filedialog-new-directory-icon: url(:/dark/folder.svg); - filedialog-parent-directory-icon: url(:/dark/up_arrow.svg); - filedialog-start-icon: url(:/dark/file_dialog_start.svg); - directory-closed-icon: url(:/dark/folder.svg); - directory-icon: url(:/dark/folder.svg); - directory-link-icon: url(:/dark/folder_link.svg); - directory-open-icon: url(:/dark/folder_open.svg); - file-icon: url(:/dark/file.svg); - file-link-icon: url(:/dark/file_link.svg); - home-icon: url(:/dark/home_directory.svg); - - /* QMessageBox icons */ - messagebox-critical-icon: url(:/dark/message_critical.svg); - messagebox-information-icon: url(:/dark/message_information.svg); - messagebox-question-icon: url(:/dark/message_question.svg); - messagebox-warning-icon: url(:/dark/message_warning.svg); - - /* Computer icons */ - computer-icon: url(:/dark/computer.svg); - desktop-icon: url(:/dark/desktop.svg); - cd-icon: url(:/dark/disc_drive.svg); - dvd-icon: url(:/dark/disc_drive.svg); - floppy-icon: url(:/dark/floppy_drive.svg); - harddisk-icon: url(:/dark/hard_drive.svg); - network-icon: url(:/dark/network_drive.svg); - trash-icon: url(:/dark/trash.svg); - - /* Arrow icons */ - uparrow-icon: url(:/dark/up_arrow.svg); - downarrow-icon: url(:/dark/down_arrow.svg); - leftarrow-icon: url(:/dark/left_arrow.svg); - rightarrow-icon: url(:/dark/right_arrow.svg); - backward-icon: url(:/dark/left_arrow.svg); - forward-icon: url(:/dark/right_arrow.svg); - - /* Titlebar icons */ - titlebar-close-icon: url(:/dark/window_close.svg); - titlebar-contexthelp-icon: url(:/dark/help.svg); - titlebar-maximize-icon: url(:/dark/maximize.svg); - titlebar-menu-icon: url(:/dark/menu.svg); - titlebar-minimize-icon: url(:/dark/minimize.svg); - titlebar-normal-icon: url(:/dark/restore.svg); - titlebar-shade-icon: url(:/dark/shade.svg); - titlebar-unshade-icon: url(:/dark/unshade.svg); - - /* Other icons */ - dockwidget-close-icon: url(:/dark/close.svg); - /** - * Only available in Qt6, and causes other issues. See #62. - * lineedit-clear-button-icon: url(:/dark/clear_text.svg); - */ -} - -QWidget:disabled -{ - color: #454545; - background-color: #31363b; -} - -QCheckBox -{ - spacing: 0.23em; - color: #eff0f1; - margin-bottom: 0.09em; - opacity: 200; -} - -QCheckBox:disabled -{ - color: #b0b0b0; -} - -QGroupBox -{ - /* Need to make sure the groupbox doesn't compress below the title. */ - min-height: 1.2em; - border: 0.04em solid #76797c; - border-radius: 0.09em; - /** - * This gives us enough space at the top to ensure we can move the - * title to be inside the guidelines, and the padding at the top - * ensures we have space below the title. - */ - margin-top: 0.5em; - padding-top: 1em; -} - -QGroupBox:focus -{ - border: 0.04em solid #76797c; - border-radius: 0.09em; -} - -QGroupBox::title -{ - /* We need to move 0.6em up to be inside the lines, +1em for padding. */ - top: -1.6em; - subcontrol-origin: content; - subcontrol-position: top center; - background: #31363b; - padding-left: 0.2em; - padding-right: 0.2em; -} - -QGroupBox:flat -{ - border-top: 0.04em solid #626568; - border-left: 0.04em transparent #76797c; - border-right: 0.04em transparent #76797c; - border-bottom: 0.04em transparent #76797c; -} - -QCheckBox::indicator, -QTreeView::indicator, -QGroupBox::indicator -{ - width: 1em; - height: 1em; -} - -QGroupBox::indicator:unchecked, -QGroupBox::indicator:unchecked:focus, -QCheckBox::indicator:unchecked, -QCheckBox::indicator:unchecked:focus, -QTreeView::indicator:unchecked, -QTreeView::indicator:unchecked:focus -{ - border-image: url(:/dark/checkbox_unchecked_disabled.svg); -} - -QGroupBox::indicator:unchecked, -QCheckBox::indicator:unchecked:hover, -QCheckBox::indicator:unchecked:pressed, -QTreeView::indicator:unchecked:hover, -QTreeView::indicator:unchecked:pressed, -QGroupBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:pressed -{ - border: none; - border-image: url(:/dark/checkbox_unchecked.svg); -} - -QCheckBox::indicator:checked, -QTreeView::indicator:checked, -QGroupBox::indicator:checked -{ - border-image: url(:/dark/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QCheckBox::indicator:checked:focus, -QCheckBox::indicator:checked:pressed, -QTreeView::indicator:checked:hover, -QTreeView::indicator:checked:focus, -QTreeView::indicator:checked:pressed, -QGroupBox::indicator:checked:hover, -QGroupBox::indicator:checked:focus, -QGroupBox::indicator:checked:pressed -{ - border: none; - border-image: url(:/dark/checkbox_checked.svg); -} - -QCheckBox::indicator:indeterminate, -QTreeView::indicator:indeterminate -{ - border-image: url(:/dark/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:focus, -QCheckBox::indicator:indeterminate:hover, -QCheckBox::indicator:indeterminate:pressed, -QTreeView::indicator:indeterminate:focus, -QTreeView::indicator:indeterminate:hover, -QTreeView::indicator:indeterminate:pressed -{ - border-image: url(:/dark/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:disabled, -QTreeView::indicator:indeterminate:disabled -{ - border-image: url(:/dark/checkbox_indeterminate_disabled.svg); -} - -QCheckBox::indicator:checked:disabled, -QTreeView::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled -{ - border-image: url(:/dark/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QTreeView::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled -{ - border-image: url(:/dark/checkbox_unchecked_disabled.svg); -} - -QRadioButton -{ - spacing: 0.23em; - outline: none; - color: #eff0f1; - margin-bottom: 0.09em; -} - -QRadioButton:disabled -{ - color: #76797c; -} - -QRadioButton::indicator -{ - width: 1em; - height: 1em; -} - -QRadioButton::indicator:unchecked, -QRadioButton::indicator:unchecked:focus -{ - border-image: url(:/dark/radio_unchecked_disabled.svg); -} - -QRadioButton::indicator:unchecked:hover, -QRadioButton::indicator:unchecked:pressed -{ - border: none; - outline: none; - border-image: url(:/dark/radio_unchecked.svg); -} - -QRadioButton::indicator:checked -{ - border: none; - outline: none; - border-image: url(:/dark/radio_checked.svg); -} - -QRadioButton::indicator:checked:hover, -QRadioButton::indicator:checked:focus, -QRadioButton::indicator:checked:pressed -{ - border: none; - outline: none; - border-image: url(:/dark/radio_checked.svg); -} - -QRadioButton::indicator:checked:disabled -{ - outline: none; - border-image: url(:/dark/radio_checked_disabled.svg); -} - -QRadioButton::indicator:unchecked:disabled -{ - border-image: url(:/dark/radio_unchecked_disabled.svg); -} - -QMenuBar -{ - background-color: #31363b; - color: #eff0f1; -} - -QMenuBar::item -{ - background: transparent; -} - -QMenuBar::item:selected -{ - background: transparent; - border: 0.04em solid #3daee9; -} - -QMenuBar::item:disabled -{ - color: #76797c; -} - -QMenuBar::item:pressed -{ - background-color: #3daee9; - color: #eff0f1; - margin-bottom: -0.09em; - padding-bottom: 0.09em; -} - -QMenu -{ - color: #eff0f1; - margin: 0.09em; -} - -QMenu::icon -{ - margin: 0.23em; -} - -QMenu::item -{ - /* Add extra padding on the right for the QMenu arrow */ - padding: 0.23em 1.5em 0.23em 1.3em; - border: 0.09em solid transparent; - background: transparent; -} - -QMenu::item:selected -{ - color: #eff0f1; - background-color: #3daee9; -} - -QMenu::item:selected:disabled -{ - background-color: #31363b; -} - -QMenu::item:disabled -{ - color: #76797c; -} - -QMenu::indicator -{ - width: 0.8em; - height: 0.8em; - /* To align with QMenu::icon, which has a 0.23em margin. */ - margin-left: 0.3em; - subcontrol-position: center left; -} - -QMenu::indicator:non-exclusive:unchecked -{ - border-image: url(:/dark/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:unchecked:selected -{ - border-image: url(:/dark/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:checked -{ - border-image: url(:/dark/checkbox_checked.svg); -} - -QMenu::indicator:non-exclusive:checked:selected -{ - border-image: url(:/dark/checkbox_checked.svg); -} - -QMenu::indicator:exclusive:unchecked -{ - border-image: url(:/dark/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:unchecked:selected -{ - border-image: url(:/dark/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:checked -{ - border-image: url(:/dark/radio_checked.svg); -} - -QMenu::indicator:exclusive:checked:selected -{ - border-image: url(:/dark/radio_checked.svg); -} - -QMenu::right-arrow -{ - margin: 0.23em; - border-image: url(:/dark/right_arrow.svg); - width: 0.5em; - height: 0.8em; -} - -QMenu::right-arrow:disabled -{ - border-image: url(:/dark/right_arrow_disabled.svg); -} - -QAbstractItemView -{ - alternate-background-color: #31363b; - color: #eff0f1; - border: 0.09em solid #31363b; - border-radius: 0.09em; -} - -QTabWidget:focus, -QCheckBox:focus, -QRadioButton:focus, -QSlider:focus -{ - border: none; -} - -QLineEdit -{ - background-color: #1d2023; - padding: 0.23em; - border-style: solid; - border: 0.04em solid #76797c; - border-radius: 0.09em; - color: #eff0f1; -} - -QAbstractScrollArea -{ - border-radius: 0.09em; - border: 0.09em solid #76797c; - background-color: transparent; -} - -/** - * This is the background for the box in the bottom-right corner - * whene both scrollbars are active. - */ -QAbstractScrollArea::corner -{ - background: #31363b; -} - -/** - * Can't do the KDE style of where the scrollbar handle - * becomes light on the hover, and only when the handle - * is hovered does it become stylized. This is because - * both the handle and the background events are treated - * together. - */ -QScrollBar:horizontal -{ - background-color: #1d2023; - height: 0.65em; - margin: 0.13em 0.65em 0.13em 0.65em; - border: 0.04em transparent #1d2023; - border-radius: 0.17em; -} - -QScrollBar:horizontal:hover -{ - background-color: #76797c; -} - -QScrollBar::handle:horizontal -{ - background-color: #3daee9; - border: 0.04em solid #3daee9; - min-width: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:horizontal:hover -{ - background-color: #3daee9; - border: 0.04em solid #3daee9; -} - -QScrollBar::add-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/dark/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/dark/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover, -QScrollBar::add-line:horizontal:on -{ - border-image: url(:/dark/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal:hover, -QScrollBar::sub-line:horizontal:on -{ - border-image: url(:/dark/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, -QScrollBar::down-arrow:horizontal -{ - background: none; -} - -QScrollBar::add-page:horizontal, -QScrollBar::sub-page:horizontal -{ - background: none; -} - -QScrollBar:vertical -{ - background-color: #1d2023; - width: 0.65em; - margin: 0.65em 0.13em 0.65em 0.13em; - border: 0.04em transparent #1d2023; - border-radius: 0.17em; -} - -QScrollBar:vertical:hover -{ - background-color: #76797c; -} - -QScrollBar::handle:vertical -{ - background-color: #3daee9; - border: 0.04em solid #3daee9; - min-height: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:vertical:hover -{ - background-color: #3daee9; - border: 0.04em solid #3daee9; -} - -QScrollBar::sub-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/dark/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/dark/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover, -QScrollBar::sub-line:vertical:on -{ - border-image: url(:/dark/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical:hover, -QScrollBar::add-line:vertical:on -{ - border-image: url(:/dark/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:vertical, -QScrollBar::down-arrow:vertical -{ - background: none; -} - -QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical -{ - background: none; -} - -QTextEdit -{ - background-color: #1d2023; - color: #eff0f1; - border: 0.04em solid #76797c; -} - -QPlainTextEdit -{ - background-color: #1d2023; - color: #eff0f1; - border-radius: 0.09em; - border: 0.04em solid #76797c; -} - -QSizeGrip -{ - border-image: url(:/dark/sizegrip.svg); - width: 0.5em; - height: 0.5em; -} - -/** - * Set the separator to be transparent, since the dock has a border. - * On PyQt6, neither the border nor the background seem to be respected. - */ -QMainWindow::separator -{ - border: 0.09em transparent #76797c; - background: transparent; -} - -QMenu::separator -{ - height: 0.09em; - background-color: #76797c; - padding-left: 0.2em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-left: 0.41em; - margin-right: 0.41em; -} - -QFrame[frameShape="2"], /* QFrame::Panel == 0x0003 */ -QFrame[frameShape="3"], /* QFrame::WinPanel == 0x0003 */ -QFrame[frameShape="4"], /* QFrame::HLine == 0x0004 */ -QFrame[frameShape="5"], /* QFrame::VLine == 0x0005 */ -QFrame[frameShape="6"] /* QFrame::StyledPanel == 0x0006 */ -{ - border-width: 0.04em; - padding: 0.09em; - border-style: solid; - border-color: #31363b; - background-color: #76797c; - border-radius: 0.23em; -} - -/* Provide highlighting for frame objects. */ -QFrame[frameShape="2"]:hover, -QFrame[frameShape="3"]:hover, -QFrame[frameShape="4"]:hover, -QFrame[frameShape="5"]:hover, -QFrame[frameShape="6"]:hover -{ - border: 0.04em solid #3daee9; -} - -/* Don't provide an outline if we have a widget that takes up the space. */ -QFrame[frameShape] QAbstractItemView:hover -{ - border: 0em solid black; -} - -/** - * Note: I can't really change the background of the toolbars - * independently, since KDE Breeze has different colors for the - * window bar and the rest of the UI. The top toolbar uses - * the window style, and the rest use the application style, - * which we can't do. - */ -QToolBar -{ - font-weight: bold; -} - -QToolBar:horizontal -{ - background: 0.09em solid #31363b; -} - -QToolBar:vertical -{ - background: 0.09em solid #31363b; -} - -QToolBar::handle:horizontal -{ - border-image: url(:/dark/hmovetoolbar.svg); -} - -QToolBar::handle:vertical -{ - border-image: url(:/dark/vmovetoolbar.svg); -} - -QToolBar::separator:horizontal -{ - border-image: url(:/dark/hseptoolbar.svg); -} - -QToolBar::separator:vertical -{ - border-image: url(:/dark/vseptoolbar.svg); -} - -QToolBar QToolButton -{ - font-weight: bold; - border: 0.04em transparent black; - padding-left: 0.2em; - padding-right: 0.3em; -} - -QToolBar QToolButton:hover -{ - border: 0.04em solid #3daee9; -} - -QToolBar QToolButton:pressed -{ - border: 0.04em solid #3daee9; - /* The padding doesn't inherit from `QToolBar QToolButton`, so leave it in. */ - padding-left: 0.2em; - padding-right: 0.3em; -} - -/** - * Special rules for a QFileDialog. - * - * Due to the widgets, we get rid of the min sizes to allow them - * to pack closer together, and ensure we have enough padding for - * the drop-down menu in the popup. - */ -QDialog QToolBar QToolButton[popupMode="0"], -QDialog QToolBar QToolButton[popupMode="1"] -{ - padding-left: 0.1em; - padding-right: 0.1em; -} - -QDialog QToolBar QToolButton[popupMode="2"] -{ - padding-left: 0.1em; - padding-right: 0.7em; -} - -QPushButton -{ - color: #eff0f1; - background-color: #31363b; - border: 0.04em solid #76797c; - padding: 0.23em; - border-radius: 0.09em; - outline: none; - min-height: 1.1em; -} - -QPushButton:flat, -QPushButton:flat:hover -{ - border: 0.04em transparent #76797c; -} - -QComboBox:open, -QPushButton:open -{ - border-width: 0.04em; - border-color: #76797c; -} - -QComboBox:closed, -QPushButton:closed -{ - border-width: 0.04em; - border-color: #76797c; -} - -QPushButton:disabled -{ - background-color: #31363b; - border-width: 0.04em; - border-color: #76797c; - border-style: solid; - padding-top: 0.23em; - padding-bottom: 0.23em; - padding-left: 1ex; - padding-right: 1ex; - border-radius: 0.04em; - color: #454545; -} - -QPushButton:focus -{ - color: #eff0f1; -} - -QPushButton:pressed -{ - background-color: #454a4f; - padding-top: -0.65em; - padding-bottom: -0.74em; - color: #eff0f1; -} - -QComboBox -{ - border: 0.04em solid #76797c; - border-radius: 0.09em; - padding: 0.23em; - min-width: 2.5em; -} - -QComboBox:editable -{ - background-color: #1d2023; -} - -QPushButton:checked -{ - background-color: #626568; - border: 0.04em solid #76797c; - color: #eff0f1; -} - -QPushButton:hover -{ - background-color: #31363b; - border: 0.04em solid #3daee9; - color: #eff0f1; -} - -QPushButton:checked:hover -{ - background-color: #626568; - border: 0.04em solid #3daee9; - color: #eff0f1; -} - -QComboBox:hover, -QComboBox:focus, -QAbstractSpinBox:hover, -QAbstractSpinBox:focus, -QLineEdit:hover, -QLineEdit:focus, -QTextEdit:hover, -QTextEdit:focus, -QPlainTextEdit:hover, -QPlainTextEdit:focus, -QAbstractView:hover, -QTreeView:hover, -QTreeView:focus -{ - border: 0.04em solid #3daee9; - color: #eff0f1; -} - -QComboBox:hover:pressed:!editable, -QPushButton:hover:pressed, -QAbstractSpinBox:hover:pressed, -QLineEdit:hover:pressed, -QTextEdit:hover:pressed, -QPlainTextEdit:hover:pressed, -QAbstractView:hover:pressed, -QTreeView:hover:pressed -{ - background-color: #31363b; -} - -QColumnView -{ - border: 0.04em transparent #31363b; -} - -QColumnViewGrip -{ - border-image: url(:/dark/sizegrip.svg); -} - -/* Each column in the view is a QAbstractItemView. */ -QColumnView QAbstractItemView -{ - border: 0.04em transparent #3daee9; -} - -/** - * In order to set consistency between Qt5 and Qt6, we need - * to ensure that we do the following steps: - * 1. Set a consistent `max-height` in the item. Anything - * below `0.8em` will cause clipping, so set that value - * to ensure the icon isn't larger. - * 2. Set padding to ensure the item is properly padded. - * 3. Set `0.2em` margins on the top and bottom of the arrows, - * and `0.1em` on the left and right to ensure the arrows - * are properly padded and have the same size. - * - * The size consistency only works if both the `::item` subcontrol - * `max-height` and the `::*-arrow` subcontrol `margin` is set. - */ -QColumnView QAbstractItemView::item -{ - padding: 0.23em; - max-width: 0.5em; - max-height: 0.8em; -} - -QColumnView QAbstractItemView::right-arrow -{ - image: url(:/dark/right_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::right-arrow:selected, -QColumnView QAbstractItemView::right-arrow:hover -{ - image: url(:/dark/right_arrow_hover.svg); -} - -QColumnView QAbstractItemView::left-arrow -{ - image: url(:/dark/left_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::left-arrow:selected, -QColumnView QAbstractItemView::left-arrow:hover -{ - image: url(:/dark/left_arrow_hover.svg); -} - -QComboBox:hover:pressed:editable -{ - background-color: #1d2023; -} - -QComboBox QAbstractItemView -{ - /* This happens for the drop-down menu always, whether editable or not.*/ - background-color: #1d2023; - selection-background-color: #2a79a3; - outline-color: 0em; - border-radius: 0.09em; -} - -QComboBox::drop-down -{ - subcontrol-origin: padding; - subcontrol-position: top right; - width: 0.65em; - - border-left-width: 0em; - border-left-style: solid; - border-top-right-radius: 0.13em; - border-bottom-right-radius: 0.13em; -} - -QComboBox::down-arrow -{ - border-image: url(:/dark/down_arrow_disabled.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QComboBox::down-arrow:on, -QComboBox::down-arrow:hover, -QComboBox::down-arrow:focus -{ - border-image: url(:/dark/down_arrow.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QAbstractSpinBox -{ - padding: 0.23em; - border: 0.09em solid #76797c; - background-color: #1d2023; - color: #eff0f1; - border-radius: 0.09em; - min-width: 3em; - min-height: 1em; -} - -QAbstractSpinBox:hover -{ - border: 0.09em solid #3daee9; -} - -QAbstractSpinBox:up-button, -QAbstractSpinBox:up-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center right; - padding-right: 0.1em; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox:down-button, -QAbstractSpinBox:down-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center left; - padding-left: 0.1em; - width: 0.8em; - height: 0.5em; -} - -/** - * Bug fixes for elongated items in QSpinBoxes. - * By default, the items are bounded by `down-button` - * and `up-button`, so this doesn't actually affect the styling. - * - * This does however affect some custom styling using - * QStyle.CC_ComboBox, which affects QDateEdit. This cannot - * be selected using QDateEdit, since it uses a global style. - * This sounds nonsensical, because CC_ComboBox isn't a spin box, - * but through trial and error, this is in fact the case. - * - * Affects #40. - */ -QAbstractSpinBox::up-arrow, -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off, -QAbstractSpinBox::up-arrow:!off:!disabled:hover, -QAbstractSpinBox::down-arrow, -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off, -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - border-image: none; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox::up-arrow -{ - image: url(:/dark/up_arrow.svg); -} - -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off -{ - image: url(:/dark/up_arrow_disabled.svg); -} - -QAbstractSpinBox::up-arrow:hover -{ - image: url(:/dark/up_arrow_hover.svg); -} - -QAbstractSpinBox::down-arrow -{ - image: url(:/dark/down_arrow.svg); -} - -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off -{ - image: url(:/dark/down_arrow_disabled.svg); -} - -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - image: url(:/dark/down_arrow_hover.svg); -} - -QDoubleSpinBox -{ - min-width: 4em; -} - -/** - * `QCalendarWidget QAbstractItemView:enabled` sets the color, background - * color, and selection color for active dates in the view. - * `QCalendarWidget QAbstractItemView:enabled` sets the disabled dates. - */ -QCalendarWidget QAbstractItemView:enabled -{ - color: #eff0f1; - selection-color: #eff0f1; - selection-background-color: #3daee9; -} - -/* Won't take hover events. */ -QPrevNextCalButton -{ - min-width: 0.8em; - min-height: 1.2em; - qproperty-iconSize: 0px 0px; -} - -QPrevNextCalButton#qt_calendar_nextmonth -{ - image: url(:/dark/calendar_next.svg); -} - -QPrevNextCalButton#qt_calendar_prevmonth -{ - image: url(:/dark/calendar_previous.svg); -} - -/** - * Setting for the month and year displays and drop-down menu for the - * month select. We style this separately because we want a drop-down - * indicator in the bottom right, unlike the normal QToolButton. - */ -QCalendarWidget QToolButton -{ - background-color: transparent; - border: 0.04em solid #76797c; - border-radius: 0.09em; - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; - min-height: 1.1em; -} - -QCalendarWidget QToolButton:hover -{ - border: 0.04em solid #3daee9; -} - -QCalendarWidget QToolButton:checked, -QCalendarWidget QToolButton:pressed -{ - background-color: #3daee9; - padding: 0.23em; - padding-right: 1.2em; - min-height: 1.3em; - outline: none; -} - -/** - * The QCalendarWidget for QDateTimeEdit seems to improperly - * style the year QToolButton, which has an object name - * `qt_datetimedit_calendar`, so ensure we style it as well. - */ -QCalendarWidget QToolButton::menu-indicator, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-indicator -{ - border-image: none; - image: url(:/dark/down_arrow.svg); - width: 0.8em; - height: 0.5em; - top: -0.7ex; - left: -0.09em; - padding-right: -1.11em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -QCalendarWidget QToolButton::menu-arrow, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-arrow -{ - border-image: none; - image: url(:/dark/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -/** - * Setting for the year button. Both the month select and the year - * select are QToolButtons, and both are auto-raised. The year - * button however has the popup mode set to `DelayedPopup`. - */ -QCalendarWidget QToolButton[autoRaise="true"][popupMode="0"] -{ - padding: 0.23em; -} - -QCalendarWidget QSpinBox -{ - max-height: 1.5em; - min-width: 3.5em; - margin: 0em; - margin-top: 0.2em; - padding: 0em; - outline: 0em; - padding-left: 0.5em; -} - -QLabel -{ - border: 0em solid black; -} - -/* BORDERS */ -QTabWidget::pane -{ - padding: 0.23em; - margin: 0.04em; -} - -QTabWidget::pane:top -{ - border: 0.04em solid #76797c; - top: -0.04em; -} - -QTabWidget::pane:bottom -{ - border: 0.04em solid #76797c; - bottom: -0.04em; -} - -QTabWidget::pane:left -{ - border: 0.04em solid #76797c; - left: -0.04em; -} - -QTabWidget::pane:right -{ - border: 0.04em solid #76797c; - right: -0.04em; -} - -QTabBar -{ - qproperty-drawBase: 0; - left: 0.23em; - border-radius: 0.13em; - /** - * Note: this is the underline for each tab title. It's not - * documented, and this took forever to track down. At least - * 10 hours have been wasted trying to turn off this line, - * do not deleted this comment. - */ - selection-color: transparent; -} - -QTabBar:focus -{ - border: 0em transparent black; -} - -QTabBar::close-button -{ - /* Doesn't seem possible to resize these buttons */ - border-image: url(:/dark/transparent.svg); - image: url(:/dark/close.svg); - background: transparent; -} - -QTabBar::close-button:hover -{ - image: url(:/dark/close_hover.svg); -} - -QTabBar::close-button:pressed -{ - image: url(:/dark/close_pressed.svg); -} - -/* TOP TABS */ -QTabBar::tab:top, -QTabBar::tab:top:last, -QTabBar::tab:top:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-left: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-top: 0.09em solid #3daee9; - background-color: #31363b; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-right: 0.04em solid #76797c; - border-bottom: 0.04em solid #76797c; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:next-selected -{ - border-right: 0.04em transparent #2c3034; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* BOTTOM TABS */ -QTabBar::tab:bottom, -QTabBar::tab:bottom:last, -QTabBar::tab:bottom:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-left: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-bottom: 0.09em solid #3daee9; - background-color: #31363b; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-top: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:next-selected -{ - border-right: 0.04em transparent #2c3034; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -/* LEFT TABS */ -QTabBar::tab:left, -QTabBar::tab:left:last, -QTabBar::tab:left:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-top: 0.09em solid #3daee9; - border-bottom: 0.04em solid #76797c; - border-left: 0.04em solid #76797c; - background-color: #31363b; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-top: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:previous-selected -{ - border-top: 0.04em transparent #2c3034; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* RIGHT TABS */ -QTabBar::tab:right, -QTabBar::tab:right:last, -QTabBar::tab:right:only-one -{ - color: #eff0f1; - border: 0.04em transparent black; - border-top: 0.09em solid #3daee9; - border-bottom: 0.04em solid #76797c; - border-right: 0.04em solid #76797c; - background-color: #31363b; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected -{ - color: #eff0f1; - background-color: #2c3034; - border: 0.04em transparent black; - border-top: 0.04em solid #76797c; - border-left: 0.04em solid #76797c; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:previous-selected -{ - border-top: 0.04em transparent #2c3034; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.1); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -/** - * Special styles for triangular QTabWidgets. - * These ignore the border attributes, and the border and - * text color seems to be set via the `QTabBar::tab` color - * property. This seemingly cannot be changed. - * - * The rounded shapes are 0-3, and the triangular ones are 4-7. - * - * The QTabBar outline doesn't respect on QTabBar::tab: - * border-color - * outline-color - */ -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - /* Need a dark color without alpha channel since it affects the text. */ - color: #3daee9; - background-color: #31363b; - padding: 0.23em; -} - -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one -{ - min-width: 50px; -} - -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - min-height: 50px; -} - -QTabBar[shape="4"]::tab:!selected, -QTabBar[shape="5"]::tab:!selected, -QTabBar[shape="6"]::tab:!selected, -QTabBar[shape="7"]::tab:!selected -{ - color: #eff0f1; - background-color: #2c3034; -} - -/** - * Increase padding on the opposite side of the icon to avoid text clipping. - * - * BUG: The padding works for North, West, and East in Qt5, South does not - * work. All tab positions work for triangular tabs in Qt6. - */ -QTabBar[shape="4"][tabsClosable="true"]::tab, -QTabBar[shape="5"][tabsClosable="true"]::tab -{ - padding-left: 0.5em; -} - -QTabBar[shape="6"][tabsClosable="true"]::tab -{ - padding-bottom: 0.5em; -} - -QTabBar[shape="7"][tabsClosable="true"]::tab -{ - padding-top: 0.5em; -} - -/** -* Undo the padding for the tab. -* -* Enumerated values are North, South, West, East in that order, -* from 4-7. -* -* NOTE: Any higher padding will clip the icon. -*/ -QTabBar[shape="4"]::close-button, -QTabBar[shape="5"]::close-button -{ - padding-left: -0.12em; -} - -QTabBar[shape="6"]::close-button -{ - padding-bottom: -0.18em; -} - -QTabBar[shape="7"]::close-button -{ - padding-top: -0.18em; -} - -QDockWidget -{ - background: #31363b; - /** - * It doesn't seem possible to change the border of the - * QDockWidget without changing the content margins. - */ - /** - * This is a bug fix so we can handle hover, pressed, and other events. - * Reference: https://stackoverflow.com/questions/32145080/qdockwidget-float-close-button-hover-images - */ - titlebar-close-icon: url(:/dark/transparent.svg); - titlebar-normal-icon: url(:/dark/transparent.svg); -} - -/** - * Don't style the title, since it gives a weird, missing border - * around the rest of the dock widget, which the remaining border - * cannot be removed. - * - * There is a bug in non-Breeze styles, where the icons are small. It - * doesn't change if we use `image` instead of `border-image`, nor if - * we use `qproperty-icon`, etc. The icon seem to be half the size - * of our desired values. - */ -QDockWidget::close-button, -QDockWidget::float-button -{ - border: 0.04em solid transparent; - border-radius: 0.09em; - background: transparent; - /* Maximum icon size for buttons */ - icon-size: 14px; -} - -QDockWidget::float-button -{ - border-image: url(:/dark/transparent.svg); - image: url(:/dark/undock.svg); -} - -QDockWidget::float-button:hover -{ - image: url(:/dark/undock_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::float-button:pressed -{ - image: url(:/dark/undock_hover.svg); -} - -QDockWidget::close-button -{ - border-image: url(:/dark/transparent.svg); - image: url(:/dark/close.svg); -} - -QDockWidget::close-button:hover -{ - image: url(:/dark/close_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::close-button:pressed -{ - image: url(:/dark/close_pressed.svg); -} - -QTreeView, -QListView -{ - background-color: #1d2023; - border: 0em solid black; -} - -QTreeView:selected, -QTreeView:!selected, -QListView:selected, -QListView:!selected -{ - border: 0em solid black; -} - -QTreeView::branch:has-siblings -{ - border-image: url(:/dark/vline.svg); - image: none; -} - -/* These branch indicators don't scale */ -QTreeView::branch:!has-siblings -{ - border-image: none; - image: none; -} - -QTreeView::branch:has-siblings:adjoins-item -{ - border-image: url(:/dark/branch_more.svg); -} - -QTreeView::branch:!has-children:!has-siblings:adjoins-item -{ - border-image: url(:/dark/branch_end.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:closed:has-children:has-siblings -{ - image: url(:/dark/branch_closed.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed:hover, -QTreeView::branch:closed:has-children:has-siblings:hover -{ - image: url(:/dark/branch_closed_hover.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:open:has-children:!has-siblings -{ - border-image: url(:/dark/branch_end_arrow.svg); -} - -QTreeView::branch:closed:has-children:has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - border-image: url(:/dark/branch_more_arrow.svg); -} - -QTreeView::branch:open:has-children:!has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - image: url(:/dark/branch_open.svg); -} - -QTreeView::branch:open:has-children:!has-siblings:hover, -QTreeView::branch:open:has-children:has-siblings:hover -{ - image: url(:/dark/branch_open_hover.svg); -} - -QListView -{ - /* Give space for elements aligned left or right. */ - padding: 0.2em; -} - -QTableView::item, -QListView::item, -QTreeView::item -{ - padding: 0.13em; - color: #eff0f1; -} - -QTreeView::item -{ - /** - * Need to set the background color in Qt6, or else - * the QTreeView indicators use the style defaults, - * along with the box model, which conflicts with our - * theme (except with hover/focus/selected pseudostates). - * - * Affects issue #51. - */ - background-color: #1d2023; - outline: 0; -} - -QTableView::item:!selected:hover, -QListView::item:!selected:hover, -QTreeView::item:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.1); - outline: 0; - color: #eff0f1; - padding: 0.13em; -} - -QAbstractItemView::item QLineEdit -{ - border: 0em transparent black; - /* - * The top/bottom padding causes the editable widget to conceal text. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/69 - */ - padding: 0em; -} - -QSlider::handle:horizontal, -QSlider::handle:vertical -{ - background: #1d2023; - border: 0.04em solid #626568; - width: 0.7em; - height: 0.7em; - border-radius: 0.35em; -} - -QSlider:horizontal -{ - height: 2em; -} - -QSlider:vertical -{ - width: 2em; -} - -QSlider::handle:horizontal -{ - margin: -0.23em 0; -} - -QSlider::handle:vertical -{ - margin: 0 -0.23em; -} - -QSlider::groove:horizontal, -QSlider::groove:vertical -{ - background: #2c3034; - border: 0em solid #31363b; - border-radius: 0.19em; -} - -QSlider::groove:horizontal -{ - height: 0.4em; -} - -QSlider::groove:vertical -{ - width: 0.4em; -} - -QSlider::handle:horizontal:hover, -QSlider::handle:horizontal:focus, -QSlider::handle:vertical:hover, -QSlider::handle:vertical:focus -{ - border: 0.04em solid #3daee9; -} - -QSlider::handle:horizontal:!focus:!hover, -QSlider::handle:vertical:!focus:!hover -{ - border: 0.04em solid #626568; -} - -QSlider::sub-page:horizontal, -QSlider::add-page:vertical -{ - background: #3daee9; - border-radius: 0.19em; -} - -QSlider::add-page:horizontal, -QSlider::sub-page:vertical -{ - background: #626568; - border-radius: 0.19em; -} - -/* QToolButton */ -/** - * QToolButton's that have a push button need to be styled differently, - * depending on whether there are actions (a menu) associated with it. - * This is signaled by a drop-down arrow on the right of the push button. - * Unfortunately, there's no good property to determine this. The property - * we need is `QWidget::actions`, however, it's a method and not a - * property.Note that the drop-down menu is not signaled by any of the - * following: - * popupMode: any pop-up mode does not affect the right arrow style. - * arrowType: only replaces the icon. - * toolButtonStyle: this is almost always set to icon only, even with text. - * text: can have a drop-down menu with or without text. - * - * Notably, we need to ensure we don't pad the widgets in the following - * cases: - * 1. If the QToolButton is auto-raised. - * This adds undesired padding in`QFileDialog`. These widgets - * have text, even though no text is visible. This is not the - * default, so it won't affect most situations. - * 2. If the QToolButton does not have text. - * Normally, text-less buttons do not have a menu, and this - * is required for #47, since the padding affects the scroll - * bar icons in QTabBar. This causes major issues in the - * UI, so disable the padding by default. - * - * The padding can affect the placement of icons and other things - * inside the toolbutton: near the menu-button in QFileDialog, - * the clear text icon is misplaced vertically, making it nearly - * illegible. - * - * We provide special styles for a custom, dynamic property to - * override the padding decisions with or without a menu. - * To force styling as if there is a menu, set the `hasMenu` property - * to true. Setting `hasMenu` to false will style as if there is no menu. - * You can use `QWidget::setProperty` to set this property dynamically. - * - * The affected issues are #22, #28, #47. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/22 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/28 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/47 - */ - -/** - * Use an overly specific selector here to ensure no margins, - * or for the default QToolButton. We must have `autoRaise="false"` - * and `text` to have padding, so just add a `hasMenu="false"` to - * undo the padding in that case. Also add selectors for QDialog - * if a menu is explicitly forbidden. - */ -QToolButton, -QToolButton[hasMenu="false"][autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="2"] -{ - margin: 0em; - padding: 0em; -} - -QToolButton[autoRaise="false"] -{ - background-color: #31363b; - border: 0.04em solid #76797c; - border-radius: 0.09em; -} - -QToolButton[autoRaise="true"] -{ - background-color: #31363b; - border: 0.04em solid transparent; -} - -/* Add selectors for the QDialog if a menu is explicitly requested. */ -QToolButton[hasMenu="true"], -QToolButton[autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="2"] -{ - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; -} - -QToolButton:hover -{ - border: 0.04em solid #3daee9; -} - -QToolButton:checked, -QToolButton:pressed -{ - border: 0.04em solid #3daee9; - background-color: #3daee9; -} - -QToolButton::right-arrow, -QToolButton::left-arrow, -QToolButton::up-arrow, -QToolButton::down-arrow -{ - /** - * Do not set the arrow width/height here. It causes - * small icons in Qt6, and doesn't affect the styling - * in Qt5. Both look ideal without manually specified sizes. - */ - subcontrol-origin: content; - subcontrol-position: center; - margin: 0em; - padding: 0em; -} - -QToolButton::right-arrow:enabled -{ - image: url(:/dark/right_arrow.svg); -} - -QToolButton::left-arrow:enabled -{ - image: url(:/dark/left_arrow.svg); -} - -QToolButton::up-arrow:enabled -{ - image: url(:/dark/up_arrow.svg); -} - -QToolButton::down-arrow:enabled -{ - image: url(:/dark/down_arrow.svg); -} - -QToolButton::right-arrow:disabled -{ - image: url(:/dark/right_arrow_disabled.svg); -} - -QToolButton::left-arrow:disabled -{ - image: url(:/dark/left_arrow_disabled.svg); -} - -QToolButton::up-arrow:disabled -{ - image: url(:/dark/up_arrow_disabled.svg); -} - -QToolButton::down-arrow:disabled -{ - image: url(:/dark/down_arrow_disabled.svg); -} - -QToolButton::menu-indicator -{ - border-image: none; - image: url(:/dark/down_arrow.svg); - width: 0.8em; - height: 0.5em; - left: -0.09em; - /* -1.2em + 0.09em */ - padding-right: -1.11em; - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: right; -} - -/** - * Special rule for the drop-down indicator in a QFileDialog. - * We want these to be more compact, hence the smaller padding. - */ -QDialog QToolBar QToolButton[popupMode="2"]::menu-indicator -{ - padding-right: -0.7em; -} - -QToolButton::menu-arrow -{ - border-image: none; - image: url(:/dark/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button -{ - border-top-right-radius: 0.5em; - border-bottom-right-radius: 0.5em; - /* 1ex width + 0.4ex for border + no text = 2ex allocated above */ - width: 1.3em; - padding: 0.23em; - outline: none; -} - -QToolButton::menu-button::menu-arrow -{ - left: -0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button:hover -{ - background-color: transparent; -} - -QToolButton::menu-button:pressed -{ - background-color: transparent; - padding: 0.23em; - outline: none; -} - -QTableView -{ - border: 0em solid black; - gridline-color: #31363b; - background-color: #1d2023; -} - -QTableView:!selected, -QTableView:selected -{ - border: 0em solid black; -} - -QTableView -{ - border-radius: 0em; -} - -QAbstractItemView::item -{ - color: #eff0f1; -} - -QAbstractItemView::item:pressed -{ - background: #2a79a3; - color: #eff0f1; -} - -QAbstractItemView::item:selected:!active -{ - background: rgba(61, 173, 232, 0.1); -} - -/* Use background with qlineargradient to avoid ugly border on widget. */ -QAbstractItemView::item:selected:active -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 #2a79a3, - stop: 1 #2a79a3 - ); - color: #eff0f1; -} - -QAbstractItemView::item:selected:hover -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 #2f88b7, - stop: 1 #2f88b7 - ); - color: #eff0f1; -} - -QHeaderView -{ - background-color: #31363b; - border: 0.04em transparent; - border-radius: 0em; - margin: 0em; - padding: 0em; -} - -QHeaderView::section -{ - background-color: #31363b; - border: 0.04em solid #76797c; - color: #eff0f1; - border-radius: 0em; - padding: 0em 0.23em 0em 0.23em; - text-align: center; -} - -QHeaderView::section::vertical::first, -QHeaderView::section::vertical::only-one -{ - border-top: 0.04em solid #76797c; -} - -QHeaderView::section::vertical -{ - border-top: transparent; -} - -QHeaderView::section::horizontal::first, -QHeaderView::section::horizontal::only-one -{ - border-left: 0.04em solid #76797c; -} - -QHeaderView::section::horizontal -{ - border-left: transparent; -} - -QHeaderView[showSortIndicator="true"]::section::horizontal -{ - /* Same as the width of the arrow subcontrols below. */ - padding-right: 0.8em; -} - -QHeaderView::section:checked -{ - color: #ffffff; - background-color: #334e5e; -} - -/* Note that this doesn't work for QTreeView unless the header is clickable */ -QHeaderView::section:hover, -QHeaderView::section::horizontal::first:hover, -QHeaderView::section::horizontal::only-one:hover, -QHeaderView::section::vertical::first:hover, -QHeaderView::section::vertical::only-one:hover -{ - border: 0.04em solid #3daee9; -} - -QHeaderView[showSortIndicator="true"]::down-arrow -{ - image: url(:/dark/down_arrow.svg); - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - /** - * Qt5 and Qt6 have different ideas of the padding of the - * arrow subcontrols: using `padding-left` to ensure that - * the width is undone fixes the padding of the content by - * an extra `0.8em` in Qt6, but doesn't affect Qt5. - */ - padding-right: 0.09em; - padding-left: -0.8em; -} - -QHeaderView[showSortIndicator="true"]::up-arrow -{ - image: url(:/dark/up_arrow.svg); - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - padding-left: -0.8em; -} - -QTableView QTableCornerButton::section -{ - background-color: #31363b; - border: 0.04em transparent #76797c; - border-top: 0.04em solid #76797c; - border-left: 0.04em solid #76797c; - border-radius: 0em; -} - -/* No hover event */ -QTableView QTableCornerButton:hover -{ - border: 0.04em transparent #76797c; -} - -QTableView QTableCornerButton::section:pressed -{ - border: 0.04em solid #3daee9; - border-radius: 0em; -} - -QToolBox -{ - padding: 0.23em; - border: 0.09em transparent black; -} - -QToolBox::tab -{ - border-bottom: 0.09em solid #76797c; - margin-left: 1.5em; -} - -QToolBox::tab:selected, -QToolBox::tab:hover -{ - border-bottom: 0.09em solid #3daee9; -} - -QSplitter::handle -{ - border: 0.09em solid #2c3034; - background: -0.5em solid #2c3034; - max-width: 0em; - max-height: 0em; -} - -/** - * It's not possible to get satisfactory rounded borders here. - * If you set the border to be negative, while adjusting the - * widths, you get an asymmetrical curve which produces an - * unappealing border. - */ -QProgressBar:horizontal, -QProgressBar:vertical -{ - background-color: #626568; - border: 0.9em solid #31363b; - border-radius: 0.13em; - padding: 0em; -} - -QProgressBar:horizontal -{ - height: 0.2em; - min-width: 6em; - text-align: right; - padding-left: -0.03em; - padding-right: -0.03em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-right: 1.3em; -} - -QProgressBar:vertical -{ - width: 0.2em; - min-height: 6em; - text-align: bottom; - padding-top: -0.03em; - padding-bottom: -0.03em; - margin-left: 0.2em; - margin-right: 0.2em; - margin-bottom: 0.41em; -} - -QProgressBar::chunk:horizontal, -QProgressBar::chunk:vertical -{ - background-color: #3daee9; - border: 0.9em transparent; - border-radius: 0.08em; -} - -QScrollArea, -QScrollArea:focus, -QScrollArea:hover -{ - border: 0em solid black; -} - -/* ICONS */ -/** - * Qt's built-in icons can look pretty bad if the system theme - * is a different color than the current one. For example, when - * using a dark theme, with a light UI, the `Ok` button is greyed - * out for an about dialog. - * - * QDialogButtonBox will apply for all standard buttons in all standard - * widgets, such as QMessageBox, etc. However, we do need to override - * standard icons elsewhere. - * - * The rest of the icons make little sense to implement: - * Qt uses native window decorations. - * Qt normally uses native file dialogs, which look nicer. - * Media controls are used in custom widgets, which aren't standard. - */ -QDialogButtonBox -{ - dialogbuttonbox-buttons-have-icons: true; - - dialog-cancel-icon: url(:/dark/dialog_cancel.svg); - dialog-close-icon: url(:/dark/dialog_close.svg); - dialog-ok-icon: url(:/dark/dialog_ok.svg); - dialog-open-icon: url(:/dark/dialog_open.svg); - dialog-reset-icon: url(:/dark/dialog_reset.svg); - dialog-save-icon: url(:/dark/dialog_save.svg); - /** - * No support yet for overriding saveall. - * dialog-saveall-icon: url(:/dark/dialog_saveall.svg); - */ - dialog-yes-icon: url(:/dark/dialog_ok.svg); - dialog-help-icon: url(:/dark/dialog_help.svg); - dialog-no-icon: url(:/dark/dialog_no.svg); - dialog-apply-icon: url(:/dark/dialog_ok.svg); - dialog-discard-icon: url(:/dark/dialog_discard.svg); -} - -/* Set some styles for these custom dialog buttons */ -QDialogButtonBox QPushButton, -QMessageBox QPushButton -{ - min-height: 1.1em; - min-width: 5em; -} - -/** - * Special rules for creating a custom titlebar. This can only work - * when setting the Qt property `isTitlebar` to `true`. - */ -QWidget[isTitlebar="true"], -QWidget[isTitlebar="true"] * -{ - background-color: #2c3034; -} - -/** - * Special rules for creating a border around a top-level frame of a window. - * This can only work when setting the Qt property `isWindow` to `true`. - * We've manually enumerated border widths from 1-5 below. - */ -QFrame[isWindow="true"], -QFrame[frameShape][isWindow="true"] -{ - border: 0px transparent #2c3034; -} - -QFrame[isWindow="true"][windowFrame="1"], -QFrame[frameShape][isWindow="true"][windowFrame="1"] -{ - border: 1px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="2"], -QFrame[frameShape][isWindow="true"][windowFrame="2"] -{ - border: 2px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="3"], -QFrame[frameShape][isWindow="true"][windowFrame="3"] -{ - border: 3px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="4"], -QFrame[frameShape][isWindow="true"][windowFrame="4"] -{ - border: 4px solid #2c3034; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="5"], -QFrame[frameShape][isWindow="true"][windowFrame="5"] -{ - border: 5px solid #2c3034; - border-radius: 3px; -} diff --git a/lib/theme/qrc/dark/transparent.svg b/lib/theme/qrc/dark/transparent.svg deleted file mode 100644 index 3a8ca5cf..00000000 --- a/lib/theme/qrc/dark/transparent.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/theme/qrc/dark/trash.svg b/lib/theme/qrc/dark/trash.svg deleted file mode 100644 index d20d3d8e..00000000 --- a/lib/theme/qrc/dark/trash.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/undock.svg b/lib/theme/qrc/dark/undock.svg deleted file mode 100644 index 886196c8..00000000 --- a/lib/theme/qrc/dark/undock.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/undock_hover.svg b/lib/theme/qrc/dark/undock_hover.svg deleted file mode 100644 index 5f1c72c6..00000000 --- a/lib/theme/qrc/dark/undock_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/undock_hover_pressed.svg b/lib/theme/qrc/dark/undock_hover_pressed.svg deleted file mode 100644 index 1fa8d5c6..00000000 --- a/lib/theme/qrc/dark/undock_hover_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/unshade.svg b/lib/theme/qrc/dark/unshade.svg deleted file mode 100644 index bc0c9a87..00000000 --- a/lib/theme/qrc/dark/unshade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/up_arrow.svg b/lib/theme/qrc/dark/up_arrow.svg deleted file mode 100644 index b6ec6185..00000000 --- a/lib/theme/qrc/dark/up_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/up_arrow_disabled.svg b/lib/theme/qrc/dark/up_arrow_disabled.svg deleted file mode 100644 index de13ac29..00000000 --- a/lib/theme/qrc/dark/up_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/up_arrow_hover.svg b/lib/theme/qrc/dark/up_arrow_hover.svg deleted file mode 100644 index 6f33e689..00000000 --- a/lib/theme/qrc/dark/up_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/vline.svg b/lib/theme/qrc/dark/vline.svg deleted file mode 100644 index 2ecc5cee..00000000 --- a/lib/theme/qrc/dark/vline.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/dark/vmovetoolbar.svg b/lib/theme/qrc/dark/vmovetoolbar.svg deleted file mode 100644 index e626a335..00000000 --- a/lib/theme/qrc/dark/vmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/dark/vseptoolbar.svg b/lib/theme/qrc/dark/vseptoolbar.svg deleted file mode 100644 index 3ab1a7b2..00000000 --- a/lib/theme/qrc/dark/vseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/dark/window_close.svg b/lib/theme/qrc/dark/window_close.svg deleted file mode 100644 index 3649be5d..00000000 --- a/lib/theme/qrc/dark/window_close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/branch_closed.svg b/lib/theme/qrc/light-purple/branch_closed.svg deleted file mode 100644 index 5365e9e4..00000000 --- a/lib/theme/qrc/light-purple/branch_closed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/branch_closed_hover.svg b/lib/theme/qrc/light-purple/branch_closed_hover.svg deleted file mode 100644 index 478ec35a..00000000 --- a/lib/theme/qrc/light-purple/branch_closed_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/branch_end.svg b/lib/theme/qrc/light-purple/branch_end.svg deleted file mode 100644 index 70ea5fde..00000000 --- a/lib/theme/qrc/light-purple/branch_end.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/branch_end_arrow.svg b/lib/theme/qrc/light-purple/branch_end_arrow.svg deleted file mode 100644 index 5f25d556..00000000 --- a/lib/theme/qrc/light-purple/branch_end_arrow.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/branch_more.svg b/lib/theme/qrc/light-purple/branch_more.svg deleted file mode 100644 index 543a3538..00000000 --- a/lib/theme/qrc/light-purple/branch_more.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/branch_more_arrow.svg b/lib/theme/qrc/light-purple/branch_more_arrow.svg deleted file mode 100644 index 66814477..00000000 --- a/lib/theme/qrc/light-purple/branch_more_arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light-purple/branch_open.svg b/lib/theme/qrc/light-purple/branch_open.svg deleted file mode 100644 index 5ef0d331..00000000 --- a/lib/theme/qrc/light-purple/branch_open.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/branch_open_hover.svg b/lib/theme/qrc/light-purple/branch_open_hover.svg deleted file mode 100644 index 7e2e2fea..00000000 --- a/lib/theme/qrc/light-purple/branch_open_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/calendar_next.svg b/lib/theme/qrc/light-purple/calendar_next.svg deleted file mode 100644 index 5c2e0d51..00000000 --- a/lib/theme/qrc/light-purple/calendar_next.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/calendar_previous.svg b/lib/theme/qrc/light-purple/calendar_previous.svg deleted file mode 100644 index 04ebc9c2..00000000 --- a/lib/theme/qrc/light-purple/calendar_previous.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/checkbox_checked.svg b/lib/theme/qrc/light-purple/checkbox_checked.svg deleted file mode 100644 index 506f0205..00000000 --- a/lib/theme/qrc/light-purple/checkbox_checked.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light-purple/checkbox_checked_disabled.svg b/lib/theme/qrc/light-purple/checkbox_checked_disabled.svg deleted file mode 100644 index fd8fa467..00000000 --- a/lib/theme/qrc/light-purple/checkbox_checked_disabled.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light-purple/checkbox_indeterminate.svg b/lib/theme/qrc/light-purple/checkbox_indeterminate.svg deleted file mode 100644 index 580d5229..00000000 --- a/lib/theme/qrc/light-purple/checkbox_indeterminate.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/checkbox_indeterminate_disabled.svg b/lib/theme/qrc/light-purple/checkbox_indeterminate_disabled.svg deleted file mode 100644 index a8b22cdc..00000000 --- a/lib/theme/qrc/light-purple/checkbox_indeterminate_disabled.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/checkbox_unchecked.svg b/lib/theme/qrc/light-purple/checkbox_unchecked.svg deleted file mode 100644 index 2e46929b..00000000 --- a/lib/theme/qrc/light-purple/checkbox_unchecked.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/checkbox_unchecked_disabled.svg b/lib/theme/qrc/light-purple/checkbox_unchecked_disabled.svg deleted file mode 100644 index af0f0b31..00000000 --- a/lib/theme/qrc/light-purple/checkbox_unchecked_disabled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/clear_text.svg b/lib/theme/qrc/light-purple/clear_text.svg deleted file mode 100644 index c3605d81..00000000 --- a/lib/theme/qrc/light-purple/clear_text.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/close.svg b/lib/theme/qrc/light-purple/close.svg deleted file mode 100644 index 13caf6f2..00000000 --- a/lib/theme/qrc/light-purple/close.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/close_hover.svg b/lib/theme/qrc/light-purple/close_hover.svg deleted file mode 100644 index 8e2d0ab1..00000000 --- a/lib/theme/qrc/light-purple/close_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/close_pressed.svg b/lib/theme/qrc/light-purple/close_pressed.svg deleted file mode 100644 index 9bd9228e..00000000 --- a/lib/theme/qrc/light-purple/close_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/computer.svg b/lib/theme/qrc/light-purple/computer.svg deleted file mode 100644 index 7b15b411..00000000 --- a/lib/theme/qrc/light-purple/computer.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/desktop.svg b/lib/theme/qrc/light-purple/desktop.svg deleted file mode 100644 index 919feedd..00000000 --- a/lib/theme/qrc/light-purple/desktop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light-purple/dialog_cancel.svg b/lib/theme/qrc/light-purple/dialog_cancel.svg deleted file mode 100644 index 4bd6495a..00000000 --- a/lib/theme/qrc/light-purple/dialog_cancel.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_close.svg b/lib/theme/qrc/light-purple/dialog_close.svg deleted file mode 100644 index 6f1aa215..00000000 --- a/lib/theme/qrc/light-purple/dialog_close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_discard.svg b/lib/theme/qrc/light-purple/dialog_discard.svg deleted file mode 100644 index 2d6d0967..00000000 --- a/lib/theme/qrc/light-purple/dialog_discard.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_help.svg b/lib/theme/qrc/light-purple/dialog_help.svg deleted file mode 100644 index 86407302..00000000 --- a/lib/theme/qrc/light-purple/dialog_help.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_no.svg b/lib/theme/qrc/light-purple/dialog_no.svg deleted file mode 100644 index 6c4ed779..00000000 --- a/lib/theme/qrc/light-purple/dialog_no.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_ok.svg b/lib/theme/qrc/light-purple/dialog_ok.svg deleted file mode 100644 index 7756ff18..00000000 --- a/lib/theme/qrc/light-purple/dialog_ok.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_open.svg b/lib/theme/qrc/light-purple/dialog_open.svg deleted file mode 100644 index 65f282db..00000000 --- a/lib/theme/qrc/light-purple/dialog_open.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_reset.svg b/lib/theme/qrc/light-purple/dialog_reset.svg deleted file mode 100644 index 4af3b6b0..00000000 --- a/lib/theme/qrc/light-purple/dialog_reset.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/dialog_save.svg b/lib/theme/qrc/light-purple/dialog_save.svg deleted file mode 100644 index 346dc526..00000000 --- a/lib/theme/qrc/light-purple/dialog_save.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/disc_drive.svg b/lib/theme/qrc/light-purple/disc_drive.svg deleted file mode 100644 index 045b7ca9..00000000 --- a/lib/theme/qrc/light-purple/disc_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/down_arrow.svg b/lib/theme/qrc/light-purple/down_arrow.svg deleted file mode 100644 index 213c94ab..00000000 --- a/lib/theme/qrc/light-purple/down_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/down_arrow_disabled.svg b/lib/theme/qrc/light-purple/down_arrow_disabled.svg deleted file mode 100644 index a3ae7e5a..00000000 --- a/lib/theme/qrc/light-purple/down_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/down_arrow_hover.svg b/lib/theme/qrc/light-purple/down_arrow_hover.svg deleted file mode 100644 index 6a76b7e3..00000000 --- a/lib/theme/qrc/light-purple/down_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/file.svg b/lib/theme/qrc/light-purple/file.svg deleted file mode 100644 index 59bdd092..00000000 --- a/lib/theme/qrc/light-purple/file.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/file_dialog_contents.svg b/lib/theme/qrc/light-purple/file_dialog_contents.svg deleted file mode 100644 index 9872db38..00000000 --- a/lib/theme/qrc/light-purple/file_dialog_contents.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light-purple/file_dialog_detailed.svg b/lib/theme/qrc/light-purple/file_dialog_detailed.svg deleted file mode 100644 index 03b519db..00000000 --- a/lib/theme/qrc/light-purple/file_dialog_detailed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/light-purple/file_dialog_end.svg b/lib/theme/qrc/light-purple/file_dialog_end.svg deleted file mode 100644 index 9ae29b94..00000000 --- a/lib/theme/qrc/light-purple/file_dialog_end.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/file_dialog_info.svg b/lib/theme/qrc/light-purple/file_dialog_info.svg deleted file mode 100644 index beef8f7f..00000000 --- a/lib/theme/qrc/light-purple/file_dialog_info.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/lib/theme/qrc/light-purple/file_dialog_list.svg b/lib/theme/qrc/light-purple/file_dialog_list.svg deleted file mode 100644 index 40be548e..00000000 --- a/lib/theme/qrc/light-purple/file_dialog_list.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/file_dialog_start.svg b/lib/theme/qrc/light-purple/file_dialog_start.svg deleted file mode 100644 index 7ba87ddc..00000000 --- a/lib/theme/qrc/light-purple/file_dialog_start.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/file_link.svg b/lib/theme/qrc/light-purple/file_link.svg deleted file mode 100644 index dcb4cbe2..00000000 --- a/lib/theme/qrc/light-purple/file_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/floppy_drive.svg b/lib/theme/qrc/light-purple/floppy_drive.svg deleted file mode 100644 index 7f2cf84e..00000000 --- a/lib/theme/qrc/light-purple/floppy_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/folder.svg b/lib/theme/qrc/light-purple/folder.svg deleted file mode 100644 index 184d3f24..00000000 --- a/lib/theme/qrc/light-purple/folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/folder_link.svg b/lib/theme/qrc/light-purple/folder_link.svg deleted file mode 100644 index 4c3bd11f..00000000 --- a/lib/theme/qrc/light-purple/folder_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/folder_open.svg b/lib/theme/qrc/light-purple/folder_open.svg deleted file mode 100644 index 95ebe9c1..00000000 --- a/lib/theme/qrc/light-purple/folder_open.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/hard_drive.svg b/lib/theme/qrc/light-purple/hard_drive.svg deleted file mode 100644 index a57f31b2..00000000 --- a/lib/theme/qrc/light-purple/hard_drive.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/help.svg b/lib/theme/qrc/light-purple/help.svg deleted file mode 100644 index 2ce5157e..00000000 --- a/lib/theme/qrc/light-purple/help.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/hmovetoolbar.svg b/lib/theme/qrc/light-purple/hmovetoolbar.svg deleted file mode 100644 index 87e684f9..00000000 --- a/lib/theme/qrc/light-purple/hmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light-purple/home_directory.svg b/lib/theme/qrc/light-purple/home_directory.svg deleted file mode 100644 index 3ed08ab4..00000000 --- a/lib/theme/qrc/light-purple/home_directory.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/hseptoolbar.svg b/lib/theme/qrc/light-purple/hseptoolbar.svg deleted file mode 100644 index c4952263..00000000 --- a/lib/theme/qrc/light-purple/hseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/left_arrow.svg b/lib/theme/qrc/light-purple/left_arrow.svg deleted file mode 100644 index 80cf674c..00000000 --- a/lib/theme/qrc/light-purple/left_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/left_arrow_disabled.svg b/lib/theme/qrc/light-purple/left_arrow_disabled.svg deleted file mode 100644 index 0568aaff..00000000 --- a/lib/theme/qrc/light-purple/left_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/left_arrow_hover.svg b/lib/theme/qrc/light-purple/left_arrow_hover.svg deleted file mode 100644 index a3527b84..00000000 --- a/lib/theme/qrc/light-purple/left_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/maximize.svg b/lib/theme/qrc/light-purple/maximize.svg deleted file mode 100644 index 1a011966..00000000 --- a/lib/theme/qrc/light-purple/maximize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/menu.svg b/lib/theme/qrc/light-purple/menu.svg deleted file mode 100644 index d7efaa64..00000000 --- a/lib/theme/qrc/light-purple/menu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/message_critical.svg b/lib/theme/qrc/light-purple/message_critical.svg deleted file mode 100644 index 5ab22603..00000000 --- a/lib/theme/qrc/light-purple/message_critical.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/message_information.svg b/lib/theme/qrc/light-purple/message_information.svg deleted file mode 100644 index 8c7639bc..00000000 --- a/lib/theme/qrc/light-purple/message_information.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/light-purple/message_question.svg b/lib/theme/qrc/light-purple/message_question.svg deleted file mode 100644 index 8e12e7bd..00000000 --- a/lib/theme/qrc/light-purple/message_question.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/light-purple/message_warning.svg b/lib/theme/qrc/light-purple/message_warning.svg deleted file mode 100644 index 166c1593..00000000 --- a/lib/theme/qrc/light-purple/message_warning.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/lib/theme/qrc/light-purple/minimize.svg b/lib/theme/qrc/light-purple/minimize.svg deleted file mode 100644 index b7f6cad8..00000000 --- a/lib/theme/qrc/light-purple/minimize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/network_drive.svg b/lib/theme/qrc/light-purple/network_drive.svg deleted file mode 100644 index 578a99f2..00000000 --- a/lib/theme/qrc/light-purple/network_drive.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light-purple/radio_checked.svg b/lib/theme/qrc/light-purple/radio_checked.svg deleted file mode 100644 index 58dad548..00000000 --- a/lib/theme/qrc/light-purple/radio_checked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light-purple/radio_checked_disabled.svg b/lib/theme/qrc/light-purple/radio_checked_disabled.svg deleted file mode 100644 index 62a922c7..00000000 --- a/lib/theme/qrc/light-purple/radio_checked_disabled.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light-purple/radio_unchecked.svg b/lib/theme/qrc/light-purple/radio_unchecked.svg deleted file mode 100644 index 2d6ba2e0..00000000 --- a/lib/theme/qrc/light-purple/radio_unchecked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/radio_unchecked_disabled.svg b/lib/theme/qrc/light-purple/radio_unchecked_disabled.svg deleted file mode 100644 index abd1b6e5..00000000 --- a/lib/theme/qrc/light-purple/radio_unchecked_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/restore.svg b/lib/theme/qrc/light-purple/restore.svg deleted file mode 100644 index 46684bf2..00000000 --- a/lib/theme/qrc/light-purple/restore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/right_arrow.svg b/lib/theme/qrc/light-purple/right_arrow.svg deleted file mode 100644 index 7a750fdd..00000000 --- a/lib/theme/qrc/light-purple/right_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/right_arrow_disabled.svg b/lib/theme/qrc/light-purple/right_arrow_disabled.svg deleted file mode 100644 index d2d29aeb..00000000 --- a/lib/theme/qrc/light-purple/right_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/right_arrow_hover.svg b/lib/theme/qrc/light-purple/right_arrow_hover.svg deleted file mode 100644 index 572b3366..00000000 --- a/lib/theme/qrc/light-purple/right_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/shade.svg b/lib/theme/qrc/light-purple/shade.svg deleted file mode 100644 index 4731b181..00000000 --- a/lib/theme/qrc/light-purple/shade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/sizegrip.svg b/lib/theme/qrc/light-purple/sizegrip.svg deleted file mode 100644 index a0cd5356..00000000 --- a/lib/theme/qrc/light-purple/sizegrip.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/stylesheet.qss b/lib/theme/qrc/light-purple/stylesheet.qss deleted file mode 100644 index 2f032e17..00000000 --- a/lib/theme/qrc/light-purple/stylesheet.qss +++ /dev/null @@ -1,2510 +0,0 @@ -/* - * BreezeDark stylesheet. - * - * :author: Colin Duquesnoy - * :editor: Alex Huszagh - * :license: MIT, see LICENSE.md - * - * This is originally a fork of QDarkStyleSheet, and is based on Breeze/ - * BreezeDark color scheme, but is in no way affiliated with KDE. - * - * --------------------------------------------------------------------- - * The MIT License (MIT) - * - * Copyright (c) <2013-2014> - * Copyright (c) <2015-2021> - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * --------------------------------------------------------------------- - */ - -/** - * MAIN STYLESHEET - * --------------- - */ - -QToolTip -{ - /* 0.2ex is the smallest value that's not ignored on Windows. */ - border: 0.04em solid #31363b; - background-image: none; - background-color: #eff0f1; - alternate-background-color: #eaebec; - color: #31363b; - padding: 0.1em; - opacity: 200; -} - -QWidget -{ - color: #31363b; - background-color: #eff0f1; - selection-background-color: rgba(164, 51, 223, 0.5); - selection-color: #31363b; - background-clip: border; - border-image: none; - - /* QDialogButtonBox icons */ - dialog-cancel-icon: url(:/light-purple/dialog_cancel.svg); - dialog-close-icon: url(:/light-purple/dialog_close.svg); - dialog-ok-icon: url(:/light-purple/dialog_ok.svg); - dialog-open-icon: url(:/light-purple/dialog_open.svg); - dialog-reset-icon: url(:/light-purple/dialog_reset.svg); - dialog-save-icon: url(:/light-purple/dialog_save.svg); - dialog-yes-icon: url(:/light-purple/dialog_ok.svg); - dialog-help-icon: url(:/light-purple/dialog_help.svg); - dialog-no-icon: url(:/light-purple/dialog_no.svg); - dialog-apply-icon: url(:/light-purple/dialog_ok.svg); - dialog-discard-icon: url(:/light-purple/dialog_discard.svg); - - /* File icons */ - filedialog-backward-icon: url(:/light-purple/left_arrow.svg); - filedialog-contentsview-icon: url(:/light-purple/file_dialog_contents.svg); - filedialog-detailedview-icon: url(:/light-purple/file_dialog_detailed.svg); - filedialog-end-icon: url(:/light-purple/file_dialog_end.svg); - filedialog-infoview-icon: url(:/light-purple/file_dialog_info.svg); - filedialog-listview-icon: url(:/light-purple/file_dialog_list.svg); - filedialog-new-directory-icon: url(:/light-purple/folder.svg); - filedialog-parent-directory-icon: url(:/light-purple/up_arrow.svg); - filedialog-start-icon: url(:/light-purple/file_dialog_start.svg); - directory-closed-icon: url(:/light-purple/folder.svg); - directory-icon: url(:/light-purple/folder.svg); - directory-link-icon: url(:/light-purple/folder_link.svg); - directory-open-icon: url(:/light-purple/folder_open.svg); - file-icon: url(:/light-purple/file.svg); - file-link-icon: url(:/light-purple/file_link.svg); - home-icon: url(:/light-purple/home_directory.svg); - - /* QMessageBox icons */ - messagebox-critical-icon: url(:/light-purple/message_critical.svg); - messagebox-information-icon: url(:/light-purple/message_information.svg); - messagebox-question-icon: url(:/light-purple/message_question.svg); - messagebox-warning-icon: url(:/light-purple/message_warning.svg); - - /* Computer icons */ - computer-icon: url(:/light-purple/computer.svg); - desktop-icon: url(:/light-purple/desktop.svg); - cd-icon: url(:/light-purple/disc_drive.svg); - dvd-icon: url(:/light-purple/disc_drive.svg); - floppy-icon: url(:/light-purple/floppy_drive.svg); - harddisk-icon: url(:/light-purple/hard_drive.svg); - network-icon: url(:/light-purple/network_drive.svg); - trash-icon: url(:/light-purple/trash.svg); - - /* Arrow icons */ - uparrow-icon: url(:/light-purple/up_arrow.svg); - downarrow-icon: url(:/light-purple/down_arrow.svg); - leftarrow-icon: url(:/light-purple/left_arrow.svg); - rightarrow-icon: url(:/light-purple/right_arrow.svg); - backward-icon: url(:/light-purple/left_arrow.svg); - forward-icon: url(:/light-purple/right_arrow.svg); - - /* Titlebar icons */ - titlebar-close-icon: url(:/light-purple/window_close.svg); - titlebar-contexthelp-icon: url(:/light-purple/help.svg); - titlebar-maximize-icon: url(:/light-purple/maximize.svg); - titlebar-menu-icon: url(:/light-purple/menu.svg); - titlebar-minimize-icon: url(:/light-purple/minimize.svg); - titlebar-normal-icon: url(:/light-purple/restore.svg); - titlebar-shade-icon: url(:/light-purple/shade.svg); - titlebar-unshade-icon: url(:/light-purple/unshade.svg); - - /* Other icons */ - dockwidget-close-icon: url(:/light-purple/close.svg); - /** - * Only available in Qt6, and causes other issues. See #62. - * lineedit-clear-button-icon: url(:/light-purple/clear_text.svg); - */ -} - -QWidget:disabled -{ - color: #b4b4b4; - background-color: #eff0f1; -} - -QCheckBox -{ - spacing: 0.23em; - color: #31363b; - margin-bottom: 0.09em; - opacity: 200; -} - -QCheckBox:disabled -{ - color: #bab9b8; -} - -QGroupBox -{ - /* Need to make sure the groupbox doesn't compress below the title. */ - min-height: 1.2em; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - /** - * This gives us enough space at the top to ensure we can move the - * title to be inside the guidelines, and the padding at the top - * ensures we have space below the title. - */ - margin-top: 0.5em; - padding-top: 1em; -} - -QGroupBox:focus -{ - border: 0.04em solid #bab9b8; - border-radius: 0.09em; -} - -QGroupBox::title -{ - /* We need to move 0.6em up to be inside the lines, +1em for padding. */ - top: -1.6em; - subcontrol-origin: content; - subcontrol-position: top center; - background: #eff0f1; - padding-left: 0.2em; - padding-right: 0.2em; -} - -QGroupBox:flat -{ - border-top: 0.04em solid rgba(106, 105, 105, 0.7); - border-left: 0.04em transparent #bab9b8; - border-right: 0.04em transparent #bab9b8; - border-bottom: 0.04em transparent #bab9b8; -} - -QCheckBox::indicator, -QTreeView::indicator, -QGroupBox::indicator -{ - width: 1em; - height: 1em; -} - -QGroupBox::indicator:unchecked, -QGroupBox::indicator:unchecked:focus, -QCheckBox::indicator:unchecked, -QCheckBox::indicator:unchecked:focus, -QTreeView::indicator:unchecked, -QTreeView::indicator:unchecked:focus -{ - border-image: url(:/light-purple/checkbox_unchecked_disabled.svg); -} - -QGroupBox::indicator:unchecked, -QCheckBox::indicator:unchecked:hover, -QCheckBox::indicator:unchecked:pressed, -QTreeView::indicator:unchecked:hover, -QTreeView::indicator:unchecked:pressed, -QGroupBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:pressed -{ - border: none; - border-image: url(:/light-purple/checkbox_unchecked.svg); -} - -QCheckBox::indicator:checked, -QTreeView::indicator:checked, -QGroupBox::indicator:checked -{ - border-image: url(:/light-purple/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QCheckBox::indicator:checked:focus, -QCheckBox::indicator:checked:pressed, -QTreeView::indicator:checked:hover, -QTreeView::indicator:checked:focus, -QTreeView::indicator:checked:pressed, -QGroupBox::indicator:checked:hover, -QGroupBox::indicator:checked:focus, -QGroupBox::indicator:checked:pressed -{ - border: none; - border-image: url(:/light-purple/checkbox_checked.svg); -} - -QCheckBox::indicator:indeterminate, -QTreeView::indicator:indeterminate -{ - border-image: url(:/light-purple/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:focus, -QCheckBox::indicator:indeterminate:hover, -QCheckBox::indicator:indeterminate:pressed, -QTreeView::indicator:indeterminate:focus, -QTreeView::indicator:indeterminate:hover, -QTreeView::indicator:indeterminate:pressed -{ - border-image: url(:/light-purple/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:disabled, -QTreeView::indicator:indeterminate:disabled -{ - border-image: url(:/light-purple/checkbox_indeterminate_disabled.svg); -} - -QCheckBox::indicator:checked:disabled, -QTreeView::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled -{ - border-image: url(:/light-purple/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QTreeView::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled -{ - border-image: url(:/light-purple/checkbox_unchecked_disabled.svg); -} - -QRadioButton -{ - spacing: 0.23em; - outline: none; - color: #31363b; - margin-bottom: 0.09em; -} - -QRadioButton:disabled -{ - color: #bab9b8; -} - -QRadioButton::indicator -{ - width: 1em; - height: 1em; -} - -QRadioButton::indicator:unchecked, -QRadioButton::indicator:unchecked:focus -{ - border-image: url(:/light-purple/radio_unchecked_disabled.svg); -} - -QRadioButton::indicator:unchecked:hover, -QRadioButton::indicator:unchecked:pressed -{ - border: none; - outline: none; - border-image: url(:/light-purple/radio_unchecked.svg); -} - -QRadioButton::indicator:checked -{ - border: none; - outline: none; - border-image: url(:/light-purple/radio_checked.svg); -} - -QRadioButton::indicator:checked:hover, -QRadioButton::indicator:checked:focus, -QRadioButton::indicator:checked:pressed -{ - border: none; - outline: none; - border-image: url(:/light-purple/radio_checked.svg); -} - -QRadioButton::indicator:checked:disabled -{ - outline: none; - border-image: url(:/light-purple/radio_checked_disabled.svg); -} - -QRadioButton::indicator:unchecked:disabled -{ - border-image: url(:/light-purple/radio_unchecked_disabled.svg); -} - -QMenuBar -{ - background-color: #eff0f1; - color: #31363b; -} - -QMenuBar::item -{ - background: transparent; -} - -QMenuBar::item:selected -{ - background: transparent; - border: 0.04em solid rgba(164, 51, 223, 0.5); -} - -QMenuBar::item:disabled -{ - color: #bab9b8; -} - -QMenuBar::item:pressed -{ - background-color: rgba(164, 51, 223, 0.5); - color: #31363b; - margin-bottom: -0.09em; - padding-bottom: 0.09em; -} - -QMenu -{ - color: #31363b; - margin: 0.09em; -} - -QMenu::icon -{ - margin: 0.23em; -} - -QMenu::item -{ - /* Add extra padding on the right for the QMenu arrow */ - padding: 0.23em 1.5em 0.23em 1.3em; - border: 0.09em solid transparent; - background: transparent; -} - -QMenu::item:selected -{ - color: #31363b; - background-color: rgba(164, 51, 223, 0.5); -} - -QMenu::item:selected:disabled -{ - background-color: #eff0f1; -} - -QMenu::item:disabled -{ - color: #bab9b8; -} - -QMenu::indicator -{ - width: 0.8em; - height: 0.8em; - /* To align with QMenu::icon, which has a 0.23em margin. */ - margin-left: 0.3em; - subcontrol-position: center left; -} - -QMenu::indicator:non-exclusive:unchecked -{ - border-image: url(:/light-purple/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:unchecked:selected -{ - border-image: url(:/light-purple/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:checked -{ - border-image: url(:/light-purple/checkbox_checked.svg); -} - -QMenu::indicator:non-exclusive:checked:selected -{ - border-image: url(:/light-purple/checkbox_checked.svg); -} - -QMenu::indicator:exclusive:unchecked -{ - border-image: url(:/light-purple/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:unchecked:selected -{ - border-image: url(:/light-purple/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:checked -{ - border-image: url(:/light-purple/radio_checked.svg); -} - -QMenu::indicator:exclusive:checked:selected -{ - border-image: url(:/light-purple/radio_checked.svg); -} - -QMenu::right-arrow -{ - margin: 0.23em; - border-image: url(:/light-purple/right_arrow.svg); - width: 0.5em; - height: 0.8em; -} - -QMenu::right-arrow:disabled -{ - border-image: url(:/light-purple/right_arrow_disabled.svg); -} - -QAbstractItemView -{ - alternate-background-color: #eff0f1; - color: #31363b; - border: 0.09em solid #bab9b8; - border-radius: 0.09em; -} - -QTabWidget:focus, -QCheckBox:focus, -QRadioButton:focus, -QSlider:focus -{ - border: none; -} - -QLineEdit -{ - background-color: #eff0f1; - padding: 0.23em; - border-style: solid; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - color: #31363b; -} - -QAbstractScrollArea -{ - border-radius: 0.09em; - border: 0.09em solid #bab9b8; - background-color: transparent; -} - -/** - * This is the background for the box in the bottom-right corner - * whene both scrollbars are active. - */ -QAbstractScrollArea::corner -{ - background: #eff0f1; -} - -/** - * Can't do the KDE style of where the scrollbar handle - * becomes light on the hover, and only when the handle - * is hovered does it become stylized. This is because - * both the handle and the background events are treated - * together. - */ -QScrollBar:horizontal -{ - background-color: #eff0f1; - height: 0.65em; - margin: 0.13em 0.65em 0.13em 0.65em; - border: 0.04em transparent #eff0f1; - border-radius: 0.17em; -} - -QScrollBar:horizontal:hover -{ - background-color: #c7c7c6; -} - -QScrollBar::handle:horizontal -{ - background-color: rgba(194, 51, 223, 0.8); - border: 0.04em solid rgba(194, 51, 223, 0.8); - min-width: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:horizontal:hover -{ - background-color: rgba(194, 51, 223, 0.8); - border: 0.04em solid rgba(194, 51, 223, 0.8); -} - -QScrollBar::add-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/light-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/light-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover, -QScrollBar::add-line:horizontal:on -{ - border-image: url(:/light-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal:hover, -QScrollBar::sub-line:horizontal:on -{ - border-image: url(:/light-purple/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, -QScrollBar::down-arrow:horizontal -{ - background: none; -} - -QScrollBar::add-page:horizontal, -QScrollBar::sub-page:horizontal -{ - background: none; -} - -QScrollBar:vertical -{ - background-color: #eff0f1; - width: 0.65em; - margin: 0.65em 0.13em 0.65em 0.13em; - border: 0.04em transparent #eff0f1; - border-radius: 0.17em; -} - -QScrollBar:vertical:hover -{ - background-color: #c7c7c6; -} - -QScrollBar::handle:vertical -{ - background-color: rgba(194, 51, 223, 0.8); - border: 0.04em solid rgba(194, 51, 223, 0.8); - min-height: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:vertical:hover -{ - background-color: rgba(194, 51, 223, 0.8); - border: 0.04em solid rgba(194, 51, 223, 0.8); -} - -QScrollBar::sub-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/light-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/light-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover, -QScrollBar::sub-line:vertical:on -{ - border-image: url(:/light-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical:hover, -QScrollBar::add-line:vertical:on -{ - border-image: url(:/light-purple/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:vertical, -QScrollBar::down-arrow:vertical -{ - background: none; -} - -QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical -{ - background: none; -} - -QTextEdit -{ - background-color: #eff0f1; - color: #31363b; - border: 0.04em solid #bab9b8; -} - -QPlainTextEdit -{ - background-color: #eff0f1; - color: #31363b; - border-radius: 0.09em; - border: 0.04em solid #bab9b8; -} - -QSizeGrip -{ - border-image: url(:/light-purple/sizegrip.svg); - width: 0.5em; - height: 0.5em; -} - -/** - * Set the separator to be transparent, since the dock has a border. - * On PyQt6, neither the border nor the background seem to be respected. - */ -QMainWindow::separator -{ - border: 0.09em transparent #bab9b8; - background: transparent; -} - -QMenu::separator -{ - height: 0.09em; - background-color: #bab9b8; - padding-left: 0.2em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-left: 0.41em; - margin-right: 0.41em; -} - -QFrame[frameShape="2"], /* QFrame::Panel == 0x0003 */ -QFrame[frameShape="3"], /* QFrame::WinPanel == 0x0003 */ -QFrame[frameShape="4"], /* QFrame::HLine == 0x0004 */ -QFrame[frameShape="5"], /* QFrame::VLine == 0x0005 */ -QFrame[frameShape="6"] /* QFrame::StyledPanel == 0x0006 */ -{ - border-width: 0.04em; - padding: 0.09em; - border-style: solid; - border-color: #eff0f1; - background-color: #bab9b8; - border-radius: 0.23em; -} - -/* Provide highlighting for frame objects. */ -QFrame[frameShape="2"]:hover, -QFrame[frameShape="3"]:hover, -QFrame[frameShape="4"]:hover, -QFrame[frameShape="5"]:hover, -QFrame[frameShape="6"]:hover -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); -} - -/* Don't provide an outline if we have a widget that takes up the space. */ -QFrame[frameShape] QAbstractItemView:hover -{ - border: 0em solid black; -} - -/** - * Note: I can't really change the background of the toolbars - * independently, since KDE Breeze has different colors for the - * window bar and the rest of the UI. The top toolbar uses - * the window style, and the rest use the application style, - * which we can't do. - */ -QToolBar -{ - font-weight: bold; -} - -QToolBar:horizontal -{ - background: 0.09em solid #eff0f1; -} - -QToolBar:vertical -{ - background: 0.09em solid #eff0f1; -} - -QToolBar::handle:horizontal -{ - border-image: url(:/light-purple/hmovetoolbar.svg); -} - -QToolBar::handle:vertical -{ - border-image: url(:/light-purple/vmovetoolbar.svg); -} - -QToolBar::separator:horizontal -{ - border-image: url(:/light-purple/hseptoolbar.svg); -} - -QToolBar::separator:vertical -{ - border-image: url(:/light-purple/vseptoolbar.svg); -} - -QToolBar QToolButton -{ - font-weight: bold; - border: 0.04em transparent black; - padding-left: 0.2em; - padding-right: 0.3em; -} - -QToolBar QToolButton:hover -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); -} - -QToolBar QToolButton:pressed -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); - /* The padding doesn't inherit from `QToolBar QToolButton`, so leave it in. */ - padding-left: 0.2em; - padding-right: 0.3em; -} - -/** - * Special rules for a QFileDialog. - * - * Due to the widgets, we get rid of the min sizes to allow them - * to pack closer together, and ensure we have enough padding for - * the drop-down menu in the popup. - */ -QDialog QToolBar QToolButton[popupMode="0"], -QDialog QToolBar QToolButton[popupMode="1"] -{ - padding-left: 0.1em; - padding-right: 0.1em; -} - -QDialog QToolBar QToolButton[popupMode="2"] -{ - padding-left: 0.1em; - padding-right: 0.7em; -} - -QPushButton -{ - color: #31363b; - background-color: #eaebec; - border: 0.04em solid #bab9b8; - padding: 0.23em; - border-radius: 0.09em; - outline: none; - min-height: 1.1em; -} - -QPushButton:flat, -QPushButton:flat:hover -{ - border: 0.04em transparent #bab9b8; -} - -QComboBox:open, -QPushButton:open -{ - border-width: 0.04em; - border-color: #bab9b8; -} - -QComboBox:closed, -QPushButton:closed -{ - border-width: 0.04em; - border-color: #bab9b8; -} - -QPushButton:disabled -{ - background-color: #eff0f1; - border-width: 0.04em; - border-color: #bab9b8; - border-style: solid; - padding-top: 0.23em; - padding-bottom: 0.23em; - padding-left: 1ex; - padding-right: 1ex; - border-radius: 0.04em; - color: #b4b4b4; -} - -QPushButton:focus -{ - color: #31363b; -} - -QPushButton:pressed -{ - background-color: #dfbeec; - padding-top: -0.65em; - padding-bottom: -0.74em; - color: #31363b; -} - -QComboBox -{ - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - padding: 0.23em; - min-width: 2.5em; -} - -QComboBox:editable -{ - background-color: #eff0f1; -} - -QPushButton:checked -{ - background-color: #c7c7c6; - border: 0.04em solid #bab9b8; - color: #31363b; -} - -QPushButton:hover -{ - background-color: #eaebec; - border: 0.04em solid rgba(164, 51, 223, 0.5); - color: #31363b; -} - -QPushButton:checked:hover -{ - background-color: #c7c7c6; - border: 0.04em solid rgba(164, 51, 223, 0.5); - color: #31363b; -} - -QComboBox:hover, -QComboBox:focus, -QAbstractSpinBox:hover, -QAbstractSpinBox:focus, -QLineEdit:hover, -QLineEdit:focus, -QTextEdit:hover, -QTextEdit:focus, -QPlainTextEdit:hover, -QPlainTextEdit:focus, -QAbstractView:hover, -QTreeView:hover, -QTreeView:focus -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); - color: #31363b; -} - -QComboBox:hover:pressed:!editable, -QPushButton:hover:pressed, -QAbstractSpinBox:hover:pressed, -QLineEdit:hover:pressed, -QTextEdit:hover:pressed, -QPlainTextEdit:hover:pressed, -QAbstractView:hover:pressed, -QTreeView:hover:pressed -{ - background-color: #eff0f1; -} - -QColumnView -{ - border: 0.04em transparent #eff0f1; -} - -QColumnViewGrip -{ - border-image: url(:/light-purple/sizegrip.svg); -} - -/* Each column in the view is a QAbstractItemView. */ -QColumnView QAbstractItemView -{ - border: 0.04em transparent rgba(164, 51, 223, 0.5); -} - -/** - * In order to set consistency between Qt5 and Qt6, we need - * to ensure that we do the following steps: - * 1. Set a consistent `max-height` in the item. Anything - * below `0.8em` will cause clipping, so set that value - * to ensure the icon isn't larger. - * 2. Set padding to ensure the item is properly padded. - * 3. Set `0.2em` margins on the top and bottom of the arrows, - * and `0.1em` on the left and right to ensure the arrows - * are properly padded and have the same size. - * - * The size consistency only works if both the `::item` subcontrol - * `max-height` and the `::*-arrow` subcontrol `margin` is set. - */ -QColumnView QAbstractItemView::item -{ - padding: 0.23em; - max-width: 0.5em; - max-height: 0.8em; -} - -QColumnView QAbstractItemView::right-arrow -{ - image: url(:/light-purple/right_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::right-arrow:selected, -QColumnView QAbstractItemView::right-arrow:hover -{ - image: url(:/light-purple/right_arrow_hover.svg); -} - -QColumnView QAbstractItemView::left-arrow -{ - image: url(:/light-purple/left_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::left-arrow:selected, -QColumnView QAbstractItemView::left-arrow:hover -{ - image: url(:/light-purple/left_arrow_hover.svg); -} - -QComboBox:hover:pressed:editable -{ - background-color: #eff0f1; -} - -QComboBox QAbstractItemView -{ - /* This happens for the drop-down menu always, whether editable or not.*/ - background-color: #eff0f1; - selection-background-color: rgba(147, 45, 200, 0.5); - outline-color: 0em; - border-radius: 0.09em; -} - -QComboBox::drop-down -{ - subcontrol-origin: padding; - subcontrol-position: top right; - width: 0.65em; - - border-left-width: 0em; - border-left-style: solid; - border-top-right-radius: 0.13em; - border-bottom-right-radius: 0.13em; -} - -QComboBox::down-arrow -{ - border-image: url(:/light-purple/down_arrow_disabled.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QComboBox::down-arrow:on, -QComboBox::down-arrow:hover, -QComboBox::down-arrow:focus -{ - border-image: url(:/light-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QAbstractSpinBox -{ - padding: 0.23em; - border: 0.09em solid #bab9b8; - background-color: #eff0f1; - color: #31363b; - border-radius: 0.09em; - min-width: 3em; - min-height: 1em; -} - -QAbstractSpinBox:hover -{ - border: 0.09em solid rgba(164, 51, 223, 0.5); -} - -QAbstractSpinBox:up-button, -QAbstractSpinBox:up-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center right; - padding-right: 0.1em; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox:down-button, -QAbstractSpinBox:down-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center left; - padding-left: 0.1em; - width: 0.8em; - height: 0.5em; -} - -/** - * Bug fixes for elongated items in QSpinBoxes. - * By default, the items are bounded by `down-button` - * and `up-button`, so this doesn't actually affect the styling. - * - * This does however affect some custom styling using - * QStyle.CC_ComboBox, which affects QDateEdit. This cannot - * be selected using QDateEdit, since it uses a global style. - * This sounds nonsensical, because CC_ComboBox isn't a spin box, - * but through trial and error, this is in fact the case. - * - * Affects #40. - */ -QAbstractSpinBox::up-arrow, -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off, -QAbstractSpinBox::up-arrow:!off:!disabled:hover, -QAbstractSpinBox::down-arrow, -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off, -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - border-image: none; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox::up-arrow -{ - image: url(:/light-purple/up_arrow.svg); -} - -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off -{ - image: url(:/light-purple/up_arrow_disabled.svg); -} - -QAbstractSpinBox::up-arrow:hover -{ - image: url(:/light-purple/up_arrow_hover.svg); -} - -QAbstractSpinBox::down-arrow -{ - image: url(:/light-purple/down_arrow.svg); -} - -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off -{ - image: url(:/light-purple/down_arrow_disabled.svg); -} - -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - image: url(:/light-purple/down_arrow_hover.svg); -} - -QDoubleSpinBox -{ - min-width: 4em; -} - -/** - * `QCalendarWidget QAbstractItemView:enabled` sets the color, background - * color, and selection color for active dates in the view. - * `QCalendarWidget QAbstractItemView:enabled` sets the disabled dates. - */ -QCalendarWidget QAbstractItemView:enabled -{ - color: #31363b; - selection-color: #31363b; - selection-background-color: rgba(164, 51, 223, 0.5); -} - -/* Won't take hover events. */ -QPrevNextCalButton -{ - min-width: 0.8em; - min-height: 1.2em; - qproperty-iconSize: 0px 0px; -} - -QPrevNextCalButton#qt_calendar_nextmonth -{ - image: url(:/light-purple/calendar_next.svg); -} - -QPrevNextCalButton#qt_calendar_prevmonth -{ - image: url(:/light-purple/calendar_previous.svg); -} - -/** - * Setting for the month and year displays and drop-down menu for the - * month select. We style this separately because we want a drop-down - * indicator in the bottom right, unlike the normal QToolButton. - */ -QCalendarWidget QToolButton -{ - background-color: transparent; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; - min-height: 1.1em; -} - -QCalendarWidget QToolButton:hover -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); -} - -QCalendarWidget QToolButton:checked, -QCalendarWidget QToolButton:pressed -{ - background-color: rgba(164, 51, 223, 0.5); - padding: 0.23em; - padding-right: 1.2em; - min-height: 1.3em; - outline: none; -} - -/** - * The QCalendarWidget for QDateTimeEdit seems to improperly - * style the year QToolButton, which has an object name - * `qt_datetimedit_calendar`, so ensure we style it as well. - */ -QCalendarWidget QToolButton::menu-indicator, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-indicator -{ - border-image: none; - image: url(:/light-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - top: -0.7ex; - left: -0.09em; - padding-right: -1.11em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -QCalendarWidget QToolButton::menu-arrow, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-arrow -{ - border-image: none; - image: url(:/light-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -/** - * Setting for the year button. Both the month select and the year - * select are QToolButtons, and both are auto-raised. The year - * button however has the popup mode set to `DelayedPopup`. - */ -QCalendarWidget QToolButton[autoRaise="true"][popupMode="0"] -{ - padding: 0.23em; -} - -QCalendarWidget QSpinBox -{ - max-height: 1.5em; - min-width: 3.5em; - margin: 0em; - margin-top: 0.2em; - padding: 0em; - outline: 0em; - padding-left: 0.5em; -} - -QLabel -{ - border: 0em solid black; -} - -/* BORDERS */ -QTabWidget::pane -{ - padding: 0.23em; - margin: 0.04em; -} - -QTabWidget::pane:top -{ - border: 0.04em solid #bab9b8; - top: -0.04em; -} - -QTabWidget::pane:bottom -{ - border: 0.04em solid #bab9b8; - bottom: -0.04em; -} - -QTabWidget::pane:left -{ - border: 0.04em solid #bab9b8; - left: -0.04em; -} - -QTabWidget::pane:right -{ - border: 0.04em solid #bab9b8; - right: -0.04em; -} - -QTabBar -{ - qproperty-drawBase: 0; - left: 0.23em; - border-radius: 0.13em; - /** - * Note: this is the underline for each tab title. It's not - * documented, and this took forever to track down. At least - * 10 hours have been wasted trying to turn off this line, - * do not deleted this comment. - */ - selection-color: transparent; -} - -QTabBar:focus -{ - border: 0em transparent black; -} - -QTabBar::close-button -{ - /* Doesn't seem possible to resize these buttons */ - border-image: url(:/light-purple/transparent.svg); - image: url(:/light-purple/close.svg); - background: transparent; -} - -QTabBar::close-button:hover -{ - image: url(:/light-purple/close_hover.svg); -} - -QTabBar::close-button:pressed -{ - image: url(:/light-purple/close_pressed.svg); -} - -/* TOP TABS */ -QTabBar::tab:top, -QTabBar::tab:top:last, -QTabBar::tab:top:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-left: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-top: 0.09em solid rgba(164, 51, 223, 0.5); - background-color: #eff0f1; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-right: 0.04em solid #bab9b8; - border-bottom: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:next-selected -{ - border-right: 0.04em transparent #d9d8d7; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:first:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* BOTTOM TABS */ -QTabBar::tab:bottom, -QTabBar::tab:bottom:last, -QTabBar::tab:bottom:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-left: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-bottom: 0.09em solid rgba(164, 51, 223, 0.5); - background-color: #eff0f1; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-top: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:next-selected -{ - border-right: 0.04em transparent #d9d8d7; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:first:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -/* LEFT TABS */ -QTabBar::tab:left, -QTabBar::tab:left:last, -QTabBar::tab:left:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-top: 0.09em solid rgba(164, 51, 223, 0.5); - border-bottom: 0.04em solid #bab9b8; - border-left: 0.04em solid #bab9b8; - background-color: #eff0f1; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-top: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:previous-selected -{ - border-top: 0.04em transparent #d9d8d7; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:first:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* RIGHT TABS */ -QTabBar::tab:right, -QTabBar::tab:right:last, -QTabBar::tab:right:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-top: 0.09em solid rgba(164, 51, 223, 0.5); - border-bottom: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - background-color: #eff0f1; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-top: 0.04em solid #bab9b8; - border-left: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:previous-selected -{ - border-top: 0.04em transparent #d9d8d7; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:first:hover -{ - background-color: rgba(194, 51, 223, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -/** - * Special styles for triangular QTabWidgets. - * These ignore the border attributes, and the border and - * text color seems to be set via the `QTabBar::tab` color - * property. This seemingly cannot be changed. - * - * The rounded shapes are 0-3, and the triangular ones are 4-7. - * - * The QTabBar outline doesn't respect on QTabBar::tab: - * border-color - * outline-color - */ -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - /* Need a dark color without alpha channel since it affects the text. */ - color: #c233df; - background-color: #eff0f1; - padding: 0.23em; -} - -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one -{ - min-width: 50px; -} - -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - min-height: 50px; -} - -QTabBar[shape="4"]::tab:!selected, -QTabBar[shape="5"]::tab:!selected, -QTabBar[shape="6"]::tab:!selected, -QTabBar[shape="7"]::tab:!selected -{ - color: #31363b; - background-color: #d9d8d7; -} - -/** - * Increase padding on the opposite side of the icon to avoid text clipping. - * - * BUG: The padding works for North, West, and East in Qt5, South does not - * work. All tab positions work for triangular tabs in Qt6. - */ -QTabBar[shape="4"][tabsClosable="true"]::tab, -QTabBar[shape="5"][tabsClosable="true"]::tab -{ - padding-left: 0.5em; -} - -QTabBar[shape="6"][tabsClosable="true"]::tab -{ - padding-bottom: 0.5em; -} - -QTabBar[shape="7"][tabsClosable="true"]::tab -{ - padding-top: 0.5em; -} - -/** -* Undo the padding for the tab. -* -* Enumerated values are North, South, West, East in that order, -* from 4-7. -* -* NOTE: Any higher padding will clip the icon. -*/ -QTabBar[shape="4"]::close-button, -QTabBar[shape="5"]::close-button -{ - padding-left: -0.12em; -} - -QTabBar[shape="6"]::close-button -{ - padding-bottom: -0.18em; -} - -QTabBar[shape="7"]::close-button -{ - padding-top: -0.18em; -} - -QDockWidget -{ - background: #eaebec; - /** - * It doesn't seem possible to change the border of the - * QDockWidget without changing the content margins. - */ - /** - * This is a bug fix so we can handle hover, pressed, and other events. - * Reference: https://stackoverflow.com/questions/32145080/qdockwidget-float-close-button-hover-images - */ - titlebar-close-icon: url(:/light-purple/transparent.svg); - titlebar-normal-icon: url(:/light-purple/transparent.svg); -} - -/** - * Don't style the title, since it gives a weird, missing border - * around the rest of the dock widget, which the remaining border - * cannot be removed. - * - * There is a bug in non-Breeze styles, where the icons are small. It - * doesn't change if we use `image` instead of `border-image`, nor if - * we use `qproperty-icon`, etc. The icon seem to be half the size - * of our desired values. - */ -QDockWidget::close-button, -QDockWidget::float-button -{ - border: 0.04em solid transparent; - border-radius: 0.09em; - background: transparent; - /* Maximum icon size for buttons */ - icon-size: 14px; -} - -QDockWidget::float-button -{ - border-image: url(:/light-purple/transparent.svg); - image: url(:/light-purple/undock.svg); -} - -QDockWidget::float-button:hover -{ - image: url(:/light-purple/undock_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::float-button:pressed -{ - image: url(:/light-purple/undock_hover.svg); -} - -QDockWidget::close-button -{ - border-image: url(:/light-purple/transparent.svg); - image: url(:/light-purple/close.svg); -} - -QDockWidget::close-button:hover -{ - image: url(:/light-purple/close_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::close-button:pressed -{ - image: url(:/light-purple/close_pressed.svg); -} - -QTreeView, -QListView -{ - background-color: #eff0f1; - border: 0em solid black; -} - -QTreeView:selected, -QTreeView:!selected, -QListView:selected, -QListView:!selected -{ - border: 0em solid black; -} - -QTreeView::branch:has-siblings -{ - border-image: url(:/light-purple/vline.svg); - image: none; -} - -/* These branch indicators don't scale */ -QTreeView::branch:!has-siblings -{ - border-image: none; - image: none; -} - -QTreeView::branch:has-siblings:adjoins-item -{ - border-image: url(:/light-purple/branch_more.svg); -} - -QTreeView::branch:!has-children:!has-siblings:adjoins-item -{ - border-image: url(:/light-purple/branch_end.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:closed:has-children:has-siblings -{ - image: url(:/light-purple/branch_closed.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed:hover, -QTreeView::branch:closed:has-children:has-siblings:hover -{ - image: url(:/light-purple/branch_closed_hover.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:open:has-children:!has-siblings -{ - border-image: url(:/light-purple/branch_end_arrow.svg); -} - -QTreeView::branch:closed:has-children:has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - border-image: url(:/light-purple/branch_more_arrow.svg); -} - -QTreeView::branch:open:has-children:!has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - image: url(:/light-purple/branch_open.svg); -} - -QTreeView::branch:open:has-children:!has-siblings:hover, -QTreeView::branch:open:has-children:has-siblings:hover -{ - image: url(:/light-purple/branch_open_hover.svg); -} - -QListView -{ - /* Give space for elements aligned left or right. */ - padding: 0.2em; -} - -QTableView::item, -QListView::item, -QTreeView::item -{ - padding: 0.13em; - color: #31363b; -} - -QTreeView::item -{ - /** - * Need to set the background color in Qt6, or else - * the QTreeView indicators use the style defaults, - * along with the box model, which conflicts with our - * theme (except with hover/focus/selected pseudostates). - * - * Affects issue #51. - */ - background-color: #eff0f1; - outline: 0; -} - -QTableView::item:!selected:hover, -QListView::item:!selected:hover, -QTreeView::item:!selected:hover -{ - background-color: rgba(194, 51, 223, 0.2); - outline: 0; - color: #31363b; - padding: 0.13em; -} - -QAbstractItemView::item QLineEdit -{ - border: 0em transparent black; - /* - * The top/bottom padding causes the editable widget to conceal text. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/69 - */ - padding: 0em; -} - -QSlider::handle:horizontal, -QSlider::handle:vertical -{ - background: #eff0f1; - border: 0.04em solid rgba(106, 105, 105, 0.7); - width: 0.7em; - height: 0.7em; - border-radius: 0.35em; -} - -QSlider:horizontal -{ - height: 2em; -} - -QSlider:vertical -{ - width: 2em; -} - -QSlider::handle:horizontal -{ - margin: -0.23em 0; -} - -QSlider::handle:vertical -{ - margin: 0 -0.23em; -} - -QSlider::groove:horizontal, -QSlider::groove:vertical -{ - background: #d9d8d7; - border: 0em solid #eff0f1; - border-radius: 0.19em; -} - -QSlider::groove:horizontal -{ - height: 0.4em; -} - -QSlider::groove:vertical -{ - width: 0.4em; -} - -QSlider::handle:horizontal:hover, -QSlider::handle:horizontal:focus, -QSlider::handle:vertical:hover, -QSlider::handle:vertical:focus -{ - border: 0.04em solid #c233df; -} - -QSlider::handle:horizontal:!focus:!hover, -QSlider::handle:vertical:!focus:!hover -{ - border: 0.04em solid rgba(106, 105, 105, 0.7); -} - -QSlider::sub-page:horizontal, -QSlider::add-page:vertical -{ - background: #c233df; - border-radius: 0.19em; -} - -QSlider::add-page:horizontal, -QSlider::sub-page:vertical -{ - background: rgba(106, 105, 105, 0.7); - border-radius: 0.19em; -} - -/* QToolButton */ -/** - * QToolButton's that have a push button need to be styled differently, - * depending on whether there are actions (a menu) associated with it. - * This is signaled by a drop-down arrow on the right of the push button. - * Unfortunately, there's no good property to determine this. The property - * we need is `QWidget::actions`, however, it's a method and not a - * property.Note that the drop-down menu is not signaled by any of the - * following: - * popupMode: any pop-up mode does not affect the right arrow style. - * arrowType: only replaces the icon. - * toolButtonStyle: this is almost always set to icon only, even with text. - * text: can have a drop-down menu with or without text. - * - * Notably, we need to ensure we don't pad the widgets in the following - * cases: - * 1. If the QToolButton is auto-raised. - * This adds undesired padding in`QFileDialog`. These widgets - * have text, even though no text is visible. This is not the - * default, so it won't affect most situations. - * 2. If the QToolButton does not have text. - * Normally, text-less buttons do not have a menu, and this - * is required for #47, since the padding affects the scroll - * bar icons in QTabBar. This causes major issues in the - * UI, so disable the padding by default. - * - * The padding can affect the placement of icons and other things - * inside the toolbutton: near the menu-button in QFileDialog, - * the clear text icon is misplaced vertically, making it nearly - * illegible. - * - * We provide special styles for a custom, dynamic property to - * override the padding decisions with or without a menu. - * To force styling as if there is a menu, set the `hasMenu` property - * to true. Setting `hasMenu` to false will style as if there is no menu. - * You can use `QWidget::setProperty` to set this property dynamically. - * - * The affected issues are #22, #28, #47. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/22 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/28 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/47 - */ - -/** - * Use an overly specific selector here to ensure no margins, - * or for the default QToolButton. We must have `autoRaise="false"` - * and `text` to have padding, so just add a `hasMenu="false"` to - * undo the padding in that case. Also add selectors for QDialog - * if a menu is explicitly forbidden. - */ -QToolButton, -QToolButton[hasMenu="false"][autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="2"] -{ - margin: 0em; - padding: 0em; -} - -QToolButton[autoRaise="false"] -{ - background-color: #eff0f1; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; -} - -QToolButton[autoRaise="true"] -{ - background-color: #eff0f1; - border: 0.04em solid transparent; -} - -/* Add selectors for the QDialog if a menu is explicitly requested. */ -QToolButton[hasMenu="true"], -QToolButton[autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="2"] -{ - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; -} - -QToolButton:hover -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); -} - -QToolButton:checked, -QToolButton:pressed -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); - background-color: rgba(164, 51, 223, 0.5); -} - -QToolButton::right-arrow, -QToolButton::left-arrow, -QToolButton::up-arrow, -QToolButton::down-arrow -{ - /** - * Do not set the arrow width/height here. It causes - * small icons in Qt6, and doesn't affect the styling - * in Qt5. Both look ideal without manually specified sizes. - */ - subcontrol-origin: content; - subcontrol-position: center; - margin: 0em; - padding: 0em; -} - -QToolButton::right-arrow:enabled -{ - image: url(:/light-purple/right_arrow.svg); -} - -QToolButton::left-arrow:enabled -{ - image: url(:/light-purple/left_arrow.svg); -} - -QToolButton::up-arrow:enabled -{ - image: url(:/light-purple/up_arrow.svg); -} - -QToolButton::down-arrow:enabled -{ - image: url(:/light-purple/down_arrow.svg); -} - -QToolButton::right-arrow:disabled -{ - image: url(:/light-purple/right_arrow_disabled.svg); -} - -QToolButton::left-arrow:disabled -{ - image: url(:/light-purple/left_arrow_disabled.svg); -} - -QToolButton::up-arrow:disabled -{ - image: url(:/light-purple/up_arrow_disabled.svg); -} - -QToolButton::down-arrow:disabled -{ - image: url(:/light-purple/down_arrow_disabled.svg); -} - -QToolButton::menu-indicator -{ - border-image: none; - image: url(:/light-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - left: -0.09em; - /* -1.2em + 0.09em */ - padding-right: -1.11em; - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: right; -} - -/** - * Special rule for the drop-down indicator in a QFileDialog. - * We want these to be more compact, hence the smaller padding. - */ -QDialog QToolBar QToolButton[popupMode="2"]::menu-indicator -{ - padding-right: -0.7em; -} - -QToolButton::menu-arrow -{ - border-image: none; - image: url(:/light-purple/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button -{ - border-top-right-radius: 0.5em; - border-bottom-right-radius: 0.5em; - /* 1ex width + 0.4ex for border + no text = 2ex allocated above */ - width: 1.3em; - padding: 0.23em; - outline: none; -} - -QToolButton::menu-button::menu-arrow -{ - left: -0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button:hover -{ - background-color: transparent; -} - -QToolButton::menu-button:pressed -{ - background-color: transparent; - padding: 0.23em; - outline: none; -} - -QTableView -{ - border: 0em solid black; - gridline-color: #bab9b8; - background-color: #eff0f1; -} - -QTableView:!selected, -QTableView:selected -{ - border: 0em solid black; -} - -QTableView -{ - border-radius: 0em; -} - -QAbstractItemView::item -{ - color: #31363b; -} - -QAbstractItemView::item:pressed -{ - background: rgba(147, 45, 200, 0.5); - color: #31363b; -} - -QAbstractItemView::item:selected:!active -{ - background: rgba(194, 51, 223, 0.2); -} - -/* Use background with qlineargradient to avoid ugly border on widget. */ -QAbstractItemView::item:selected:active -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 rgba(147, 45, 200, 0.5), - stop: 1 rgba(147, 45, 200, 0.5) - ); - color: #31363b; -} - -QAbstractItemView::item:selected:hover -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 rgba(184, 71, 243, 0.6), - stop: 1 rgba(184, 71, 243, 0.6) - ); - color: #31363b; -} - -QHeaderView -{ - background-color: #eff0f1; - border: 0.04em transparent; - border-radius: 0em; - margin: 0em; - padding: 0em; -} - -QHeaderView::section -{ - background-color: #eff0f1; - border: 0.04em solid #bab9b8; - color: #31363b; - border-radius: 0em; - padding: 0em 0.23em 0em 0.23em; - text-align: center; -} - -QHeaderView::section::vertical::first, -QHeaderView::section::vertical::only-one -{ - border-top: 0.04em solid #bab9b8; -} - -QHeaderView::section::vertical -{ - border-top: transparent; -} - -QHeaderView::section::horizontal::first, -QHeaderView::section::horizontal::only-one -{ - border-left: 0.04em solid #bab9b8; -} - -QHeaderView::section::horizontal -{ - border-left: transparent; -} - -QHeaderView[showSortIndicator="true"]::section::horizontal -{ - /* Same as the width of the arrow subcontrols below. */ - padding-right: 0.8em; -} - -QHeaderView::section:checked -{ - color: #272b2f; - background-color: #dab9e7; -} - -/* Note that this doesn't work for QTreeView unless the header is clickable */ -QHeaderView::section:hover, -QHeaderView::section::horizontal::first:hover, -QHeaderView::section::horizontal::only-one:hover, -QHeaderView::section::vertical::first:hover, -QHeaderView::section::vertical::only-one:hover -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); -} - -QHeaderView[showSortIndicator="true"]::down-arrow -{ - image: url(:/light-purple/down_arrow.svg); - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - /** - * Qt5 and Qt6 have different ideas of the padding of the - * arrow subcontrols: using `padding-left` to ensure that - * the width is undone fixes the padding of the content by - * an extra `0.8em` in Qt6, but doesn't affect Qt5. - */ - padding-right: 0.09em; - padding-left: -0.8em; -} - -QHeaderView[showSortIndicator="true"]::up-arrow -{ - image: url(:/light-purple/up_arrow.svg); - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - padding-left: -0.8em; -} - -QTableView QTableCornerButton::section -{ - background-color: #eff0f1; - border: 0.04em transparent #bab9b8; - border-top: 0.04em solid #bab9b8; - border-left: 0.04em solid #bab9b8; - border-radius: 0em; -} - -/* No hover event */ -QTableView QTableCornerButton:hover -{ - border: 0.04em transparent #bab9b8; -} - -QTableView QTableCornerButton::section:pressed -{ - border: 0.04em solid rgba(164, 51, 223, 0.5); - border-radius: 0em; -} - -QToolBox -{ - padding: 0.23em; - border: 0.09em transparent black; -} - -QToolBox::tab -{ - border-bottom: 0.09em solid #bab9b8; - margin-left: 1.5em; -} - -QToolBox::tab:selected, -QToolBox::tab:hover -{ - border-bottom: 0.09em solid rgba(164, 51, 223, 0.5); -} - -QSplitter::handle -{ - border: 0.09em solid #d9d8d7; - background: -0.5em solid #d9d8d7; - max-width: 0em; - max-height: 0em; -} - -/** - * It's not possible to get satisfactory rounded borders here. - * If you set the border to be negative, while adjusting the - * widths, you get an asymmetrical curve which produces an - * unappealing border. - */ -QProgressBar:horizontal, -QProgressBar:vertical -{ - background-color: rgba(106, 105, 105, 0.7); - border: 0.9em solid #eff0f1; - border-radius: 0.13em; - padding: 0em; -} - -QProgressBar:horizontal -{ - height: 0.2em; - min-width: 6em; - text-align: right; - padding-left: -0.03em; - padding-right: -0.03em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-right: 1.3em; -} - -QProgressBar:vertical -{ - width: 0.2em; - min-height: 6em; - text-align: bottom; - padding-top: -0.03em; - padding-bottom: -0.03em; - margin-left: 0.2em; - margin-right: 0.2em; - margin-bottom: 0.41em; -} - -QProgressBar::chunk:horizontal, -QProgressBar::chunk:vertical -{ - background-color: #c233df; - border: 0.9em transparent; - border-radius: 0.08em; -} - -QScrollArea, -QScrollArea:focus, -QScrollArea:hover -{ - border: 0em solid black; -} - -/* ICONS */ -/** - * Qt's built-in icons can look pretty bad if the system theme - * is a different color than the current one. For example, when - * using a dark theme, with a light UI, the `Ok` button is greyed - * out for an about dialog. - * - * QDialogButtonBox will apply for all standard buttons in all standard - * widgets, such as QMessageBox, etc. However, we do need to override - * standard icons elsewhere. - * - * The rest of the icons make little sense to implement: - * Qt uses native window decorations. - * Qt normally uses native file dialogs, which look nicer. - * Media controls are used in custom widgets, which aren't standard. - */ -QDialogButtonBox -{ - dialogbuttonbox-buttons-have-icons: true; - - dialog-cancel-icon: url(:/light-purple/dialog_cancel.svg); - dialog-close-icon: url(:/light-purple/dialog_close.svg); - dialog-ok-icon: url(:/light-purple/dialog_ok.svg); - dialog-open-icon: url(:/light-purple/dialog_open.svg); - dialog-reset-icon: url(:/light-purple/dialog_reset.svg); - dialog-save-icon: url(:/light-purple/dialog_save.svg); - /** - * No support yet for overriding saveall. - * dialog-saveall-icon: url(:/light-purple/dialog_saveall.svg); - */ - dialog-yes-icon: url(:/light-purple/dialog_ok.svg); - dialog-help-icon: url(:/light-purple/dialog_help.svg); - dialog-no-icon: url(:/light-purple/dialog_no.svg); - dialog-apply-icon: url(:/light-purple/dialog_ok.svg); - dialog-discard-icon: url(:/light-purple/dialog_discard.svg); -} - -/* Set some styles for these custom dialog buttons */ -QDialogButtonBox QPushButton, -QMessageBox QPushButton -{ - min-height: 1.1em; - min-width: 5em; -} - -/** - * Special rules for creating a custom titlebar. This can only work - * when setting the Qt property `isTitlebar` to `true`. - */ -QWidget[isTitlebar="true"], -QWidget[isTitlebar="true"] * -{ - background-color: #d9d8d7; -} - -/** - * Special rules for creating a border around a top-level frame of a window. - * This can only work when setting the Qt property `isWindow` to `true`. - * We've manually enumerated border widths from 1-5 below. - */ -QFrame[isWindow="true"], -QFrame[frameShape][isWindow="true"] -{ - border: 0px transparent #d9d8d7; -} - -QFrame[isWindow="true"][windowFrame="1"], -QFrame[frameShape][isWindow="true"][windowFrame="1"] -{ - border: 1px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="2"], -QFrame[frameShape][isWindow="true"][windowFrame="2"] -{ - border: 2px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="3"], -QFrame[frameShape][isWindow="true"][windowFrame="3"] -{ - border: 3px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="4"], -QFrame[frameShape][isWindow="true"][windowFrame="4"] -{ - border: 4px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="5"], -QFrame[frameShape][isWindow="true"][windowFrame="5"] -{ - border: 5px solid #d9d8d7; - border-radius: 3px; -} diff --git a/lib/theme/qrc/light-purple/transparent.svg b/lib/theme/qrc/light-purple/transparent.svg deleted file mode 100644 index 3a8ca5cf..00000000 --- a/lib/theme/qrc/light-purple/transparent.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/theme/qrc/light-purple/trash.svg b/lib/theme/qrc/light-purple/trash.svg deleted file mode 100644 index af968619..00000000 --- a/lib/theme/qrc/light-purple/trash.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/undock.svg b/lib/theme/qrc/light-purple/undock.svg deleted file mode 100644 index 886196c8..00000000 --- a/lib/theme/qrc/light-purple/undock.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/undock_hover.svg b/lib/theme/qrc/light-purple/undock_hover.svg deleted file mode 100644 index 3a031ba0..00000000 --- a/lib/theme/qrc/light-purple/undock_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/undock_hover_pressed.svg b/lib/theme/qrc/light-purple/undock_hover_pressed.svg deleted file mode 100644 index 288c863e..00000000 --- a/lib/theme/qrc/light-purple/undock_hover_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/unshade.svg b/lib/theme/qrc/light-purple/unshade.svg deleted file mode 100644 index a8b30667..00000000 --- a/lib/theme/qrc/light-purple/unshade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/up_arrow.svg b/lib/theme/qrc/light-purple/up_arrow.svg deleted file mode 100644 index 4f65d325..00000000 --- a/lib/theme/qrc/light-purple/up_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/up_arrow_disabled.svg b/lib/theme/qrc/light-purple/up_arrow_disabled.svg deleted file mode 100644 index e409602a..00000000 --- a/lib/theme/qrc/light-purple/up_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/up_arrow_hover.svg b/lib/theme/qrc/light-purple/up_arrow_hover.svg deleted file mode 100644 index 2f98dfea..00000000 --- a/lib/theme/qrc/light-purple/up_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/vline.svg b/lib/theme/qrc/light-purple/vline.svg deleted file mode 100644 index 5d35af38..00000000 --- a/lib/theme/qrc/light-purple/vline.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light-purple/vmovetoolbar.svg b/lib/theme/qrc/light-purple/vmovetoolbar.svg deleted file mode 100644 index b7142346..00000000 --- a/lib/theme/qrc/light-purple/vmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light-purple/vseptoolbar.svg b/lib/theme/qrc/light-purple/vseptoolbar.svg deleted file mode 100644 index 1510cdf4..00000000 --- a/lib/theme/qrc/light-purple/vseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light-purple/window_close.svg b/lib/theme/qrc/light-purple/window_close.svg deleted file mode 100644 index 87341de8..00000000 --- a/lib/theme/qrc/light-purple/window_close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/branch_closed.svg b/lib/theme/qrc/light/branch_closed.svg deleted file mode 100644 index 5365e9e4..00000000 --- a/lib/theme/qrc/light/branch_closed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/branch_closed_hover.svg b/lib/theme/qrc/light/branch_closed_hover.svg deleted file mode 100644 index 91f8e3a2..00000000 --- a/lib/theme/qrc/light/branch_closed_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/branch_end.svg b/lib/theme/qrc/light/branch_end.svg deleted file mode 100644 index 70ea5fde..00000000 --- a/lib/theme/qrc/light/branch_end.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/branch_end_arrow.svg b/lib/theme/qrc/light/branch_end_arrow.svg deleted file mode 100644 index 5f25d556..00000000 --- a/lib/theme/qrc/light/branch_end_arrow.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/branch_more.svg b/lib/theme/qrc/light/branch_more.svg deleted file mode 100644 index 543a3538..00000000 --- a/lib/theme/qrc/light/branch_more.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/branch_more_arrow.svg b/lib/theme/qrc/light/branch_more_arrow.svg deleted file mode 100644 index 66814477..00000000 --- a/lib/theme/qrc/light/branch_more_arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light/branch_open.svg b/lib/theme/qrc/light/branch_open.svg deleted file mode 100644 index 5ef0d331..00000000 --- a/lib/theme/qrc/light/branch_open.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/branch_open_hover.svg b/lib/theme/qrc/light/branch_open_hover.svg deleted file mode 100644 index 233530b9..00000000 --- a/lib/theme/qrc/light/branch_open_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/calendar_next.svg b/lib/theme/qrc/light/calendar_next.svg deleted file mode 100644 index 5c2e0d51..00000000 --- a/lib/theme/qrc/light/calendar_next.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/calendar_previous.svg b/lib/theme/qrc/light/calendar_previous.svg deleted file mode 100644 index 04ebc9c2..00000000 --- a/lib/theme/qrc/light/calendar_previous.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/checkbox_checked.svg b/lib/theme/qrc/light/checkbox_checked.svg deleted file mode 100644 index 506f0205..00000000 --- a/lib/theme/qrc/light/checkbox_checked.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light/checkbox_checked_disabled.svg b/lib/theme/qrc/light/checkbox_checked_disabled.svg deleted file mode 100644 index fd8fa467..00000000 --- a/lib/theme/qrc/light/checkbox_checked_disabled.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light/checkbox_indeterminate.svg b/lib/theme/qrc/light/checkbox_indeterminate.svg deleted file mode 100644 index 580d5229..00000000 --- a/lib/theme/qrc/light/checkbox_indeterminate.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/checkbox_indeterminate_disabled.svg b/lib/theme/qrc/light/checkbox_indeterminate_disabled.svg deleted file mode 100644 index a8b22cdc..00000000 --- a/lib/theme/qrc/light/checkbox_indeterminate_disabled.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/checkbox_unchecked.svg b/lib/theme/qrc/light/checkbox_unchecked.svg deleted file mode 100644 index 2e46929b..00000000 --- a/lib/theme/qrc/light/checkbox_unchecked.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/checkbox_unchecked_disabled.svg b/lib/theme/qrc/light/checkbox_unchecked_disabled.svg deleted file mode 100644 index af0f0b31..00000000 --- a/lib/theme/qrc/light/checkbox_unchecked_disabled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/clear_text.svg b/lib/theme/qrc/light/clear_text.svg deleted file mode 100644 index c3605d81..00000000 --- a/lib/theme/qrc/light/clear_text.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/close.svg b/lib/theme/qrc/light/close.svg deleted file mode 100644 index 13caf6f2..00000000 --- a/lib/theme/qrc/light/close.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/close_hover.svg b/lib/theme/qrc/light/close_hover.svg deleted file mode 100644 index 8e2d0ab1..00000000 --- a/lib/theme/qrc/light/close_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/close_pressed.svg b/lib/theme/qrc/light/close_pressed.svg deleted file mode 100644 index 850141a9..00000000 --- a/lib/theme/qrc/light/close_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/computer.svg b/lib/theme/qrc/light/computer.svg deleted file mode 100644 index 7b15b411..00000000 --- a/lib/theme/qrc/light/computer.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/desktop.svg b/lib/theme/qrc/light/desktop.svg deleted file mode 100644 index 919feedd..00000000 --- a/lib/theme/qrc/light/desktop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light/dialog_cancel.svg b/lib/theme/qrc/light/dialog_cancel.svg deleted file mode 100644 index 4bd6495a..00000000 --- a/lib/theme/qrc/light/dialog_cancel.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_close.svg b/lib/theme/qrc/light/dialog_close.svg deleted file mode 100644 index 6f1aa215..00000000 --- a/lib/theme/qrc/light/dialog_close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_discard.svg b/lib/theme/qrc/light/dialog_discard.svg deleted file mode 100644 index 2d6d0967..00000000 --- a/lib/theme/qrc/light/dialog_discard.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_help.svg b/lib/theme/qrc/light/dialog_help.svg deleted file mode 100644 index 86407302..00000000 --- a/lib/theme/qrc/light/dialog_help.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_no.svg b/lib/theme/qrc/light/dialog_no.svg deleted file mode 100644 index 6c4ed779..00000000 --- a/lib/theme/qrc/light/dialog_no.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_ok.svg b/lib/theme/qrc/light/dialog_ok.svg deleted file mode 100644 index 7756ff18..00000000 --- a/lib/theme/qrc/light/dialog_ok.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_open.svg b/lib/theme/qrc/light/dialog_open.svg deleted file mode 100644 index 65f282db..00000000 --- a/lib/theme/qrc/light/dialog_open.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_reset.svg b/lib/theme/qrc/light/dialog_reset.svg deleted file mode 100644 index 4af3b6b0..00000000 --- a/lib/theme/qrc/light/dialog_reset.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/dialog_save.svg b/lib/theme/qrc/light/dialog_save.svg deleted file mode 100644 index 346dc526..00000000 --- a/lib/theme/qrc/light/dialog_save.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/disc_drive.svg b/lib/theme/qrc/light/disc_drive.svg deleted file mode 100644 index 045b7ca9..00000000 --- a/lib/theme/qrc/light/disc_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/down_arrow.svg b/lib/theme/qrc/light/down_arrow.svg deleted file mode 100644 index 213c94ab..00000000 --- a/lib/theme/qrc/light/down_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/down_arrow_disabled.svg b/lib/theme/qrc/light/down_arrow_disabled.svg deleted file mode 100644 index a3ae7e5a..00000000 --- a/lib/theme/qrc/light/down_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/down_arrow_hover.svg b/lib/theme/qrc/light/down_arrow_hover.svg deleted file mode 100644 index 6fa931a4..00000000 --- a/lib/theme/qrc/light/down_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/file.svg b/lib/theme/qrc/light/file.svg deleted file mode 100644 index 59bdd092..00000000 --- a/lib/theme/qrc/light/file.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/file_dialog_contents.svg b/lib/theme/qrc/light/file_dialog_contents.svg deleted file mode 100644 index b98abd7a..00000000 --- a/lib/theme/qrc/light/file_dialog_contents.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/theme/qrc/light/file_dialog_detailed.svg b/lib/theme/qrc/light/file_dialog_detailed.svg deleted file mode 100644 index 03b519db..00000000 --- a/lib/theme/qrc/light/file_dialog_detailed.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/light/file_dialog_end.svg b/lib/theme/qrc/light/file_dialog_end.svg deleted file mode 100644 index 9ae29b94..00000000 --- a/lib/theme/qrc/light/file_dialog_end.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/file_dialog_info.svg b/lib/theme/qrc/light/file_dialog_info.svg deleted file mode 100644 index beef8f7f..00000000 --- a/lib/theme/qrc/light/file_dialog_info.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/lib/theme/qrc/light/file_dialog_list.svg b/lib/theme/qrc/light/file_dialog_list.svg deleted file mode 100644 index 40be548e..00000000 --- a/lib/theme/qrc/light/file_dialog_list.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/file_dialog_start.svg b/lib/theme/qrc/light/file_dialog_start.svg deleted file mode 100644 index 7ba87ddc..00000000 --- a/lib/theme/qrc/light/file_dialog_start.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/lib/theme/qrc/light/file_link.svg b/lib/theme/qrc/light/file_link.svg deleted file mode 100644 index dcb4cbe2..00000000 --- a/lib/theme/qrc/light/file_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/floppy_drive.svg b/lib/theme/qrc/light/floppy_drive.svg deleted file mode 100644 index 7f2cf84e..00000000 --- a/lib/theme/qrc/light/floppy_drive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/folder.svg b/lib/theme/qrc/light/folder.svg deleted file mode 100644 index 184d3f24..00000000 --- a/lib/theme/qrc/light/folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/folder_link.svg b/lib/theme/qrc/light/folder_link.svg deleted file mode 100644 index 4c3bd11f..00000000 --- a/lib/theme/qrc/light/folder_link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/folder_open.svg b/lib/theme/qrc/light/folder_open.svg deleted file mode 100644 index 95ebe9c1..00000000 --- a/lib/theme/qrc/light/folder_open.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/hard_drive.svg b/lib/theme/qrc/light/hard_drive.svg deleted file mode 100644 index a57f31b2..00000000 --- a/lib/theme/qrc/light/hard_drive.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/lib/theme/qrc/light/help.svg b/lib/theme/qrc/light/help.svg deleted file mode 100644 index 2ce5157e..00000000 --- a/lib/theme/qrc/light/help.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/hmovetoolbar.svg b/lib/theme/qrc/light/hmovetoolbar.svg deleted file mode 100644 index 87e684f9..00000000 --- a/lib/theme/qrc/light/hmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light/home_directory.svg b/lib/theme/qrc/light/home_directory.svg deleted file mode 100644 index 3ed08ab4..00000000 --- a/lib/theme/qrc/light/home_directory.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/hseptoolbar.svg b/lib/theme/qrc/light/hseptoolbar.svg deleted file mode 100644 index c4952263..00000000 --- a/lib/theme/qrc/light/hseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/left_arrow.svg b/lib/theme/qrc/light/left_arrow.svg deleted file mode 100644 index 80cf674c..00000000 --- a/lib/theme/qrc/light/left_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/left_arrow_disabled.svg b/lib/theme/qrc/light/left_arrow_disabled.svg deleted file mode 100644 index 0568aaff..00000000 --- a/lib/theme/qrc/light/left_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/left_arrow_hover.svg b/lib/theme/qrc/light/left_arrow_hover.svg deleted file mode 100644 index 668c3b91..00000000 --- a/lib/theme/qrc/light/left_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/maximize.svg b/lib/theme/qrc/light/maximize.svg deleted file mode 100644 index 1a011966..00000000 --- a/lib/theme/qrc/light/maximize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/menu.svg b/lib/theme/qrc/light/menu.svg deleted file mode 100644 index d7efaa64..00000000 --- a/lib/theme/qrc/light/menu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/message_critical.svg b/lib/theme/qrc/light/message_critical.svg deleted file mode 100644 index 5ab22603..00000000 --- a/lib/theme/qrc/light/message_critical.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/message_information.svg b/lib/theme/qrc/light/message_information.svg deleted file mode 100644 index 8c7639bc..00000000 --- a/lib/theme/qrc/light/message_information.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/light/message_question.svg b/lib/theme/qrc/light/message_question.svg deleted file mode 100644 index 8e12e7bd..00000000 --- a/lib/theme/qrc/light/message_question.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/lib/theme/qrc/light/message_warning.svg b/lib/theme/qrc/light/message_warning.svg deleted file mode 100644 index 166c1593..00000000 --- a/lib/theme/qrc/light/message_warning.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/lib/theme/qrc/light/minimize.svg b/lib/theme/qrc/light/minimize.svg deleted file mode 100644 index b7f6cad8..00000000 --- a/lib/theme/qrc/light/minimize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/network_drive.svg b/lib/theme/qrc/light/network_drive.svg deleted file mode 100644 index 578a99f2..00000000 --- a/lib/theme/qrc/light/network_drive.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/theme/qrc/light/radio_checked.svg b/lib/theme/qrc/light/radio_checked.svg deleted file mode 100644 index 58dad548..00000000 --- a/lib/theme/qrc/light/radio_checked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light/radio_checked_disabled.svg b/lib/theme/qrc/light/radio_checked_disabled.svg deleted file mode 100644 index 62a922c7..00000000 --- a/lib/theme/qrc/light/radio_checked_disabled.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light/radio_unchecked.svg b/lib/theme/qrc/light/radio_unchecked.svg deleted file mode 100644 index 2d6ba2e0..00000000 --- a/lib/theme/qrc/light/radio_unchecked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/radio_unchecked_disabled.svg b/lib/theme/qrc/light/radio_unchecked_disabled.svg deleted file mode 100644 index abd1b6e5..00000000 --- a/lib/theme/qrc/light/radio_unchecked_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/restore.svg b/lib/theme/qrc/light/restore.svg deleted file mode 100644 index 46684bf2..00000000 --- a/lib/theme/qrc/light/restore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/right_arrow.svg b/lib/theme/qrc/light/right_arrow.svg deleted file mode 100644 index 7a750fdd..00000000 --- a/lib/theme/qrc/light/right_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/right_arrow_disabled.svg b/lib/theme/qrc/light/right_arrow_disabled.svg deleted file mode 100644 index d2d29aeb..00000000 --- a/lib/theme/qrc/light/right_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/right_arrow_hover.svg b/lib/theme/qrc/light/right_arrow_hover.svg deleted file mode 100644 index c7c33da1..00000000 --- a/lib/theme/qrc/light/right_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/shade.svg b/lib/theme/qrc/light/shade.svg deleted file mode 100644 index 4731b181..00000000 --- a/lib/theme/qrc/light/shade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/sizegrip.svg b/lib/theme/qrc/light/sizegrip.svg deleted file mode 100644 index a0cd5356..00000000 --- a/lib/theme/qrc/light/sizegrip.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/stylesheet.qss b/lib/theme/qrc/light/stylesheet.qss deleted file mode 100644 index cb6d1dc8..00000000 --- a/lib/theme/qrc/light/stylesheet.qss +++ /dev/null @@ -1,2510 +0,0 @@ -/* - * BreezeDark stylesheet. - * - * :author: Colin Duquesnoy - * :editor: Alex Huszagh - * :license: MIT, see LICENSE.md - * - * This is originally a fork of QDarkStyleSheet, and is based on Breeze/ - * BreezeDark color scheme, but is in no way affiliated with KDE. - * - * --------------------------------------------------------------------- - * The MIT License (MIT) - * - * Copyright (c) <2013-2014> - * Copyright (c) <2015-2021> - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * --------------------------------------------------------------------- - */ - -/** - * MAIN STYLESHEET - * --------------- - */ - -QToolTip -{ - /* 0.2ex is the smallest value that's not ignored on Windows. */ - border: 0.04em solid #31363b; - background-image: none; - background-color: #eff0f1; - alternate-background-color: #eaebec; - color: #31363b; - padding: 0.1em; - opacity: 200; -} - -QWidget -{ - color: #31363b; - background-color: #eff0f1; - selection-background-color: rgba(51, 164, 223, 0.5); - selection-color: #31363b; - background-clip: border; - border-image: none; - - /* QDialogButtonBox icons */ - dialog-cancel-icon: url(:/light/dialog_cancel.svg); - dialog-close-icon: url(:/light/dialog_close.svg); - dialog-ok-icon: url(:/light/dialog_ok.svg); - dialog-open-icon: url(:/light/dialog_open.svg); - dialog-reset-icon: url(:/light/dialog_reset.svg); - dialog-save-icon: url(:/light/dialog_save.svg); - dialog-yes-icon: url(:/light/dialog_ok.svg); - dialog-help-icon: url(:/light/dialog_help.svg); - dialog-no-icon: url(:/light/dialog_no.svg); - dialog-apply-icon: url(:/light/dialog_ok.svg); - dialog-discard-icon: url(:/light/dialog_discard.svg); - - /* File icons */ - filedialog-backward-icon: url(:/light/left_arrow.svg); - filedialog-contentsview-icon: url(:/light/file_dialog_contents.svg); - filedialog-detailedview-icon: url(:/light/file_dialog_detailed.svg); - filedialog-end-icon: url(:/light/file_dialog_end.svg); - filedialog-infoview-icon: url(:/light/file_dialog_info.svg); - filedialog-listview-icon: url(:/light/file_dialog_list.svg); - filedialog-new-directory-icon: url(:/light/folder.svg); - filedialog-parent-directory-icon: url(:/light/up_arrow.svg); - filedialog-start-icon: url(:/light/file_dialog_start.svg); - directory-closed-icon: url(:/light/folder.svg); - directory-icon: url(:/light/folder.svg); - directory-link-icon: url(:/light/folder_link.svg); - directory-open-icon: url(:/light/folder_open.svg); - file-icon: url(:/light/file.svg); - file-link-icon: url(:/light/file_link.svg); - home-icon: url(:/light/home_directory.svg); - - /* QMessageBox icons */ - messagebox-critical-icon: url(:/light/message_critical.svg); - messagebox-information-icon: url(:/light/message_information.svg); - messagebox-question-icon: url(:/light/message_question.svg); - messagebox-warning-icon: url(:/light/message_warning.svg); - - /* Computer icons */ - computer-icon: url(:/light/computer.svg); - desktop-icon: url(:/light/desktop.svg); - cd-icon: url(:/light/disc_drive.svg); - dvd-icon: url(:/light/disc_drive.svg); - floppy-icon: url(:/light/floppy_drive.svg); - harddisk-icon: url(:/light/hard_drive.svg); - network-icon: url(:/light/network_drive.svg); - trash-icon: url(:/light/trash.svg); - - /* Arrow icons */ - uparrow-icon: url(:/light/up_arrow.svg); - downarrow-icon: url(:/light/down_arrow.svg); - leftarrow-icon: url(:/light/left_arrow.svg); - rightarrow-icon: url(:/light/right_arrow.svg); - backward-icon: url(:/light/left_arrow.svg); - forward-icon: url(:/light/right_arrow.svg); - - /* Titlebar icons */ - titlebar-close-icon: url(:/light/window_close.svg); - titlebar-contexthelp-icon: url(:/light/help.svg); - titlebar-maximize-icon: url(:/light/maximize.svg); - titlebar-menu-icon: url(:/light/menu.svg); - titlebar-minimize-icon: url(:/light/minimize.svg); - titlebar-normal-icon: url(:/light/restore.svg); - titlebar-shade-icon: url(:/light/shade.svg); - titlebar-unshade-icon: url(:/light/unshade.svg); - - /* Other icons */ - dockwidget-close-icon: url(:/light/close.svg); - /** - * Only available in Qt6, and causes other issues. See #62. - * lineedit-clear-button-icon: url(:/light/clear_text.svg); - */ -} - -QWidget:disabled -{ - color: #b4b4b4; - background-color: #eff0f1; -} - -QCheckBox -{ - spacing: 0.23em; - color: #31363b; - margin-bottom: 0.09em; - opacity: 200; -} - -QCheckBox:disabled -{ - color: #bab9b8; -} - -QGroupBox -{ - /* Need to make sure the groupbox doesn't compress below the title. */ - min-height: 1.2em; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - /** - * This gives us enough space at the top to ensure we can move the - * title to be inside the guidelines, and the padding at the top - * ensures we have space below the title. - */ - margin-top: 0.5em; - padding-top: 1em; -} - -QGroupBox:focus -{ - border: 0.04em solid #bab9b8; - border-radius: 0.09em; -} - -QGroupBox::title -{ - /* We need to move 0.6em up to be inside the lines, +1em for padding. */ - top: -1.6em; - subcontrol-origin: content; - subcontrol-position: top center; - background: #eff0f1; - padding-left: 0.2em; - padding-right: 0.2em; -} - -QGroupBox:flat -{ - border-top: 0.04em solid rgba(106, 105, 105, 0.7); - border-left: 0.04em transparent #bab9b8; - border-right: 0.04em transparent #bab9b8; - border-bottom: 0.04em transparent #bab9b8; -} - -QCheckBox::indicator, -QTreeView::indicator, -QGroupBox::indicator -{ - width: 1em; - height: 1em; -} - -QGroupBox::indicator:unchecked, -QGroupBox::indicator:unchecked:focus, -QCheckBox::indicator:unchecked, -QCheckBox::indicator:unchecked:focus, -QTreeView::indicator:unchecked, -QTreeView::indicator:unchecked:focus -{ - border-image: url(:/light/checkbox_unchecked_disabled.svg); -} - -QGroupBox::indicator:unchecked, -QCheckBox::indicator:unchecked:hover, -QCheckBox::indicator:unchecked:pressed, -QTreeView::indicator:unchecked:hover, -QTreeView::indicator:unchecked:pressed, -QGroupBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:pressed -{ - border: none; - border-image: url(:/light/checkbox_unchecked.svg); -} - -QCheckBox::indicator:checked, -QTreeView::indicator:checked, -QGroupBox::indicator:checked -{ - border-image: url(:/light/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QCheckBox::indicator:checked:focus, -QCheckBox::indicator:checked:pressed, -QTreeView::indicator:checked:hover, -QTreeView::indicator:checked:focus, -QTreeView::indicator:checked:pressed, -QGroupBox::indicator:checked:hover, -QGroupBox::indicator:checked:focus, -QGroupBox::indicator:checked:pressed -{ - border: none; - border-image: url(:/light/checkbox_checked.svg); -} - -QCheckBox::indicator:indeterminate, -QTreeView::indicator:indeterminate -{ - border-image: url(:/light/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:focus, -QCheckBox::indicator:indeterminate:hover, -QCheckBox::indicator:indeterminate:pressed, -QTreeView::indicator:indeterminate:focus, -QTreeView::indicator:indeterminate:hover, -QTreeView::indicator:indeterminate:pressed -{ - border-image: url(:/light/checkbox_indeterminate.svg); -} - -QCheckBox::indicator:indeterminate:disabled, -QTreeView::indicator:indeterminate:disabled -{ - border-image: url(:/light/checkbox_indeterminate_disabled.svg); -} - -QCheckBox::indicator:checked:disabled, -QTreeView::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled -{ - border-image: url(:/light/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QTreeView::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled -{ - border-image: url(:/light/checkbox_unchecked_disabled.svg); -} - -QRadioButton -{ - spacing: 0.23em; - outline: none; - color: #31363b; - margin-bottom: 0.09em; -} - -QRadioButton:disabled -{ - color: #bab9b8; -} - -QRadioButton::indicator -{ - width: 1em; - height: 1em; -} - -QRadioButton::indicator:unchecked, -QRadioButton::indicator:unchecked:focus -{ - border-image: url(:/light/radio_unchecked_disabled.svg); -} - -QRadioButton::indicator:unchecked:hover, -QRadioButton::indicator:unchecked:pressed -{ - border: none; - outline: none; - border-image: url(:/light/radio_unchecked.svg); -} - -QRadioButton::indicator:checked -{ - border: none; - outline: none; - border-image: url(:/light/radio_checked.svg); -} - -QRadioButton::indicator:checked:hover, -QRadioButton::indicator:checked:focus, -QRadioButton::indicator:checked:pressed -{ - border: none; - outline: none; - border-image: url(:/light/radio_checked.svg); -} - -QRadioButton::indicator:checked:disabled -{ - outline: none; - border-image: url(:/light/radio_checked_disabled.svg); -} - -QRadioButton::indicator:unchecked:disabled -{ - border-image: url(:/light/radio_unchecked_disabled.svg); -} - -QMenuBar -{ - background-color: #eff0f1; - color: #31363b; -} - -QMenuBar::item -{ - background: transparent; -} - -QMenuBar::item:selected -{ - background: transparent; - border: 0.04em solid rgba(51, 164, 223, 0.5); -} - -QMenuBar::item:disabled -{ - color: #bab9b8; -} - -QMenuBar::item:pressed -{ - background-color: rgba(51, 164, 223, 0.5); - color: #31363b; - margin-bottom: -0.09em; - padding-bottom: 0.09em; -} - -QMenu -{ - color: #31363b; - margin: 0.09em; -} - -QMenu::icon -{ - margin: 0.23em; -} - -QMenu::item -{ - /* Add extra padding on the right for the QMenu arrow */ - padding: 0.23em 1.5em 0.23em 1.3em; - border: 0.09em solid transparent; - background: transparent; -} - -QMenu::item:selected -{ - color: #31363b; - background-color: rgba(51, 164, 223, 0.5); -} - -QMenu::item:selected:disabled -{ - background-color: #eff0f1; -} - -QMenu::item:disabled -{ - color: #bab9b8; -} - -QMenu::indicator -{ - width: 0.8em; - height: 0.8em; - /* To align with QMenu::icon, which has a 0.23em margin. */ - margin-left: 0.3em; - subcontrol-position: center left; -} - -QMenu::indicator:non-exclusive:unchecked -{ - border-image: url(:/light/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:unchecked:selected -{ - border-image: url(:/light/checkbox_unchecked_disabled.svg); -} - -QMenu::indicator:non-exclusive:checked -{ - border-image: url(:/light/checkbox_checked.svg); -} - -QMenu::indicator:non-exclusive:checked:selected -{ - border-image: url(:/light/checkbox_checked.svg); -} - -QMenu::indicator:exclusive:unchecked -{ - border-image: url(:/light/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:unchecked:selected -{ - border-image: url(:/light/radio_unchecked_disabled.svg); -} - -QMenu::indicator:exclusive:checked -{ - border-image: url(:/light/radio_checked.svg); -} - -QMenu::indicator:exclusive:checked:selected -{ - border-image: url(:/light/radio_checked.svg); -} - -QMenu::right-arrow -{ - margin: 0.23em; - border-image: url(:/light/right_arrow.svg); - width: 0.5em; - height: 0.8em; -} - -QMenu::right-arrow:disabled -{ - border-image: url(:/light/right_arrow_disabled.svg); -} - -QAbstractItemView -{ - alternate-background-color: #eff0f1; - color: #31363b; - border: 0.09em solid #bab9b8; - border-radius: 0.09em; -} - -QTabWidget:focus, -QCheckBox:focus, -QRadioButton:focus, -QSlider:focus -{ - border: none; -} - -QLineEdit -{ - background-color: #eff0f1; - padding: 0.23em; - border-style: solid; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - color: #31363b; -} - -QAbstractScrollArea -{ - border-radius: 0.09em; - border: 0.09em solid #bab9b8; - background-color: transparent; -} - -/** - * This is the background for the box in the bottom-right corner - * whene both scrollbars are active. - */ -QAbstractScrollArea::corner -{ - background: #eff0f1; -} - -/** - * Can't do the KDE style of where the scrollbar handle - * becomes light on the hover, and only when the handle - * is hovered does it become stylized. This is because - * both the handle and the background events are treated - * together. - */ -QScrollBar:horizontal -{ - background-color: #eff0f1; - height: 0.65em; - margin: 0.13em 0.65em 0.13em 0.65em; - border: 0.04em transparent #eff0f1; - border-radius: 0.17em; -} - -QScrollBar:horizontal:hover -{ - background-color: #c7c7c6; -} - -QScrollBar::handle:horizontal -{ - background-color: rgba(51, 164, 223, 0.8); - border: 0.04em solid rgba(51, 164, 223, 0.8); - min-width: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:horizontal:hover -{ - background-color: rgba(51, 164, 223, 0.8); - border: 0.04em solid rgba(51, 164, 223, 0.8); -} - -QScrollBar::add-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/light/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal -{ - margin: 0em 0.13em 0em 0.13em; - border-image: url(:/light/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover, -QScrollBar::add-line:horizontal:on -{ - border-image: url(:/light/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal:hover, -QScrollBar::sub-line:horizontal:on -{ - border-image: url(:/light/transparent.svg); - width: 0.41em; - height: 0.41em; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, -QScrollBar::down-arrow:horizontal -{ - background: none; -} - -QScrollBar::add-page:horizontal, -QScrollBar::sub-page:horizontal -{ - background: none; -} - -QScrollBar:vertical -{ - background-color: #eff0f1; - width: 0.65em; - margin: 0.65em 0.13em 0.65em 0.13em; - border: 0.04em transparent #eff0f1; - border-radius: 0.17em; -} - -QScrollBar:vertical:hover -{ - background-color: #c7c7c6; -} - -QScrollBar::handle:vertical -{ - background-color: rgba(51, 164, 223, 0.8); - border: 0.04em solid rgba(51, 164, 223, 0.8); - min-height: 0.5em; - border-radius: 0.17em; -} - -QScrollBar::handle:vertical:hover -{ - background-color: rgba(51, 164, 223, 0.8); - border: 0.04em solid rgba(51, 164, 223, 0.8); -} - -QScrollBar::sub-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/light/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical -{ - margin: 0.13em 0em 0.13em 0em; - border-image: url(:/light/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover, -QScrollBar::sub-line:vertical:on -{ - border-image: url(:/light/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical:hover, -QScrollBar::add-line:vertical:on -{ - border-image: url(:/light/transparent.svg); - height: 0.41em; - width: 0.41em; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:vertical, -QScrollBar::down-arrow:vertical -{ - background: none; -} - -QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical -{ - background: none; -} - -QTextEdit -{ - background-color: #eff0f1; - color: #31363b; - border: 0.04em solid #bab9b8; -} - -QPlainTextEdit -{ - background-color: #eff0f1; - color: #31363b; - border-radius: 0.09em; - border: 0.04em solid #bab9b8; -} - -QSizeGrip -{ - border-image: url(:/light/sizegrip.svg); - width: 0.5em; - height: 0.5em; -} - -/** - * Set the separator to be transparent, since the dock has a border. - * On PyQt6, neither the border nor the background seem to be respected. - */ -QMainWindow::separator -{ - border: 0.09em transparent #bab9b8; - background: transparent; -} - -QMenu::separator -{ - height: 0.09em; - background-color: #bab9b8; - padding-left: 0.2em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-left: 0.41em; - margin-right: 0.41em; -} - -QFrame[frameShape="2"], /* QFrame::Panel == 0x0003 */ -QFrame[frameShape="3"], /* QFrame::WinPanel == 0x0003 */ -QFrame[frameShape="4"], /* QFrame::HLine == 0x0004 */ -QFrame[frameShape="5"], /* QFrame::VLine == 0x0005 */ -QFrame[frameShape="6"] /* QFrame::StyledPanel == 0x0006 */ -{ - border-width: 0.04em; - padding: 0.09em; - border-style: solid; - border-color: #eff0f1; - background-color: #bab9b8; - border-radius: 0.23em; -} - -/* Provide highlighting for frame objects. */ -QFrame[frameShape="2"]:hover, -QFrame[frameShape="3"]:hover, -QFrame[frameShape="4"]:hover, -QFrame[frameShape="5"]:hover, -QFrame[frameShape="6"]:hover -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); -} - -/* Don't provide an outline if we have a widget that takes up the space. */ -QFrame[frameShape] QAbstractItemView:hover -{ - border: 0em solid black; -} - -/** - * Note: I can't really change the background of the toolbars - * independently, since KDE Breeze has different colors for the - * window bar and the rest of the UI. The top toolbar uses - * the window style, and the rest use the application style, - * which we can't do. - */ -QToolBar -{ - font-weight: bold; -} - -QToolBar:horizontal -{ - background: 0.09em solid #eff0f1; -} - -QToolBar:vertical -{ - background: 0.09em solid #eff0f1; -} - -QToolBar::handle:horizontal -{ - border-image: url(:/light/hmovetoolbar.svg); -} - -QToolBar::handle:vertical -{ - border-image: url(:/light/vmovetoolbar.svg); -} - -QToolBar::separator:horizontal -{ - border-image: url(:/light/hseptoolbar.svg); -} - -QToolBar::separator:vertical -{ - border-image: url(:/light/vseptoolbar.svg); -} - -QToolBar QToolButton -{ - font-weight: bold; - border: 0.04em transparent black; - padding-left: 0.2em; - padding-right: 0.3em; -} - -QToolBar QToolButton:hover -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); -} - -QToolBar QToolButton:pressed -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); - /* The padding doesn't inherit from `QToolBar QToolButton`, so leave it in. */ - padding-left: 0.2em; - padding-right: 0.3em; -} - -/** - * Special rules for a QFileDialog. - * - * Due to the widgets, we get rid of the min sizes to allow them - * to pack closer together, and ensure we have enough padding for - * the drop-down menu in the popup. - */ -QDialog QToolBar QToolButton[popupMode="0"], -QDialog QToolBar QToolButton[popupMode="1"] -{ - padding-left: 0.1em; - padding-right: 0.1em; -} - -QDialog QToolBar QToolButton[popupMode="2"] -{ - padding-left: 0.1em; - padding-right: 0.7em; -} - -QPushButton -{ - color: #31363b; - background-color: #eaebec; - border: 0.04em solid #bab9b8; - padding: 0.23em; - border-radius: 0.09em; - outline: none; - min-height: 1.1em; -} - -QPushButton:flat, -QPushButton:flat:hover -{ - border: 0.04em transparent #bab9b8; -} - -QComboBox:open, -QPushButton:open -{ - border-width: 0.04em; - border-color: #bab9b8; -} - -QComboBox:closed, -QPushButton:closed -{ - border-width: 0.04em; - border-color: #bab9b8; -} - -QPushButton:disabled -{ - background-color: #eff0f1; - border-width: 0.04em; - border-color: #bab9b8; - border-style: solid; - padding-top: 0.23em; - padding-bottom: 0.23em; - padding-left: 1ex; - padding-right: 1ex; - border-radius: 0.04em; - color: #b4b4b4; -} - -QPushButton:focus -{ - color: #31363b; -} - -QPushButton:pressed -{ - background-color: #bedfec; - padding-top: -0.65em; - padding-bottom: -0.74em; - color: #31363b; -} - -QComboBox -{ - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - padding: 0.23em; - min-width: 2.5em; -} - -QComboBox:editable -{ - background-color: #eff0f1; -} - -QPushButton:checked -{ - background-color: #c7c7c6; - border: 0.04em solid #bab9b8; - color: #31363b; -} - -QPushButton:hover -{ - background-color: #eaebec; - border: 0.04em solid rgba(51, 164, 223, 0.5); - color: #31363b; -} - -QPushButton:checked:hover -{ - background-color: #c7c7c6; - border: 0.04em solid rgba(51, 164, 223, 0.5); - color: #31363b; -} - -QComboBox:hover, -QComboBox:focus, -QAbstractSpinBox:hover, -QAbstractSpinBox:focus, -QLineEdit:hover, -QLineEdit:focus, -QTextEdit:hover, -QTextEdit:focus, -QPlainTextEdit:hover, -QPlainTextEdit:focus, -QAbstractView:hover, -QTreeView:hover, -QTreeView:focus -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); - color: #31363b; -} - -QComboBox:hover:pressed:!editable, -QPushButton:hover:pressed, -QAbstractSpinBox:hover:pressed, -QLineEdit:hover:pressed, -QTextEdit:hover:pressed, -QPlainTextEdit:hover:pressed, -QAbstractView:hover:pressed, -QTreeView:hover:pressed -{ - background-color: #eff0f1; -} - -QColumnView -{ - border: 0.04em transparent #eff0f1; -} - -QColumnViewGrip -{ - border-image: url(:/light/sizegrip.svg); -} - -/* Each column in the view is a QAbstractItemView. */ -QColumnView QAbstractItemView -{ - border: 0.04em transparent rgba(51, 164, 223, 0.5); -} - -/** - * In order to set consistency between Qt5 and Qt6, we need - * to ensure that we do the following steps: - * 1. Set a consistent `max-height` in the item. Anything - * below `0.8em` will cause clipping, so set that value - * to ensure the icon isn't larger. - * 2. Set padding to ensure the item is properly padded. - * 3. Set `0.2em` margins on the top and bottom of the arrows, - * and `0.1em` on the left and right to ensure the arrows - * are properly padded and have the same size. - * - * The size consistency only works if both the `::item` subcontrol - * `max-height` and the `::*-arrow` subcontrol `margin` is set. - */ -QColumnView QAbstractItemView::item -{ - padding: 0.23em; - max-width: 0.5em; - max-height: 0.8em; -} - -QColumnView QAbstractItemView::right-arrow -{ - image: url(:/light/right_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::right-arrow:selected, -QColumnView QAbstractItemView::right-arrow:hover -{ - image: url(:/light/right_arrow_hover.svg); -} - -QColumnView QAbstractItemView::left-arrow -{ - image: url(:/light/left_arrow.svg); - margin: 0.2em 0.1em 0.2em 0.1em; -} - -QColumnView QAbstractItemView::left-arrow:selected, -QColumnView QAbstractItemView::left-arrow:hover -{ - image: url(:/light/left_arrow_hover.svg); -} - -QComboBox:hover:pressed:editable -{ - background-color: #eff0f1; -} - -QComboBox QAbstractItemView -{ - /* This happens for the drop-down menu always, whether editable or not.*/ - background-color: #eff0f1; - selection-background-color: rgba(45, 147, 200, 0.5); - outline-color: 0em; - border-radius: 0.09em; -} - -QComboBox::drop-down -{ - subcontrol-origin: padding; - subcontrol-position: top right; - width: 0.65em; - - border-left-width: 0em; - border-left-style: solid; - border-top-right-radius: 0.13em; - border-bottom-right-radius: 0.13em; -} - -QComboBox::down-arrow -{ - border-image: url(:/light/down_arrow_disabled.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QComboBox::down-arrow:on, -QComboBox::down-arrow:hover, -QComboBox::down-arrow:focus -{ - border-image: url(:/light/down_arrow.svg); - width: 0.8em; - height: 0.5em; - margin-right: 0.41em; -} - -QAbstractSpinBox -{ - padding: 0.23em; - border: 0.09em solid #bab9b8; - background-color: #eff0f1; - color: #31363b; - border-radius: 0.09em; - min-width: 3em; - min-height: 1em; -} - -QAbstractSpinBox:hover -{ - border: 0.09em solid rgba(51, 164, 223, 0.5); -} - -QAbstractSpinBox:up-button, -QAbstractSpinBox:up-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center right; - padding-right: 0.1em; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox:down-button, -QAbstractSpinBox:down-button:hover -{ - background-color: transparent; - subcontrol-origin: padding; - subcontrol-position: center left; - padding-left: 0.1em; - width: 0.8em; - height: 0.5em; -} - -/** - * Bug fixes for elongated items in QSpinBoxes. - * By default, the items are bounded by `down-button` - * and `up-button`, so this doesn't actually affect the styling. - * - * This does however affect some custom styling using - * QStyle.CC_ComboBox, which affects QDateEdit. This cannot - * be selected using QDateEdit, since it uses a global style. - * This sounds nonsensical, because CC_ComboBox isn't a spin box, - * but through trial and error, this is in fact the case. - * - * Affects #40. - */ -QAbstractSpinBox::up-arrow, -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off, -QAbstractSpinBox::up-arrow:!off:!disabled:hover, -QAbstractSpinBox::down-arrow, -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off, -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - border-image: none; - width: 0.8em; - height: 0.5em; -} - -QAbstractSpinBox::up-arrow -{ - image: url(:/light/up_arrow.svg); -} - -QAbstractSpinBox::up-arrow:disabled, -QAbstractSpinBox::up-arrow:off -{ - image: url(:/light/up_arrow_disabled.svg); -} - -QAbstractSpinBox::up-arrow:hover -{ - image: url(:/light/up_arrow_hover.svg); -} - -QAbstractSpinBox::down-arrow -{ - image: url(:/light/down_arrow.svg); -} - -QAbstractSpinBox::down-arrow:disabled, -QAbstractSpinBox::down-arrow:off -{ - image: url(:/light/down_arrow_disabled.svg); -} - -QAbstractSpinBox::down-arrow:!off:!disabled:hover -{ - image: url(:/light/down_arrow_hover.svg); -} - -QDoubleSpinBox -{ - min-width: 4em; -} - -/** - * `QCalendarWidget QAbstractItemView:enabled` sets the color, background - * color, and selection color for active dates in the view. - * `QCalendarWidget QAbstractItemView:enabled` sets the disabled dates. - */ -QCalendarWidget QAbstractItemView:enabled -{ - color: #31363b; - selection-color: #31363b; - selection-background-color: rgba(51, 164, 223, 0.5); -} - -/* Won't take hover events. */ -QPrevNextCalButton -{ - min-width: 0.8em; - min-height: 1.2em; - qproperty-iconSize: 0px 0px; -} - -QPrevNextCalButton#qt_calendar_nextmonth -{ - image: url(:/light/calendar_next.svg); -} - -QPrevNextCalButton#qt_calendar_prevmonth -{ - image: url(:/light/calendar_previous.svg); -} - -/** - * Setting for the month and year displays and drop-down menu for the - * month select. We style this separately because we want a drop-down - * indicator in the bottom right, unlike the normal QToolButton. - */ -QCalendarWidget QToolButton -{ - background-color: transparent; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; - min-height: 1.1em; -} - -QCalendarWidget QToolButton:hover -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); -} - -QCalendarWidget QToolButton:checked, -QCalendarWidget QToolButton:pressed -{ - background-color: rgba(51, 164, 223, 0.5); - padding: 0.23em; - padding-right: 1.2em; - min-height: 1.3em; - outline: none; -} - -/** - * The QCalendarWidget for QDateTimeEdit seems to improperly - * style the year QToolButton, which has an object name - * `qt_datetimedit_calendar`, so ensure we style it as well. - */ -QCalendarWidget QToolButton::menu-indicator, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-indicator -{ - border-image: none; - image: url(:/light/down_arrow.svg); - width: 0.8em; - height: 0.5em; - top: -0.7ex; - left: -0.09em; - padding-right: -1.11em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -QCalendarWidget QToolButton::menu-arrow, -#qt_datetimedit_calendar QCalendarWidget QToolButton::menu-arrow -{ - border-image: none; - image: url(:/light/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-origin: content; - subcontrol-position: bottom right; -} - -/** - * Setting for the year button. Both the month select and the year - * select are QToolButtons, and both are auto-raised. The year - * button however has the popup mode set to `DelayedPopup`. - */ -QCalendarWidget QToolButton[autoRaise="true"][popupMode="0"] -{ - padding: 0.23em; -} - -QCalendarWidget QSpinBox -{ - max-height: 1.5em; - min-width: 3.5em; - margin: 0em; - margin-top: 0.2em; - padding: 0em; - outline: 0em; - padding-left: 0.5em; -} - -QLabel -{ - border: 0em solid black; -} - -/* BORDERS */ -QTabWidget::pane -{ - padding: 0.23em; - margin: 0.04em; -} - -QTabWidget::pane:top -{ - border: 0.04em solid #bab9b8; - top: -0.04em; -} - -QTabWidget::pane:bottom -{ - border: 0.04em solid #bab9b8; - bottom: -0.04em; -} - -QTabWidget::pane:left -{ - border: 0.04em solid #bab9b8; - left: -0.04em; -} - -QTabWidget::pane:right -{ - border: 0.04em solid #bab9b8; - right: -0.04em; -} - -QTabBar -{ - qproperty-drawBase: 0; - left: 0.23em; - border-radius: 0.13em; - /** - * Note: this is the underline for each tab title. It's not - * documented, and this took forever to track down. At least - * 10 hours have been wasted trying to turn off this line, - * do not deleted this comment. - */ - selection-color: transparent; -} - -QTabBar:focus -{ - border: 0em transparent black; -} - -QTabBar::close-button -{ - /* Doesn't seem possible to resize these buttons */ - border-image: url(:/light/transparent.svg); - image: url(:/light/close.svg); - background: transparent; -} - -QTabBar::close-button:hover -{ - image: url(:/light/close_hover.svg); -} - -QTabBar::close-button:pressed -{ - image: url(:/light/close_pressed.svg); -} - -/* TOP TABS */ -QTabBar::tab:top, -QTabBar::tab:top:last, -QTabBar::tab:top:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-left: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-top: 0.09em solid rgba(51, 164, 223, 0.5); - background-color: #eff0f1; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-right: 0.04em solid #bab9b8; - border-bottom: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:next-selected -{ - border-right: 0.04em transparent #d9d8d7; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:top:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-bottom-left-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* BOTTOM TABS */ -QTabBar::tab:bottom, -QTabBar::tab:bottom:last, -QTabBar::tab:bottom:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-left: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-bottom: 0.09em solid rgba(51, 164, 223, 0.5); - background-color: #eff0f1; - padding: 0.23em; - min-width: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-top: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:next-selected -{ - border-right: 0.04em transparent #d9d8d7; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -QTabBar::tab:bottom:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-top-right-radius: 0em; -} - -/* LEFT TABS */ -QTabBar::tab:left, -QTabBar::tab:left:last, -QTabBar::tab:left:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-top: 0.09em solid rgba(51, 164, 223, 0.5); - border-bottom: 0.04em solid #bab9b8; - border-left: 0.04em solid #bab9b8; - background-color: #eff0f1; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-top: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:previous-selected -{ - border-top: 0.04em transparent #d9d8d7; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -QTabBar::tab:left:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-top-right-radius: 0em; - border-bottom-right-radius: 0em; -} - -/* RIGHT TABS */ -QTabBar::tab:right, -QTabBar::tab:right:last, -QTabBar::tab:right:only-one -{ - color: #31363b; - border: 0.04em transparent black; - border-top: 0.09em solid rgba(51, 164, 223, 0.5); - border-bottom: 0.04em solid #bab9b8; - border-right: 0.04em solid #bab9b8; - background-color: #eff0f1; - padding: 0.23em; - min-height: 50px; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected -{ - color: #31363b; - background-color: #d9d8d7; - border: 0.04em transparent black; - border-top: 0.04em solid #bab9b8; - border-left: 0.04em solid #bab9b8; - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:previous-selected -{ - border-top: 0.04em transparent #d9d8d7; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -QTabBar::tab:right:!selected:first:hover -{ - background-color: rgba(61, 173, 232, 0.2); - border-radius: 0.09em; - border-top-left-radius: 0em; - border-bottom-left-radius: 0em; -} - -/** - * Special styles for triangular QTabWidgets. - * These ignore the border attributes, and the border and - * text color seems to be set via the `QTabBar::tab` color - * property. This seemingly cannot be changed. - * - * The rounded shapes are 0-3, and the triangular ones are 4-7. - * - * The QTabBar outline doesn't respect on QTabBar::tab: - * border-color - * outline-color - */ -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - /* Need a dark color without alpha channel since it affects the text. */ - color: #3daef3; - background-color: #eff0f1; - padding: 0.23em; -} - -QTabBar[shape="4"]::tab, -QTabBar[shape="5"]::tab, -QTabBar[shape="4"]::tab:last, -QTabBar[shape="5"]::tab:last, -QTabBar[shape="4"]::tab:only-one, -QTabBar[shape="5"]::tab:only-one -{ - min-width: 50px; -} - -QTabBar[shape="6"]::tab, -QTabBar[shape="7"]::tab, -QTabBar[shape="6"]::tab:last, -QTabBar[shape="7"]::tab:last, -QTabBar[shape="6"]::tab:only-one, -QTabBar[shape="7"]::tab:only-one -{ - min-height: 50px; -} - -QTabBar[shape="4"]::tab:!selected, -QTabBar[shape="5"]::tab:!selected, -QTabBar[shape="6"]::tab:!selected, -QTabBar[shape="7"]::tab:!selected -{ - color: #31363b; - background-color: #d9d8d7; -} - -/** - * Increase padding on the opposite side of the icon to avoid text clipping. - * - * BUG: The padding works for North, West, and East in Qt5, South does not - * work. All tab positions work for triangular tabs in Qt6. - */ -QTabBar[shape="4"][tabsClosable="true"]::tab, -QTabBar[shape="5"][tabsClosable="true"]::tab -{ - padding-left: 0.5em; -} - -QTabBar[shape="6"][tabsClosable="true"]::tab -{ - padding-bottom: 0.5em; -} - -QTabBar[shape="7"][tabsClosable="true"]::tab -{ - padding-top: 0.5em; -} - -/** -* Undo the padding for the tab. -* -* Enumerated values are North, South, West, East in that order, -* from 4-7. -* -* NOTE: Any higher padding will clip the icon. -*/ -QTabBar[shape="4"]::close-button, -QTabBar[shape="5"]::close-button -{ - padding-left: -0.12em; -} - -QTabBar[shape="6"]::close-button -{ - padding-bottom: -0.18em; -} - -QTabBar[shape="7"]::close-button -{ - padding-top: -0.18em; -} - -QDockWidget -{ - background: #eaebec; - /** - * It doesn't seem possible to change the border of the - * QDockWidget without changing the content margins. - */ - /** - * This is a bug fix so we can handle hover, pressed, and other events. - * Reference: https://stackoverflow.com/questions/32145080/qdockwidget-float-close-button-hover-images - */ - titlebar-close-icon: url(:/light/transparent.svg); - titlebar-normal-icon: url(:/light/transparent.svg); -} - -/** - * Don't style the title, since it gives a weird, missing border - * around the rest of the dock widget, which the remaining border - * cannot be removed. - * - * There is a bug in non-Breeze styles, where the icons are small. It - * doesn't change if we use `image` instead of `border-image`, nor if - * we use `qproperty-icon`, etc. The icon seem to be half the size - * of our desired values. - */ -QDockWidget::close-button, -QDockWidget::float-button -{ - border: 0.04em solid transparent; - border-radius: 0.09em; - background: transparent; - /* Maximum icon size for buttons */ - icon-size: 14px; -} - -QDockWidget::float-button -{ - border-image: url(:/light/transparent.svg); - image: url(:/light/undock.svg); -} - -QDockWidget::float-button:hover -{ - image: url(:/light/undock_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::float-button:pressed -{ - image: url(:/light/undock_hover.svg); -} - -QDockWidget::close-button -{ - border-image: url(:/light/transparent.svg); - image: url(:/light/close.svg); -} - -QDockWidget::close-button:hover -{ - image: url(:/light/close_hover.svg); -} - -/* The :pressed events don't register, seems to be a Qt bug. */ -QDockWidget::close-button:pressed -{ - image: url(:/light/close_pressed.svg); -} - -QTreeView, -QListView -{ - background-color: #eff0f1; - border: 0em solid black; -} - -QTreeView:selected, -QTreeView:!selected, -QListView:selected, -QListView:!selected -{ - border: 0em solid black; -} - -QTreeView::branch:has-siblings -{ - border-image: url(:/light/vline.svg); - image: none; -} - -/* These branch indicators don't scale */ -QTreeView::branch:!has-siblings -{ - border-image: none; - image: none; -} - -QTreeView::branch:has-siblings:adjoins-item -{ - border-image: url(:/light/branch_more.svg); -} - -QTreeView::branch:!has-children:!has-siblings:adjoins-item -{ - border-image: url(:/light/branch_end.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:closed:has-children:has-siblings -{ - image: url(:/light/branch_closed.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed:hover, -QTreeView::branch:closed:has-children:has-siblings:hover -{ - image: url(:/light/branch_closed_hover.svg); -} - -QTreeView::branch:has-children:!has-siblings:closed, -QTreeView::branch:open:has-children:!has-siblings -{ - border-image: url(:/light/branch_end_arrow.svg); -} - -QTreeView::branch:closed:has-children:has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - border-image: url(:/light/branch_more_arrow.svg); -} - -QTreeView::branch:open:has-children:!has-siblings, -QTreeView::branch:open:has-children:has-siblings -{ - image: url(:/light/branch_open.svg); -} - -QTreeView::branch:open:has-children:!has-siblings:hover, -QTreeView::branch:open:has-children:has-siblings:hover -{ - image: url(:/light/branch_open_hover.svg); -} - -QListView -{ - /* Give space for elements aligned left or right. */ - padding: 0.2em; -} - -QTableView::item, -QListView::item, -QTreeView::item -{ - padding: 0.13em; - color: #31363b; -} - -QTreeView::item -{ - /** - * Need to set the background color in Qt6, or else - * the QTreeView indicators use the style defaults, - * along with the box model, which conflicts with our - * theme (except with hover/focus/selected pseudostates). - * - * Affects issue #51. - */ - background-color: #eff0f1; - outline: 0; -} - -QTableView::item:!selected:hover, -QListView::item:!selected:hover, -QTreeView::item:!selected:hover -{ - background-color: rgba(61, 173, 232, 0.2); - outline: 0; - color: #31363b; - padding: 0.13em; -} - -QAbstractItemView::item QLineEdit -{ - border: 0em transparent black; - /* - * The top/bottom padding causes the editable widget to conceal text. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/69 - */ - padding: 0em; -} - -QSlider::handle:horizontal, -QSlider::handle:vertical -{ - background: #eff0f1; - border: 0.04em solid rgba(106, 105, 105, 0.7); - width: 0.7em; - height: 0.7em; - border-radius: 0.35em; -} - -QSlider:horizontal -{ - height: 2em; -} - -QSlider:vertical -{ - width: 2em; -} - -QSlider::handle:horizontal -{ - margin: -0.23em 0; -} - -QSlider::handle:vertical -{ - margin: 0 -0.23em; -} - -QSlider::groove:horizontal, -QSlider::groove:vertical -{ - background: #d9d8d7; - border: 0em solid #eff0f1; - border-radius: 0.19em; -} - -QSlider::groove:horizontal -{ - height: 0.4em; -} - -QSlider::groove:vertical -{ - width: 0.4em; -} - -QSlider::handle:horizontal:hover, -QSlider::handle:horizontal:focus, -QSlider::handle:vertical:hover, -QSlider::handle:vertical:focus -{ - border: 0.04em solid #3daef3; -} - -QSlider::handle:horizontal:!focus:!hover, -QSlider::handle:vertical:!focus:!hover -{ - border: 0.04em solid rgba(106, 105, 105, 0.7); -} - -QSlider::sub-page:horizontal, -QSlider::add-page:vertical -{ - background: #3daef3; - border-radius: 0.19em; -} - -QSlider::add-page:horizontal, -QSlider::sub-page:vertical -{ - background: rgba(106, 105, 105, 0.7); - border-radius: 0.19em; -} - -/* QToolButton */ -/** - * QToolButton's that have a push button need to be styled differently, - * depending on whether there are actions (a menu) associated with it. - * This is signaled by a drop-down arrow on the right of the push button. - * Unfortunately, there's no good property to determine this. The property - * we need is `QWidget::actions`, however, it's a method and not a - * property.Note that the drop-down menu is not signaled by any of the - * following: - * popupMode: any pop-up mode does not affect the right arrow style. - * arrowType: only replaces the icon. - * toolButtonStyle: this is almost always set to icon only, even with text. - * text: can have a drop-down menu with or without text. - * - * Notably, we need to ensure we don't pad the widgets in the following - * cases: - * 1. If the QToolButton is auto-raised. - * This adds undesired padding in`QFileDialog`. These widgets - * have text, even though no text is visible. This is not the - * default, so it won't affect most situations. - * 2. If the QToolButton does not have text. - * Normally, text-less buttons do not have a menu, and this - * is required for #47, since the padding affects the scroll - * bar icons in QTabBar. This causes major issues in the - * UI, so disable the padding by default. - * - * The padding can affect the placement of icons and other things - * inside the toolbutton: near the menu-button in QFileDialog, - * the clear text icon is misplaced vertically, making it nearly - * illegible. - * - * We provide special styles for a custom, dynamic property to - * override the padding decisions with or without a menu. - * To force styling as if there is a menu, set the `hasMenu` property - * to true. Setting `hasMenu` to false will style as if there is no menu. - * You can use `QWidget::setProperty` to set this property dynamically. - * - * The affected issues are #22, #28, #47. - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/22 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/28 - * https://github.com/Alexhuszagh/BreezeStyleSheets/issues/47 - */ - -/** - * Use an overly specific selector here to ensure no margins, - * or for the default QToolButton. We must have `autoRaise="false"` - * and `text` to have padding, so just add a `hasMenu="false"` to - * undo the padding in that case. Also add selectors for QDialog - * if a menu is explicitly forbidden. - */ -QToolButton, -QToolButton[hasMenu="false"][autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="false"][popupMode="2"] -{ - margin: 0em; - padding: 0em; -} - -QToolButton[autoRaise="false"] -{ - background-color: #eff0f1; - border: 0.04em solid #bab9b8; - border-radius: 0.09em; -} - -QToolButton[autoRaise="true"] -{ - background-color: #eff0f1; - border: 0.04em solid transparent; -} - -/* Add selectors for the QDialog if a menu is explicitly requested. */ -QToolButton[hasMenu="true"], -QToolButton[autoRaise="false"][text], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="0"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="1"], -QDialog QToolBar QToolButton[hasMenu="true"][popupMode="2"] -{ - margin: 0.23em; - padding: 0.23em; - padding-top: 0.1em; - padding-right: 1.2em; -} - -QToolButton:hover -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); -} - -QToolButton:checked, -QToolButton:pressed -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); - background-color: rgba(51, 164, 223, 0.5); -} - -QToolButton::right-arrow, -QToolButton::left-arrow, -QToolButton::up-arrow, -QToolButton::down-arrow -{ - /** - * Do not set the arrow width/height here. It causes - * small icons in Qt6, and doesn't affect the styling - * in Qt5. Both look ideal without manually specified sizes. - */ - subcontrol-origin: content; - subcontrol-position: center; - margin: 0em; - padding: 0em; -} - -QToolButton::right-arrow:enabled -{ - image: url(:/light/right_arrow.svg); -} - -QToolButton::left-arrow:enabled -{ - image: url(:/light/left_arrow.svg); -} - -QToolButton::up-arrow:enabled -{ - image: url(:/light/up_arrow.svg); -} - -QToolButton::down-arrow:enabled -{ - image: url(:/light/down_arrow.svg); -} - -QToolButton::right-arrow:disabled -{ - image: url(:/light/right_arrow_disabled.svg); -} - -QToolButton::left-arrow:disabled -{ - image: url(:/light/left_arrow_disabled.svg); -} - -QToolButton::up-arrow:disabled -{ - image: url(:/light/up_arrow_disabled.svg); -} - -QToolButton::down-arrow:disabled -{ - image: url(:/light/down_arrow_disabled.svg); -} - -QToolButton::menu-indicator -{ - border-image: none; - image: url(:/light/down_arrow.svg); - width: 0.8em; - height: 0.5em; - left: -0.09em; - /* -1.2em + 0.09em */ - padding-right: -1.11em; - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: right; -} - -/** - * Special rule for the drop-down indicator in a QFileDialog. - * We want these to be more compact, hence the smaller padding. - */ -QDialog QToolBar QToolButton[popupMode="2"]::menu-indicator -{ - padding-right: -0.7em; -} - -QToolButton::menu-arrow -{ - border-image: none; - image: url(:/light/down_arrow.svg); - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button -{ - border-top-right-radius: 0.5em; - border-bottom-right-radius: 0.5em; - /* 1ex width + 0.4ex for border + no text = 2ex allocated above */ - width: 1.3em; - padding: 0.23em; - outline: none; -} - -QToolButton::menu-button::menu-arrow -{ - left: -0.09em; - subcontrol-position: right; -} - -QToolButton::menu-button:hover -{ - background-color: transparent; -} - -QToolButton::menu-button:pressed -{ - background-color: transparent; - padding: 0.23em; - outline: none; -} - -QTableView -{ - border: 0em solid black; - gridline-color: #bab9b8; - background-color: #eff0f1; -} - -QTableView:!selected, -QTableView:selected -{ - border: 0em solid black; -} - -QTableView -{ - border-radius: 0em; -} - -QAbstractItemView::item -{ - color: #31363b; -} - -QAbstractItemView::item:pressed -{ - background: rgba(45, 147, 200, 0.5); - color: #31363b; -} - -QAbstractItemView::item:selected:!active -{ - background: rgba(61, 173, 232, 0.2); -} - -/* Use background with qlineargradient to avoid ugly border on widget. */ -QAbstractItemView::item:selected:active -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 rgba(45, 147, 200, 0.5), - stop: 1 rgba(45, 147, 200, 0.5) - ); - color: #31363b; -} - -QAbstractItemView::item:selected:hover -{ - background: qlineargradient( - x1: 0.5, y1: 0.5 - x2: 0.5, y2: 1, - stop: 0 rgba(71, 184, 243, 0.6), - stop: 1 rgba(71, 184, 243, 0.6) - ); - color: #31363b; -} - -QHeaderView -{ - background-color: #eff0f1; - border: 0.04em transparent; - border-radius: 0em; - margin: 0em; - padding: 0em; -} - -QHeaderView::section -{ - background-color: #eff0f1; - border: 0.04em solid #bab9b8; - color: #31363b; - border-radius: 0em; - padding: 0em 0.23em 0em 0.23em; - text-align: center; -} - -QHeaderView::section::vertical::first, -QHeaderView::section::vertical::only-one -{ - border-top: 0.04em solid #bab9b8; -} - -QHeaderView::section::vertical -{ - border-top: transparent; -} - -QHeaderView::section::horizontal::first, -QHeaderView::section::horizontal::only-one -{ - border-left: 0.04em solid #bab9b8; -} - -QHeaderView::section::horizontal -{ - border-left: transparent; -} - -QHeaderView[showSortIndicator="true"]::section::horizontal -{ - /* Same as the width of the arrow subcontrols below. */ - padding-right: 0.8em; -} - -QHeaderView::section:checked -{ - color: #272b2f; - background-color: #b9dae7; -} - -/* Note that this doesn't work for QTreeView unless the header is clickable */ -QHeaderView::section:hover, -QHeaderView::section::horizontal::first:hover, -QHeaderView::section::horizontal::only-one:hover, -QHeaderView::section::vertical::first:hover, -QHeaderView::section::vertical::only-one:hover -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); -} - -QHeaderView[showSortIndicator="true"]::down-arrow -{ - image: url(:/light/down_arrow.svg); - /** - * Qt5 and Qt6 differ if the subcontrol-origin is set to - * the default, AKA, padding. Setting it to the content, - * which we adjust the padding to, makes it uniform between - * both. - */ - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - /** - * Qt5 and Qt6 have different ideas of the padding of the - * arrow subcontrols: using `padding-left` to ensure that - * the width is undone fixes the padding of the content by - * an extra `0.8em` in Qt6, but doesn't affect Qt5. - */ - padding-right: 0.09em; - padding-left: -0.8em; -} - -QHeaderView[showSortIndicator="true"]::up-arrow -{ - image: url(:/light/up_arrow.svg); - subcontrol-origin: content; - subcontrol-position: center right; - width: 0.8em; - height: 0.5em; - padding-right: 0.09em; - padding-left: -0.8em; -} - -QTableView QTableCornerButton::section -{ - background-color: #eff0f1; - border: 0.04em transparent #bab9b8; - border-top: 0.04em solid #bab9b8; - border-left: 0.04em solid #bab9b8; - border-radius: 0em; -} - -/* No hover event */ -QTableView QTableCornerButton:hover -{ - border: 0.04em transparent #bab9b8; -} - -QTableView QTableCornerButton::section:pressed -{ - border: 0.04em solid rgba(51, 164, 223, 0.5); - border-radius: 0em; -} - -QToolBox -{ - padding: 0.23em; - border: 0.09em transparent black; -} - -QToolBox::tab -{ - border-bottom: 0.09em solid #bab9b8; - margin-left: 1.5em; -} - -QToolBox::tab:selected, -QToolBox::tab:hover -{ - border-bottom: 0.09em solid rgba(51, 164, 223, 0.5); -} - -QSplitter::handle -{ - border: 0.09em solid #d9d8d7; - background: -0.5em solid #d9d8d7; - max-width: 0em; - max-height: 0em; -} - -/** - * It's not possible to get satisfactory rounded borders here. - * If you set the border to be negative, while adjusting the - * widths, you get an asymmetrical curve which produces an - * unappealing border. - */ -QProgressBar:horizontal, -QProgressBar:vertical -{ - background-color: rgba(106, 105, 105, 0.7); - border: 0.9em solid #eff0f1; - border-radius: 0.13em; - padding: 0em; -} - -QProgressBar:horizontal -{ - height: 0.2em; - min-width: 6em; - text-align: right; - padding-left: -0.03em; - padding-right: -0.03em; - margin-top: 0.2em; - margin-bottom: 0.2em; - margin-right: 1.3em; -} - -QProgressBar:vertical -{ - width: 0.2em; - min-height: 6em; - text-align: bottom; - padding-top: -0.03em; - padding-bottom: -0.03em; - margin-left: 0.2em; - margin-right: 0.2em; - margin-bottom: 0.41em; -} - -QProgressBar::chunk:horizontal, -QProgressBar::chunk:vertical -{ - background-color: #3daef3; - border: 0.9em transparent; - border-radius: 0.08em; -} - -QScrollArea, -QScrollArea:focus, -QScrollArea:hover -{ - border: 0em solid black; -} - -/* ICONS */ -/** - * Qt's built-in icons can look pretty bad if the system theme - * is a different color than the current one. For example, when - * using a dark theme, with a light UI, the `Ok` button is greyed - * out for an about dialog. - * - * QDialogButtonBox will apply for all standard buttons in all standard - * widgets, such as QMessageBox, etc. However, we do need to override - * standard icons elsewhere. - * - * The rest of the icons make little sense to implement: - * Qt uses native window decorations. - * Qt normally uses native file dialogs, which look nicer. - * Media controls are used in custom widgets, which aren't standard. - */ -QDialogButtonBox -{ - dialogbuttonbox-buttons-have-icons: true; - - dialog-cancel-icon: url(:/light/dialog_cancel.svg); - dialog-close-icon: url(:/light/dialog_close.svg); - dialog-ok-icon: url(:/light/dialog_ok.svg); - dialog-open-icon: url(:/light/dialog_open.svg); - dialog-reset-icon: url(:/light/dialog_reset.svg); - dialog-save-icon: url(:/light/dialog_save.svg); - /** - * No support yet for overriding saveall. - * dialog-saveall-icon: url(:/light/dialog_saveall.svg); - */ - dialog-yes-icon: url(:/light/dialog_ok.svg); - dialog-help-icon: url(:/light/dialog_help.svg); - dialog-no-icon: url(:/light/dialog_no.svg); - dialog-apply-icon: url(:/light/dialog_ok.svg); - dialog-discard-icon: url(:/light/dialog_discard.svg); -} - -/* Set some styles for these custom dialog buttons */ -QDialogButtonBox QPushButton, -QMessageBox QPushButton -{ - min-height: 1.1em; - min-width: 5em; -} - -/** - * Special rules for creating a custom titlebar. This can only work - * when setting the Qt property `isTitlebar` to `true`. - */ -QWidget[isTitlebar="true"], -QWidget[isTitlebar="true"] * -{ - background-color: #d9d8d7; -} - -/** - * Special rules for creating a border around a top-level frame of a window. - * This can only work when setting the Qt property `isWindow` to `true`. - * We've manually enumerated border widths from 1-5 below. - */ -QFrame[isWindow="true"], -QFrame[frameShape][isWindow="true"] -{ - border: 0px transparent #d9d8d7; -} - -QFrame[isWindow="true"][windowFrame="1"], -QFrame[frameShape][isWindow="true"][windowFrame="1"] -{ - border: 1px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="2"], -QFrame[frameShape][isWindow="true"][windowFrame="2"] -{ - border: 2px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="3"], -QFrame[frameShape][isWindow="true"][windowFrame="3"] -{ - border: 3px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="4"], -QFrame[frameShape][isWindow="true"][windowFrame="4"] -{ - border: 4px solid #d9d8d7; - border-radius: 3px; -} - -QFrame[isWindow="true"][windowFrame="5"], -QFrame[frameShape][isWindow="true"][windowFrame="5"] -{ - border: 5px solid #d9d8d7; - border-radius: 3px; -} diff --git a/lib/theme/qrc/light/transparent.svg b/lib/theme/qrc/light/transparent.svg deleted file mode 100644 index 3a8ca5cf..00000000 --- a/lib/theme/qrc/light/transparent.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/theme/qrc/light/trash.svg b/lib/theme/qrc/light/trash.svg deleted file mode 100644 index af968619..00000000 --- a/lib/theme/qrc/light/trash.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/undock.svg b/lib/theme/qrc/light/undock.svg deleted file mode 100644 index 886196c8..00000000 --- a/lib/theme/qrc/light/undock.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/undock_hover.svg b/lib/theme/qrc/light/undock_hover.svg deleted file mode 100644 index 3a031ba0..00000000 --- a/lib/theme/qrc/light/undock_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/undock_hover_pressed.svg b/lib/theme/qrc/light/undock_hover_pressed.svg deleted file mode 100644 index c39c8ade..00000000 --- a/lib/theme/qrc/light/undock_hover_pressed.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/unshade.svg b/lib/theme/qrc/light/unshade.svg deleted file mode 100644 index a8b30667..00000000 --- a/lib/theme/qrc/light/unshade.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/up_arrow.svg b/lib/theme/qrc/light/up_arrow.svg deleted file mode 100644 index 4f65d325..00000000 --- a/lib/theme/qrc/light/up_arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/up_arrow_disabled.svg b/lib/theme/qrc/light/up_arrow_disabled.svg deleted file mode 100644 index e409602a..00000000 --- a/lib/theme/qrc/light/up_arrow_disabled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/up_arrow_hover.svg b/lib/theme/qrc/light/up_arrow_hover.svg deleted file mode 100644 index 4a33aac9..00000000 --- a/lib/theme/qrc/light/up_arrow_hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/vline.svg b/lib/theme/qrc/light/vline.svg deleted file mode 100644 index 5d35af38..00000000 --- a/lib/theme/qrc/light/vline.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/theme/qrc/light/vmovetoolbar.svg b/lib/theme/qrc/light/vmovetoolbar.svg deleted file mode 100644 index b7142346..00000000 --- a/lib/theme/qrc/light/vmovetoolbar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lib/theme/qrc/light/vseptoolbar.svg b/lib/theme/qrc/light/vseptoolbar.svg deleted file mode 100644 index 1510cdf4..00000000 --- a/lib/theme/qrc/light/vseptoolbar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/theme/qrc/light/window_close.svg b/lib/theme/qrc/light/window_close.svg deleted file mode 100644 index 87341de8..00000000 --- a/lib/theme/qrc/light/window_close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/lib/ui/CameraSelectionDialog.ui b/lib/ui/CameraSelectionDialog.ui deleted file mode 100644 index ebf07170..00000000 --- a/lib/ui/CameraSelectionDialog.ui +++ /dev/null @@ -1,70 +0,0 @@ - - - CameraSelectionDialog - - - - 0 - 0 - 480 - 640 - - - - Select Camera... - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - CameraSelectionDialog - emitCamera() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - CameraSelectionDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - - emitCamera() - - diff --git a/lib/ui/MainWindow.ui b/lib/ui/MainWindow.ui deleted file mode 100644 index ff1031bc..00000000 --- a/lib/ui/MainWindow.ui +++ /dev/null @@ -1,495 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - true - - - QFRCDashboard - - - - true - - - true - - - - - - 0 - 0 - 800 - 22 - - - - - &File - - - - &Recent Files - - - - - - - - - - &Tab - - - - - - - - - &Widget - - - - - - - - - &About - - - - - - - &CameraServer - - - - - - &Settings - - - - - - - - - - - - - - &Save - - - Ctrl+S - - - - - Save &As... - - - Ctrl+Shift+S - - - - - &Open File... - - - Ctrl+O - - - - - &Close Tab - - - Ctrl+W - - - - - &New Tab... - - - Ctrl+T - - - - - &Rename Tab... - - - - - Re&size Tab... - - - - - New &NT Widget - - - Ctrl+N - - - - - &About - - - - - About &Qt - - - - - New &Graph - - - Ctrl+G - - - - - New &Camera View - - - - - Add &Camera... - - - - - &Preferences - - - Ctrl+, - - - - - &NT Server - - - - - New &Swerve Widget - - - - - - - action_NT_Server - triggered() - MainWindow - ntSettingsPopup() - - - -1 - -1 - - - 399 - 299 - - - - - actionAbout - triggered() - MainWindow - about() - - - -1 - -1 - - - 399 - 299 - - - - - actionAbout_Qt - triggered() - MainWindow - aboutQt() - - - -1 - -1 - - - 399 - 299 - - - - - actionClose_Tab - triggered() - MainWindow - closeTab() - - - -1 - -1 - - - 399 - 299 - - - - - actionNew_Camera_View - triggered() - MainWindow - newCameraView() - - - -1 - -1 - - - 399 - 299 - - - - - actionNew_Graph - triggered() - MainWindow - newGraph() - - - -1 - -1 - - - 399 - 299 - - - - - actionNew_NT_Widget - triggered() - MainWindow - newWidgetPopup() - - - -1 - -1 - - - 399 - 299 - - - - - actionNew_Tab - triggered() - MainWindow - newTab() - - - -1 - -1 - - - 399 - 299 - - - - - actionOpen - triggered() - MainWindow - openDialog() - - - -1 - -1 - - - 399 - 299 - - - - - actionRename_Tab - triggered() - MainWindow - renameTab() - - - -1 - -1 - - - 399 - 299 - - - - - actionResize_Tab - triggered() - MainWindow - setMaxSize() - - - -1 - -1 - - - 399 - 299 - - - - - actionSave - triggered() - MainWindow - save() - - - -1 - -1 - - - 399 - 299 - - - - - actionSave_As - triggered() - MainWindow - saveAs() - - - -1 - -1 - - - 399 - 299 - - - - - actionAdd_Camera - triggered() - MainWindow - cameraServerPopup() - - - -1 - -1 - - - 399 - 299 - - - - - action_Preferences - triggered() - MainWindow - preferences() - - - -1 - -1 - - - 399 - 299 - - - - - centralwidget - currentChanged(int) - MainWindow - forceUpdateTab(int) - - - 399 - 310 - - - 399 - 299 - - - - - actionNew_Swerve_Widget - triggered() - MainWindow - newSwerve() - - - -1 - -1 - - - 399 - 299 - - - - - - ntSettingsPopup() - save() - saveAs() - openDialog() - newTab() - closeTab() - setMaxSize() - renameTab() - newCameraView() - newWidgetPopup() - newGraph() - about() - aboutQt() - cameraServerPopup() - preferences() - refreshRecentFiles() - forceUpdateTab(int) - newSwerve() - - diff --git a/lib/ui/NTSettingsDialog.ui b/lib/ui/NTSettingsDialog.ui deleted file mode 100644 index 024fcb66..00000000 --- a/lib/ui/NTSettingsDialog.ui +++ /dev/null @@ -1,170 +0,0 @@ - - - NTSettingsDialog - - - - 0 - 0 - 433 - 152 - - - - NetworkTables Settings - - - - - - Server Address: - - - - - - - Whether or not the server address is a team number. - - - Use Team Number? - - - - - - - Port: - - - - - - - 1 - - - 65535 - - - 5810 - - - - - - - - - - - - - - 0.0.0.0 - - - - - - - Tab Selection Topic: - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - - Select Topic... - - - - - - - - - - - buttonBox - accepted() - NTSettingsDialog - serializeData() - - - 91 - 243 - - - 199 - 149 - - - - - buttonBox - rejected() - NTSettingsDialog - reject() - - - 91 - 243 - - - 199 - 149 - - - - - buttonBox - accepted() - NTSettingsDialog - accept() - - - 91 - 243 - - - 199 - 149 - - - - - topicSelect - clicked() - NTSettingsDialog - putTopic() - - - 380 - 101 - - - 216 - 75 - - - - - - dataReady(ServerData) - serializeData() - putTopic() - - diff --git a/lib/ui/NewWidgetTreeDialog.ui b/lib/ui/NewWidgetTreeDialog.ui deleted file mode 100644 index 46edfdad..00000000 --- a/lib/ui/NewWidgetTreeDialog.ui +++ /dev/null @@ -1,65 +0,0 @@ - - - NewWidgetTreeDialog - - - - 0 - 0 - 634 - 629 - - - - Select Widget... - - - - - - 100 - - - false - - - - Topic - - - - - Type - - - - - - - - QDialogButtonBox::Cancel - - - - - - - - - buttonBox - rejected() - NewWidgetTreeDialog - reject() - - - 316 - 607 - - - 316 - 314 - - - - - diff --git a/lib/ui/PreferencesDialog.ui b/lib/ui/PreferencesDialog.ui deleted file mode 100644 index ce869616..00000000 --- a/lib/ui/PreferencesDialog.ui +++ /dev/null @@ -1,112 +0,0 @@ - - - PreferencesDialog - - - - 0 - 0 - 388 - 97 - - - - QFRCDashboard Preferences - - - - - - Style: - - - - - - - - Light - - - - - Dark - - - - - Light Purple - - - - - Dark Purple - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - Default to last loaded file? - - - - - - - - - - - - - - - - buttonBox - accepted() - PreferencesDialog - emitData() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - PreferencesDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - - emitData() - - diff --git a/lib/ui/TabMaxSizeDialog.ui b/lib/ui/TabMaxSizeDialog.ui deleted file mode 100644 index 04dd0281..00000000 --- a/lib/ui/TabMaxSizeDialog.ui +++ /dev/null @@ -1,87 +0,0 @@ - - - TabMaxSizeDialog - - - - 0 - 0 - 184 - 107 - - - - Dialog - - - - - - Rows: - - - - - - - - - - Columns: - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - TabMaxSizeDialog - emitData() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - TabMaxSizeDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - - emitData() - - diff --git a/lib/ui/WidgetDialogGenerator.ui b/lib/ui/WidgetDialogGenerator.ui deleted file mode 100644 index 086f747c..00000000 --- a/lib/ui/WidgetDialogGenerator.ui +++ /dev/null @@ -1,129 +0,0 @@ - - - WidgetDialogGenerator - - - - 0 - 0 - 444 - 171 - - - - - 0 - 0 - - - - New Widget... - - - - - - - 0 - 1 - - - - Row: - - - - - - - - 0 - 1 - - - - Column: - - - - - - - - 0 - 1 - - - - Row Span: - - - - - - - - 0 - 1 - - - - Column Span: - - - - - - - - 0 - 1 - - - - Name: - - - - - - - - - - 1000 - - - - - - - 1000 - - - - - - - 1000 - - - 1 - - - - - - - 1000 - - - 1 - - - - - - - - diff --git a/main.cpp b/main.cpp index 281280eb..e714d031 100644 --- a/main.cpp +++ b/main.cpp @@ -1,210 +1,100 @@ -#include -#include -#include -#include -#include -#include - -#include "widgets/BooleanCheckboxWidget.h" -#include "widgets/BooleanDisplayWidget.h" -#include "widgets/CameraViewWidget.h" -#include "widgets/DoubleDialWidget.h" -#include "widgets/EnumWidget.h" -#include "widgets/DoubleDisplayWidget.h" -#include "widgets/StringChooserWidget.h" -#include "widgets/StringDisplayWidget.h" -#include "widgets/IntegerDisplayWidget.h" -#include "widgets/IntegerDialWidget.h" -#include "widgets/FieldWidget.h" -#include "widgets/SendableFieldWidget.h" -#include "widgets/CommandWidget.h" -#include "widgets/GraphWidget.h" -#include "widgets/FMSInfoWidget.h" -#include "widgets/SwerveWidget.h" - -#include "MainWindow.h" +#include +#include +#include +#include + +#include "BuildConfig.h" +#include "CameraListModel.h" +#include "Flags.h" #include "Globals.h" -#include "stores/FilterStore.h" -#include "stores/TypeStore.h" - -#include "Constants.h" - -#include -#include +#include "TitleManager.h" +#include "TopicListModel.h" +#include "TopicStore.h" -int main(int argc, char **argv) { - SingleApplication app(argc, argv); +#include - Q_INIT_RESOURCE(breeze); +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); app.setOrganizationName(BuildConfig.ORG_NAME); app.setApplicationName(BuildConfig.APP_NAME); app.setApplicationVersion(BuildConfig.versionString()); - QCommandLineParser parser; - parser.setApplicationDescription("Simple, reliable, high-performance, low-footprint FRC dashboard"); - parser.addHelpOption(); - parser.addVersionOption(); - parser.addPositionalArgument("file", "JSON file to open"); - - parser.process(app); - // i hate macos -#ifdef Q_OS_MACOS - app.setStyle(QStyleFactory::create("Fusion")); -#endif + TopicStore store(&app); + TopicListModel *topics = new TopicListModel(store, &app); + SettingsManager *settings = new SettingsManager(&app); + TabListModel *tlm = new TabListModel(settings, &app); + CameraListModel *clm = new CameraListModel(store, &app); + TitleManager *title = new TitleManager(&app); - MainWindow *window = new MainWindow(); - window->show(); - - Globals::inst.AddConnectionListener(true, [window] (const nt::Event &event) { + Globals::inst.AddConnectionListener(true, [topics, &store, clm, title] (const nt::Event &event) { bool connected = event.Is(nt::EventFlags::kConnected); - QMetaObject::invokeMethod(window, [window, connected] { - window->setWindowTitle( - QString("%1 %2 (%3) - %4") - .arg(BuildConfig.APP_NAME, - BuildConfig.versionString(), - QString::fromStdString(Globals::server.server), - QString(connected ? "" : "Not ") + "Connected") - ); - - window->setConnected(connected); - }); + store.connect(connected); + + if (!connected) { + QMetaObject::invokeMethod(topics, &TopicListModel::clear); + QMetaObject::invokeMethod(clm, &CameraListModel::clear); + title->resetTitle(); + } else { + title->setTitle("Connected (" + QString::fromStdString(event.GetConnectionInfo()->remote_ip) + ")"); + QMetaObject::invokeMethod(clm, [=] { + // kind of hacky, but what works, works + QTimer::singleShot(1000, [=] { + clm->clear(); + + for (const std::string &st : Globals::inst.GetTable("/CameraPublisher")->GetSubTables()) { + std::shared_ptr subtable = Globals::inst.GetTable("/CameraPublisher")->GetSubTable(st); + + clm->add(subtable); + } + }); + }); + } }); Globals::inst.StartClient4(BuildConfig.APP_NAME.toStdString()); Globals::inst.SetServer(Globals::server.server.c_str(), NT_DEFAULT_PORT4); Globals::inst.StartDSClient(); - // NT REGISTRATION - - FilterStore::registerNTType(nt::NetworkTableType::kBoolean, TopicTypes::Boolean, "Boolean"); - FilterStore::registerNTType(nt::NetworkTableType::kString, TopicTypes::String, "String"); - FilterStore::registerNTType(nt::NetworkTableType::kDouble, TopicTypes::Double, "Double"); - FilterStore::registerNTType(nt::NetworkTableType::kDoubleArray, TopicTypes::DoubleArray, "Double Array"); - FilterStore::registerNTType(nt::NetworkTableType::kStringArray, TopicTypes::StringArray, "String Array"); - FilterStore::registerNTType(nt::NetworkTableType::kInteger, TopicTypes::Int, "Integer"); - - // NT REGISTRATION - - // Funky - auto register_widget_types = []() { - auto register_widget = [&]() -> bool { - auto topic = WidgetType::TopicType; - auto widget = WidgetType::WidgetType; - auto sendable = WidgetType::SendableName; - auto name = WidgetType::DisplayName; - - if (widget != WidgetTypes::None) { - Globals::typeStore.registerType(topic, widget, name); - } - - if (!sendable.isEmpty()) { - FilterStore::registerSendable(sendable.toStdString(), topic); - } - - return false; - }; - (register_widget.template operator()() || ...); - }; - - register_widget_types.template operator()(); - - // settings dont exist or firstrun is true - bool firstRun = Settings::FirstRun.value().toBool(); - - if (firstRun) { - Settings::FirstRun.setValue(false); - - QMessageBox::information - (window, QString("Welcome to %1!").arg(BuildConfig.APP_NAME), - QString("Welcome to %1. Ensure to check the GitHub page in the \"about\" " - "tab.\n\n" - "To get started, open the NT server settings tab with Alt+S and input your desired " - "NetworkTables settings. Once connected, add a tab with Ctrl+T, and search " - "for your widget in the New Widget tab with Alt+N.\n\n" - - "Note that row and column indices start AT 0! Spans, however, do not.\n\n" - - "Save and load at any time with Ctrl+S and Ctrl+O, or through the File Menu. " - "Close tabs with Ctrl+W or through the Tab menu. Modify or delete widgets--" - "or even change their fonts--by right clicking any widget.") - .arg(BuildConfig.APP_NAME)); - } - - QString styleSheet = Settings::StyleSheet.value().toString(); - - setAppStyleSheet(styleSheet); - - window->refreshRecentFiles(); - - Globals::inst.AddListener({{""}}, nt::EventFlags::kTopic, [window] (const nt::Event &event) { + Globals::inst.AddListener({{""}}, nt::EventFlags::kTopic, [topics] (const nt::Event &event) { std::string topicName(event.GetTopicInfo()->name); if (event.Is(nt::EventFlags::kPublish)) { - Globals::ntTopics.append(QString::fromStdString(topicName)); - FilterStore::filterTopics(); + QMetaObject::invokeMethod(topics, [topics, topicName] { + topics->add(QString::fromStdString(topicName)); + }); + } else if (event.Is(nt::EventFlags::kUnpublish)) { - Globals::ntTopics.removeOne(QString::fromStdString(topicName)); - FilterStore::filterTopics(); + // TODO: handle unpublishing + // topics->remove(QString::fromStdString(topicName)); } }); - QObject::connect( - &app, - &SingleApplication::instanceStarted, - window, - &QMainWindow::raise + qmlRegisterUncreatableMetaObject( + QFDFlags::staticMetaObject, + "QFDFlags", + 1, 0, + "QFDFlags", + "Attempt to create uninstantiable object \"QFDFlags\" ignored" ); - const QStringList args = parser.positionalArguments(); - if (!args.isEmpty()) { - QFile file(args.at(0)); - window->open(file); - } else { - bool loadRecent = Settings::LoadRecent.value().toBool(); - if (loadRecent) { - QStringList recent = Settings::RecentFiles.value().toStringList(); - if (!recent.empty()) { - QFile file(recent.first()); - window->open(file); - } - } - } - - auto makeListener = [window]() -> auto { - std::string topic = Globals::server.switchTopic.toStdString(); - - return Globals::inst.AddListener(Globals::inst.GetEntry(topic), nt::EventFlags::kValueAll, [window](const nt::Event &event) { - std::string value = std::string{event.GetValueEventData()->value.GetString()}; - TabWidget *tab = window->tabNamed(QString::fromStdString(value)); - if (tab != nullptr) { - window->selectTab(tab); - } - }); - }; + QQmlApplicationEngine engine; - NT_Listener listener = makeListener(); - - QObject::connect(window, &MainWindow::switchTopicChanged, &app, [listener, makeListener]() mutable { - Globals::inst.RemoveListener(listener); - listener = makeListener(); - }); + engine.rootContext()->setContextProperty("topics", topics); + engine.rootContext()->setContextProperty("settings", settings); + engine.rootContext()->setContextProperty("cameras", clm); + engine.rootContext()->setContextProperty("topicStore", &store); + engine.rootContext()->setContextProperty("tlm", tlm); + engine.rootContext()->setContextProperty("titleManager", title); + QObject::connect( + &engine, + &QQmlApplicationEngine::objectCreationFailed, + &app, + []() { QCoreApplication::exit(-1); }, + Qt::QueuedConnection); + engine.loadFromModule("QFRCDashboard", "Main"); return app.exec(); } diff --git a/program_info/QFRCDashboard.desktop.in b/program_info/QFRCDashboard.desktop.in index eba2fb11..1c69d951 100644 --- a/program_info/QFRCDashboard.desktop.in +++ b/program_info/QFRCDashboard.desktop.in @@ -2,6 +2,6 @@ Type=Application Name=@Dashboard_APP_NAME@ Comment=Reliable, high-performance dashboard for FRC. -Exec=@Dashboard_APP_NAME@ +Exec=@Dashboard_EXEC_NAME@ Icon=@Dashboard_APP_NAME@ Categories=Education;Utility diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index 0963815d..11c77461 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -8,7 +8,7 @@ Name "@Dashboard_APP_NAME@" InstallDir "$LOCALAPPDATA\Programs\@Dashboard_APP_NAME@" InstallDirRegKey HKCU "Software\@Dashboard_APP_NAME@" "InstallDir" RequestExecutionLevel user -OutFile "../@Dashboard_APP_NAME@-Setup.exe" +OutFile "../@Dashboard_EXEC_NAME@-Setup.exe" !define MUI_ICON "../program_info/@Dashboard_APP_NAME@.ico" @@ -21,7 +21,7 @@ OutFile "../@Dashboard_APP_NAME@-Setup.exe" !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES -!define MUI_FINISHPAGE_RUN "$InstDir\@Dashboard_APP_NAME@.exe" +!define MUI_FINISHPAGE_RUN "$InstDir\@Dashboard_EXEC_NAME@.exe" !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM @@ -59,18 +59,18 @@ Section "@Dashboard_APP_NAME@" SectionIn RO - nsExec::Exec /TIMEOUT=2000 'TaskKill /IM @Dashboard_APP_NAME@.exe /F' + nsExec::Exec /TIMEOUT=2000 'TaskKill /IM @Dashboard_EXEC_NAME@.exe /F' SetOutPath $INSTDIR - File "@Dashboard_APP_NAME@.exe" + File "@Dashboard_EXEC_NAME@.exe" File *.dll File /r "iconengines" File /r "imageformats" - File /r "multimedia" File /r "platforms" - File /r "styles" File /r "tls" + File /r "multimedia" + File /r "qml" ; Write the installation path into the registry WriteRegStr HKCU Software\@Dashboard_APP_NAME@ "InstallDir" "$INSTDIR" @@ -81,7 +81,7 @@ Section "@Dashboard_APP_NAME@" ${If} ${Errors} !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\@Dashboard_APP_NAME@" WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "@Dashboard_APP_NAME@" - WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\@Dashboard_APP_NAME@.exe" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\@Dashboard_EXEC_NAME@.exe" WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S' WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR" @@ -102,13 +102,13 @@ SectionEnd Section "Start Menu Shortcut" SM_SHORTCUTS - CreateShortcut "$SMPROGRAMS\@Dashboard_APP_NAME@.lnk" "$INSTDIR\@Dashboard_APP_NAME@.exe" "" "$INSTDIR\@Dashboard_APP_NAME@.exe" 0 + CreateShortcut "$SMPROGRAMS\@Dashboard_EXEC_NAME@.lnk" "$INSTDIR\@Dashboard_EXEC_NAME@.exe" "" "$INSTDIR\@Dashboard_EXEC_NAME@.exe" 0 SectionEnd Section /o "Desktop Shortcut" DESKTOP_SHORTCUTS - CreateShortcut "$DESKTOP\@Dashboard_APP_NAME@.lnk" "$INSTDIR\@Dashboard_APP_NAME@.exe" "" "$INSTDIR\@Dashboard_APP_NAME@.exe" 0 + CreateShortcut "$DESKTOP\@Dashboard_EXEC_NAME@.lnk" "$INSTDIR\@Dashboard_EXEC_NAME@.exe" "" "$INSTDIR\@Dashboard_EXEC_NAME@.exe" 0 SectionEnd @@ -118,12 +118,12 @@ SectionEnd Section "Uninstall" - nsExec::Exec /TIMEOUT=2000 'TaskKill /IM @Dashboard_APP_NAME@.exe /F' + nsExec::Exec /TIMEOUT=2000 'TaskKill /IM @Dashboard_EXEC_NAME@.exe /F' DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\@Dashboard_APP_NAME@" DeleteRegKey HKCU SOFTWARE\@Dashboard_APP_NAME@ - Delete $INSTDIR\@Dashboard_APP_NAME@.exe + Delete $INSTDIR\@Dashboard_EXEC_NAME@.exe Delete $INSTDIR\qt.conf Delete $INSTDIR\*.dll @@ -134,8 +134,8 @@ Section "Uninstall" RMDir /r $INSTDIR\platforms RMDir /r $INSTDIR\styles - Delete "$SMPROGRAMS\@Dashboard_APP_NAME@.lnk" - Delete "$DESKTOP\@Dashboard_APP_NAME@.lnk" + Delete "$SMPROGRAMS\@Dashboard_EXEC_NAME@.lnk" + Delete "$DESKTOP\@Dashboard_EXEC_NAME@.lnk" RMDir "$INSTDIR" @@ -149,7 +149,7 @@ Function .onInit ${GetParameters} $R0 ${GetOptions} $R0 "/NoShortcuts" $R1 ${IfNot} ${Errors} -${OrIf} ${FileExists} "$InstDir\@Dashboard_APP_NAME@.exe" +${OrIf} ${FileExists} "$InstDir\@Dashboard_EXEC_NAME@.exe" !insertmacro UnselectSection ${SM_SHORTCUTS} !insertmacro UnselectSection ${DESKTOP_SHORTCUTS} ${EndIf} diff --git a/lib/src/Constants.cpp b/src/Constants.cpp similarity index 72% rename from lib/src/Constants.cpp rename to src/Constants.cpp index 6c71b533..5839a907 100644 --- a/lib/src/Constants.cpp +++ b/src/Constants.cpp @@ -1,7 +1,7 @@ #include "Constants.h" #include -#include +#include QVariant Setting::value() const { QSettings settings(qApp); @@ -13,7 +13,7 @@ void Setting::setValue(const QVariant &value) const { settings.setValue(Name, value); } -const Setting Settings::FirstRun{"firstRun", true}; const Setting Settings::RecentFiles{"recentFiles", QStringList{}}; const Setting Settings::LoadRecent{"loadRecent", false}; -const Setting Settings::StyleSheet{"styleSheet", ":/light/stylesheet.qss"}; +const Setting Settings::Theme{"theme", "dark"}; +const Setting Settings::Accent{"accent", "red"}; diff --git a/src/Globals.cpp b/src/Globals.cpp new file mode 100644 index 00000000..296bf545 --- /dev/null +++ b/src/Globals.cpp @@ -0,0 +1,6 @@ +#include "Globals.h" + +#include + +nt::NetworkTableInstance Globals::inst = nt::NetworkTableInstance::GetDefault(); +ServerData Globals::server{false, "0.0.0.0", NT_DEFAULT_PORT4}; diff --git a/src/SettingsManager.cpp b/src/SettingsManager.cpp new file mode 100644 index 00000000..bf60a0fe --- /dev/null +++ b/src/SettingsManager.cpp @@ -0,0 +1,151 @@ +#include "SettingsManager.h" +#include "Constants.h" +#include "Globals.h" + +SettingsManager::SettingsManager(QObject *parent) + : QObject{parent} +{} + +void SettingsManager::reconnectServer() { + std::string server = Globals::server.server; + bool isTeamNumber = Globals::server.teamNumber; + int port = Globals::server.port; + QString switchTopic = Globals::server.switchTopic; + + if (server.empty()) return; + + if (isTeamNumber) { + int team; + try { + team = std::stoi(server); + } catch (std::invalid_argument const &) { + return; + } + + Globals::inst.SetServerTeam(team, port); + } else { + Globals::inst.SetServer(server.c_str(), port); + } + + Globals::inst.Disconnect(); + + QString serverTopic = Globals::server.switchTopic; + + if (serverTopic != switchTopic) { + emit switchTopicChanged(); + } +} + +void SettingsManager::addRecentFile(QFile &file) { + QStringList recentFiles = Settings::RecentFiles.value().toStringList(); + + QString fileName = file.fileName(); + int index = recentFiles.indexOf(fileName); + + if (index != -1) { + recentFiles.move(index, 0); + } else { + recentFiles.prepend(fileName); + } + + if (recentFiles.length() > 5) { + recentFiles.removeLast(); + } + + Settings::RecentFiles.setValue(recentFiles); + + emit recentFilesChanged(); +} + +bool SettingsManager::loadRecent() const +{ + return Settings::LoadRecent.value().toBool(); +} + +void SettingsManager::setLoadRecent(bool newLoadRecent) +{ + Settings::LoadRecent.setValue(newLoadRecent); + emit loadRecentChanged(); +} + + +QStringList SettingsManager::recentFiles() const +{ + return Settings::RecentFiles.value().toStringList(); +} + +void SettingsManager::setRecentFiles(const QStringList &newRecentFiles) +{ + Settings::RecentFiles.setValue(newRecentFiles); + emit recentFilesChanged(); +} + +QString SettingsManager::theme() const +{ + return Settings::Theme.value().toString(); +} + +void SettingsManager::setTheme(const QString &newTheme) +{ + Settings::Theme.setValue(newTheme); + emit themeChanged(); +} + +QString SettingsManager::accent() const +{ + return Settings::Accent.value().toString(); +} + +void SettingsManager::setAccent(const QString &newAccent) +{ + Settings::Accent.setValue(newAccent); + emit accentChanged(); +} + +int SettingsManager::getPort() const +{ + return Globals::server.port; +} + +void SettingsManager::setPort(int newPort) +{ + Globals::server.port = newPort; + emit portChanged(); + reconnectServer(); +} + +QString SettingsManager::switchTopic() const +{ + return Globals::server.switchTopic; +} + +void SettingsManager::setSwitchTopic(const QString &newTopic) +{ + Globals::server.switchTopic = newTopic; + emit switchTopicChanged(); + reconnectServer(); +} + +QString SettingsManager::ip() const +{ + return QString::fromStdString(Globals::server.server); +} + +void SettingsManager::setIp(const QString &newIp) +{ + Globals::server.server = newIp.toStdString(); + emit ipChanged(); + reconnectServer(); +} + +bool SettingsManager::useTeam() const +{ + return Globals::server.teamNumber; +} + +void SettingsManager::setUseTeam(bool newUseTeam) +{ + Globals::server.teamNumber = newUseTeam; + emit useTeamChanged(); + reconnectServer(); +} diff --git a/src/TitleManager.cpp b/src/TitleManager.cpp new file mode 100644 index 00000000..61a6e77a --- /dev/null +++ b/src/TitleManager.cpp @@ -0,0 +1,21 @@ +#include "TitleManager.h" + +TitleManager::TitleManager(QObject *parent) + : QObject{parent} +{} + +QString TitleManager::title() const +{ + return m_title; +} + +void TitleManager::setTitle(const QString &newTitle) +{ + m_title = BuildConfig.APP_NAME + " - " + newTitle; + emit titleChanged(); +} + +void TitleManager::resetTitle() +{ + setTitle("Not Connected"); +} diff --git a/src/models/CameraListModel.cpp b/src/models/CameraListModel.cpp new file mode 100644 index 00000000..48a8b63a --- /dev/null +++ b/src/models/CameraListModel.cpp @@ -0,0 +1,186 @@ +#include "CameraListModel.h" +#include "Globals.h" + +#include +#include +#include + +CameraListModel::CameraListModel(TopicStore &store, QObject *parent) + : QAbstractListModel(parent) + , m_store(&store) +{} + +int CameraListModel::rowCount(const QModelIndex &parent) const +{ + return m_data.count(); +} + +QVariant CameraListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + Camera c = m_data[index.row()]; + + switch (role) { + case NAME: + return c.name; + case URLS: + return QVariant::fromValue(c.urls); + case SOURCE: + return c.source; + default: + break; + } + + return QVariant(); +} + +bool CameraListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + // if (data(index, role) != value) { + // Tab &t = m_data[index.row()]; + // switch (role) { + // case TITLE: + // t.title = value.toString(); + // case ROWS: + // t.rows = value.toInt(); + // case COLS: + // t.cols = value.toInt(); + // case WIDGETS: + // t.model = value.value(); + // default: + // break; + // } + // emit dataChanged(index, index, {role}); + // return true; + // } + return false; +} + +Qt::ItemFlags CameraListModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +void CameraListModel::add(std::shared_ptr table) +{ + Camera camera{}; + + camera.name = QString::fromStdString(table->GetEntry("description").GetString("")); + camera.source = QString::fromStdString(table->GetEntry("source").GetString("")); + + QString namePath = QString::fromStdString(std::string{table->GetPath()} + "/description"); + QString sourcePath = QString::fromStdString(std::string{table->GetPath()} + "/source"); + QString streamsPath = QString::fromStdString(std::string{table->GetPath()} + "/streams"); + + nt::NetworkTableEntry desc = Globals::inst.GetEntry(namePath.toStdString()); + nt::NetworkTableEntry src = Globals::inst.GetEntry(sourcePath.toStdString()); + nt::NetworkTableEntry str = Globals::inst.GetEntry(streamsPath.toStdString()); + + camera.name = QString::fromStdString(desc.GetString("invalid")); + camera.source = QString::fromStdString(src.GetString("invalid")); + + auto urls = TopicStore::toVariant(str.GetValue()).toList(); + + camera.urls = {}; + for (const QVariant &url : urls) { + static QRegularExpression re("^(mjpe?g|ip|usb):"); + QString newStream = url.toUrl().toString(); + newStream.replace(re, ""); + newStream.replace("/?action=stream", "/stream.mjpg?"); + + camera.urls.append(QUrl(newStream)); + } + + bool descriptionDone = (camera.name != "invalid"), + sourceDone = (camera.source != "invalid"), + streamsDone = (!camera.urls.empty()); + + if (sourceDone && descriptionDone && streamsDone) { + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << camera; + endInsertRows(); + } else { + m_store->subscribe(namePath); + m_store->subscribe(sourcePath); + m_store->subscribe(streamsPath); + + QMetaObject::Connection *conn = new QMetaObject::Connection; + + *conn = connect(m_store, &TopicStore::topicUpdate, this, [=, this](QString topic, QVariant value) mutable { + if (!descriptionDone && topic == namePath) { + descriptionDone = true; + + camera.name = value.toString(); + + if (camera.name.isEmpty()) { + std::string path{table->GetPath()}; + QString qpath = QString::fromStdString(path); + camera.name = qpath.split("/").last(); + } + } else if (!sourceDone && topic == sourcePath) { + sourceDone = true; + + camera.source = value.toString(); + } else if (!streamsDone && topic == streamsPath) { + streamsDone = true; + + QStringList streams = value.toStringList(); + + for (const QString &stream : streams) { + static QRegularExpression re("^(mjpe?g|ip|usb):"); + QString newStream = stream; + newStream.replace(re, ""); + newStream.replace("/?action=stream", "/stream.mjpg?"); + + camera.urls.append(QUrl(newStream)); + } + } + + if ((topic == streamsPath || topic == sourcePath || topic == namePath) + && sourceDone && descriptionDone && streamsDone) { + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << camera; + endInsertRows(); + + m_store->unsubscribe(namePath); + m_store->unsubscribe(sourcePath); + m_store->unsubscribe(streamsPath); + + disconnect(*conn); + delete conn; + } + }); + } +} + +void CameraListModel::clear() +{ + beginResetModel(); + m_data.clear(); + endResetModel(); +} + +bool CameraListModel::remove(int row, const QModelIndex &parent) +{ + beginRemoveRows(parent, row, row); + m_data.remove(row); + endRemoveRows(); + + return true; +} + +QHash CameraListModel::roleNames() const +{ + QHash rez; + rez[NAME] = "name"; + rez[URLS] = "urls"; + rez[SOURCE] = "source"; + + return rez; +} diff --git a/src/models/MapModel.cpp b/src/models/MapModel.cpp new file mode 100644 index 00000000..c502eb84 --- /dev/null +++ b/src/models/MapModel.cpp @@ -0,0 +1,148 @@ +#include "MapModel.h" + +MapModel::MapModel(QObject *parent) + : QAbstractTableModel(parent) +{} + +QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + switch (section) { + case 0: + return "Value"; + case 1: + return m_valueName; + default: + return ""; + } +} + +int MapModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_data.count(); +} + +int MapModel::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +QVariant MapModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + Data d = m_data[index.row()]; + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: + return d.key; + case 1: + return d.value; + default: + return QVariant(); + } + } + + switch (role) { + case KEY: + return d.key; + case VALUE: + return d.value; + default: + return QVariant(); + } +} + +bool MapModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + // if (data(index, role) != value) { + switch (role) { + case Qt::DisplayRole: + if (index.column() == 0) { + m_data[index.row()].key = value.toString(); + } else { + m_data[index.row()].value = value.toString(); + } + break; + case KEY: + m_data[index.row()].key = value.toString(); + break; + case VALUE: + m_data[index.row()].value = value.toString(); + break; + default: + break; + } + emit dataChanged(index, index, {role}); + return true; +// } +// return false; +} + +Qt::ItemFlags MapModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; // FIXME: Implement me! +} + +void MapModel::add(QString key, QString value) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + Data d{key, value}; + m_data << d; + endInsertRows(); +} + +void MapModel::remove(int row) +{ + if (row < 0 || row >= m_data.count()) return; + beginRemoveRows(QModelIndex(), row, row); + m_data.remove(row); + endRemoveRows(); +} + +QVariantList MapModel::asList() +{ + QVariantList list; + + for (const Data &d : m_data) { + QVariantMap map; + map.insert("Value", d.key); + map.insert(m_valueName, d.value); + list.append(map); + } + + return list; + +} + +QString MapModel::valueName() const +{ + return m_valueName; +} + +void MapModel::setValueName(const QString &newValueName) +{ + if (m_valueName == newValueName) + return; + m_valueName = newValueName; + emit valueNameChanged(); +} + +QHash MapModel::roleNames() const +{ + QHash rez; + rez[Qt::DisplayRole] = "display"; + rez[KEY] = "key"; + rez[VALUE] = "value"; + + return rez; +} diff --git a/src/models/TabListModel.cpp b/src/models/TabListModel.cpp new file mode 100644 index 00000000..9919fa6f --- /dev/null +++ b/src/models/TabListModel.cpp @@ -0,0 +1,210 @@ +#include "TabListModel.h" +#include "Globals.h" + +#include +#include +#include + +TabListModel::TabListModel(SettingsManager *settings, QObject *parent) + : QAbstractListModel(parent) + , m_settings(settings) +{} + +int TabListModel::rowCount(const QModelIndex &parent) const +{ + return m_data.count(); +} + +QVariant TabListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + Tab t = m_data[index.row()]; + + switch (role) { + case TITLE: + return t.title; + case ROWS: + return t.rows; + case COLS: + return t.cols; + case WIDGETS: + return QVariant::fromValue(t.model); + default: + break; + } + + return QVariant(); +} + +bool TabListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (data(index, role) != value) { + Tab &t = m_data[index.row()]; + switch (role) { + case TITLE: + t.title = value.toString(); + break; + case ROWS: + t.rows = value.toInt(); + break; + case COLS: + t.cols = value.toInt(); + break; + case WIDGETS: + t.model = value.value(); + break; + default: + break; + } + emit dataChanged(index, index, {role}); + return true; + } + return false; +} + +Qt::ItemFlags TabListModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +void TabListModel::add(Tab t) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << t; + endInsertRows(); +} + +void TabListModel::add(QString title) +{ + Tab t; + t.title = title; + t.rows = 3; + t.cols = 5; + + t.model = nullptr; + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << t; + endInsertRows(); +} + +bool TabListModel::remove(int row, const QModelIndex &parent) +{ + beginRemoveRows(parent, row, row); + m_data.remove(row); + endRemoveRows(); + + return true; +} + +void TabListModel::save(const QString &filename) +{ + QString name = filename; + name.replace("file://", ""); + QFile file(name); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCritical() << "Failed to open file" << name << "for writing."; + return; + } + + m_settings->addRecentFile(file); + + QTextStream stream(&file); + stream << saveObject().toJson(); + file.close(); +} + +QJsonDocument TabListModel::saveObject() const +{ + QJsonObject doc; + doc.insert("useTeamNumber", m_settings->useTeam()); + doc.insert("port", m_settings->getPort()); + doc.insert("ip", m_settings->ip()); + + QJsonArray arr; + + for (const Tab &t : m_data) { + QJsonObject obj; + + obj.insert("title", t.title); + obj.insert("rows", t.rows); + obj.insert("cols", t.cols); + obj.insert("widgets", t.model->saveObject()); + + arr.append(obj); + } + + doc.insert("tabs", arr); + + return QJsonDocument(doc); +} + +void TabListModel::loadObject(const QJsonDocument &doc) +{ + QJsonObject ob = doc.object(); + + Globals::server.server = ob.value("ip").toString().toStdString(); + Globals::server.port = ob.value("port").toInt(); + Globals::server.teamNumber = ob.value("useTeamNumber").toBool(); + + m_settings->reconnectServer(); + + QJsonArray arr = ob.value("tabs").toArray(); + + for (const QJsonValueRef ref : arr) { + QJsonObject obj = ref.toObject(); + + Tab t; + + t.title = obj.value("title").toString(); + t.rows = obj.value("rows").toInt(); + t.cols = obj.value("cols").toInt(); + t.model = TabWidgetsModel::loadObject(this, obj.value("widgets").toArray()); + t.model->setCols(t.cols); + t.model->setRows(t.rows); + + add(t); + } +} + +void TabListModel::load(const QString &filename) +{ + QString name = filename; + name.replace("file://", ""); + + QFile file(name); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + + m_settings->addRecentFile(file); + + QTextStream stream(&file); + QByteArray data = stream.readAll().toUtf8(); + + QJsonDocument doc = QJsonDocument::fromJson(data); + beginResetModel(); + m_data.clear(); + endResetModel(); + + loadObject(doc); + file.close(); +} + +QHash TabListModel::roleNames() const +{ + QHash rez; + rez[TITLE] = "title"; + rez[ROWS] = "rows"; + rez[COLS] = "cols"; + rez[WIDGETS] = "widgets"; + + return rez; +} diff --git a/src/models/TabWidgetsModel.cpp b/src/models/TabWidgetsModel.cpp new file mode 100644 index 00000000..55bfc838 --- /dev/null +++ b/src/models/TabWidgetsModel.cpp @@ -0,0 +1,365 @@ +#include "models/TabWidgetsModel.h" +#include +#include +#include +#include + +TabWidgetsModel::TabWidgetsModel(QObject *parent) + : QAbstractListModel(parent) +{} + +int TabWidgetsModel::rowCount(const QModelIndex &parent) const +{ + return m_data.count(); +} + +QVariant TabWidgetsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + Widget w = m_data[index.row()]; + + switch (role) { + case TITLE: + return w.title; + case TOPIC: + return w.topic; + case COL: + return w.col; + case ROW: + return w.row; + case COLSPAN: + return w.colSpan; + case ROWSPAN: + return w.rowSpan; + case TYPE: + return w.type; + case PROPERTIES: + return w.properties; + case IDX: + return index.row(); + default: + break; + } + + return QVariant(); +} + +bool TabWidgetsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (data(index, role) != value) { + Widget &w = m_data[index.row()]; + + switch (role) { + case TITLE: + w.title = value.toString(); + break; + case TYPE: + w.type = value.toString(); + break; + case TOPIC: + w.topic = value.toString(); + break; + case COL: + w.col = value.toInt(); + break; + case ROW: + w.row = value.toInt(); + break; + case COLSPAN: + w.colSpan = value.toInt(); + break; + case ROWSPAN: + w.rowSpan = value.toInt(); + break; + case PROPERTIES: + w.properties = value.toMap(); + break; + } + emit dataChanged(index, index, {role}); + return true; + } + return false; +} + +Qt::ItemFlags TabWidgetsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +Widget TabWidgetsModel::copy(int idx) +{ + Widget w = m_data.at(idx); + Widget nw = Widget(w); + nw.row = -1; + nw.col = -1; + nw.rowSpan = 1; + nw.colSpan = 1; + + return nw; +} + +void TabWidgetsModel::add(Widget w) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << w; + endInsertRows(); + + emit unoccupiedCellsChanged(); +} + +void TabWidgetsModel::add(QString title, QString topic, QString type) +{ + Widget w; + w.title = title; + w.topic = topic; + w.type = type; + w.row = -1; + w.col = -1; + + w.rowSpan = 1; + w.colSpan = 1; + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << w; + endInsertRows(); + + emit unoccupiedCellsChanged(); +} + +void TabWidgetsModel::addCamera(QString name, QString source, QVariantList urls) +{ + Widget w; + w.title = name; + w.type = "camera"; + + w.properties.insert("name", name); + w.properties.insert("source", source); + + QUrl url; + if (urls.empty()) { + url = source; + } else { + for (const QVariant &u : urls) { + // pure, unfaltered cancer + QString str = u.value().toString(); + + if (str.contains(".local")) { + continue; + } + + url = str; + } + + if (url.isEmpty()) { + url = urls.at(0).value().toString(); + } + } + + if (!url.isEmpty()) { + QStringList split = url.toString().split(":"); + w.properties.insert("host", (split.at(0) + ":" + split.at(1)));; + w.properties.insert("port", split.at(2).split("/").at(0).toInt()); + } + + w.row = -1; + w.col = -1; + + w.rowSpan = 1; + w.colSpan = 1; + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_data << w; + endInsertRows(); + + emit unoccupiedCellsChanged(); +} + +void TabWidgetsModel::setEqualTo(TabWidgetsModel *w) +{ + beginResetModel(); + m_data.clear(); + endResetModel(); + + beginInsertRows(QModelIndex(), 0, w->data().count() - 1); + m_data << w->data(); + endInsertRows(); + + m_rows = w->rows(); + m_cols = w->cols(); +} + +QList TabWidgetsModel::data() +{ + return m_data; +} + +bool TabWidgetsModel::remove(int idx) +{ + if (idx < 0 || idx >= m_data.count()) return false; + + beginRemoveRows(QModelIndex(), idx, idx); + m_data.remove(idx); + endRemoveRows(); + + return true; +} + +bool TabWidgetsModel::removeLatest() +{ + beginRemoveRows(QModelIndex(), rowCount() - 1, rowCount() - 1); + m_data.removeLast(); + endRemoveRows(); + + return true; +} + +int TabWidgetsModel::rows() const +{ + return m_rows; +} + +void TabWidgetsModel::setRows(int newRows) +{ + if (m_rows == newRows) + return; + m_rows = newRows; + emit rowsChanged(); +} + +int TabWidgetsModel::cols() const +{ + return m_cols; +} + +void TabWidgetsModel::setCols(int newCols) +{ + if (m_cols == newCols) + return; + m_cols = newCols; + emit colsChanged(); +} + +bool TabWidgetsModel::cellOccupied(int row, int col, int rowSpan, int colSpan, QRectF ignore) +{ + QRect itemRect = QRect(col, row, colSpan, rowSpan); + + if (col + colSpan > cols() || row + rowSpan > rows()) return true; + if (m_data.empty()) return false; + + for (const Widget &w : m_data) { + QRect dataRect = QRect(w.col, w.row, w.colSpan, w.rowSpan); + + if (dataRect.intersects(ignore.toRect())) { + continue; + } + + if (dataRect.intersects(itemRect)) { + return true; + } + } + + return false; +} + + +QHash TabWidgetsModel::roleNames() const +{ + QHash rez; + rez[TITLE] = "title"; + rez[TOPIC] = "topic"; + rez[TYPE] = "type"; + rez[COL] = "column"; + rez[ROW] = "row"; + rez[ROWSPAN] = "rowSpan"; + rez[COLSPAN] = "colSpan"; + rez[PROPERTIES] = "properties"; + rez[IDX] = "idx"; + + return rez; +} + +int TabWidgetsModel::unoccupiedCells() const +{ + int c = 0; + + for (const Widget &w : m_data) { + c += w.rowSpan * w.colSpan; + } + + return c; +} + +QJsonArray TabWidgetsModel::saveObject() const +{ + QJsonArray arr; + + for (const Widget w : m_data) { + QJsonObject obj; + obj.insert("title", w.title); + obj.insert("topic", w.topic); + obj.insert("type", w.type); + obj.insert("column", w.col); + obj.insert("row", w.row); + obj.insert("rowSpan", w.rowSpan); + obj.insert("colSpan", w.colSpan); + + QJsonObject prop; + + QMapIterator iter(w.properties); + + while (iter.hasNext()) { + iter.next(); + // I hate Qt + if (iter.value().metaType() == QMetaType::fromType()) { + prop.insert(iter.key(), iter.value().value().name()); + } else if (iter.value().metaType() == QMetaType::fromType()) { + QSize size = iter.value().value().toSize(); + prop.insert(iter.key(), QString::number(size.width()) + "x" + QString::number(size.height())); + } else { + prop.insert(iter.key(), iter.value().toJsonValue()); + } + } + + obj.insert("properties", prop); + + arr.append(obj); + } + + return arr; +} + +TabWidgetsModel *TabWidgetsModel::loadObject(QObject *parent, const QJsonArray &arr) +{ + TabWidgetsModel *model = new TabWidgetsModel(parent); + + for (const QJsonValueConstRef ref : arr) { + QJsonObject obj = ref.toObject(); + + Widget w; + w.title = obj.value("title").toString(""); + w.topic = obj.value("topic").toString(""); + w.type = obj.value("type").toString(""); + w.col = obj.value("column").toInt(0); + w.row = obj.value("row").toInt(0); + w.rowSpan = obj.value("rowSpan").toInt(0); + w.colSpan = obj.value("colSpan").toInt(0); + + QJsonObject properties = obj.value("properties").toObject(); + + QVariantMap props; + for (const QString &key : properties.keys()) { + props.insert(key, properties.value(key)); + } + + w.properties = props; + + model->add(w); + } + + return model; +} diff --git a/src/models/TopicListModel.cpp b/src/models/TopicListModel.cpp new file mode 100644 index 00000000..c7ea0c34 --- /dev/null +++ b/src/models/TopicListModel.cpp @@ -0,0 +1,158 @@ +#include "TopicListModel.h" + +TopicListModel::TopicListModel(TopicStore &store, QObject *parent) + : QStandardItemModel(parent) + , m_store(&store) +{ + QHash rez = QStandardItemModel::roleNames(); + rez.insert(TLMRoleTypes::NAME, "name"); + rez.insert(TLMRoleTypes::TYPE, "type"); + rez.insert(TLMRoleTypes::TOPIC, "topic"); + + QStandardItemModel::setItemRoleNames(rez); +} + +QVariant TopicListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == TLMRoleTypes::NAME) { + return itemFromIndex(index)->text(); + } + + return QStandardItemModel::data(index, role); +} + +void TopicListModel::reload() +{ + fetchMore(QModelIndex()); +} + +void TopicListModel::add(const QString &toAdd) +{ + if (toAdd.isEmpty() || toAdd == "/") return; + + QStringList split = toAdd.split('/'); + if (split.at(0).isEmpty()) split.remove(0); + + QStringList newList = split; + newList.removeLast(); + QString parentPath('/' + newList.join('/')); + + nt::NetworkTableEntry type = Globals::inst.GetEntry(parentPath.toStdString() + "/.type"); + + bool hasType = type.Exists(); + + QStandardItem *parentItem = invisibleRootItem(); + + for (const QString &sub : split) { + bool isLast = sub == split.last(); + + auto results = findItems(sub, Qt::MatchRecursive | Qt::MatchExactly | Qt::MatchWrap); + + if (results.isEmpty()) { + QStandardItem *item = new QStandardItem(sub); + bool append = false; + + if (isLast) { + if (hasType) { + if (sub == ".type") { + std::string value = type.GetString("invalid"); + + if (value == "invalid") { + QMetaObject::Connection *conn = new QMetaObject::Connection; + + m_store->subscribe(toAdd); + + *conn = connect(m_store, &TopicStore::topicUpdate, this, [conn, toAdd, parentItem, parentPath, this](QString topic, QVariant value) mutable { + if (topic == toAdd) { + parentItem->setData(parentPath, TOPIC); + QString typeStr = value.toString(); + parentItem->setData(typeStr, TYPE); + + m_store->unsubscribe(toAdd); + + disconnect(*conn); + delete conn; + } + }); + } else { + parentItem->setData(parentPath, TOPIC); + QString typeStr = QString::fromStdString(value); + parentItem->setData(typeStr, TYPE); + } + + append = false; + } else { + append = false; + } + } else { + append = true; + + item->setData(toAdd, TLMRoleTypes::TOPIC); + item->setData(m_store->typeString(toAdd), TYPE); + } + } else { + append = true; + item->setData("", TLMRoleTypes::TOPIC); + item->setData("", TYPE); + } + + if (append) { + parentItem->appendRow(item); + parentItem = item; + } else { + delete item; + } + } else { + for (QStandardItem *item : results) { + if (item->parent() != nullptr && item->parent()->data(TLMRoleTypes::TYPE).toString() != "") goto end; + + if (item->parent() == nullptr || item->parent()->text() == parentItem->text()) { + parentItem = item; + } + } + } + } + +end: + type.Unpublish(); + return; +} + +void TopicListModel::remove(const QString &toRemove) +{ + if (toRemove.isEmpty()) return; + + QStringList split = toRemove.split('/'); + if (split.at(0).isEmpty()) split.remove(0); + + QStandardItem *parentItem = invisibleRootItem(); + for (const QString &sub : split) { + auto results = findItems(sub, Qt::MatchRecursive | Qt::MatchExactly | Qt::MatchWrap); + + if (results.isEmpty()) return; + else { + for (QStandardItem *item : results) { + if (item == nullptr) continue; + + if (item->parent() == nullptr || item->parent() == parentItem) { + if (!item->hasChildren()) { + parentItem->removeRow(item->row()); + if (!parentItem->hasChildren()) { + if (parentItem->parent() == nullptr) { + removeRow(parentItem->row()); + } else { + parentItem->parent()->removeRow(parentItem->row()); + } + } + } + + parentItem = item; + } + } + } + } +} + diff --git a/src/stores/TopicStore.cpp b/src/stores/TopicStore.cpp new file mode 100644 index 00000000..59d2eeba --- /dev/null +++ b/src/stores/TopicStore.cpp @@ -0,0 +1,234 @@ +#include "stores/TopicStore.h" + +#include "Globals.h" + +#include + +TopicStore::TopicStore(QObject *parent) + : QObject(parent) +{ +} + +void TopicStore::connect(bool connected) +{ + emit this->connected(connected); +} + +bool TopicStore::hasEntry(QString topic) { + for (const Listener &listener : Listeners) { + if (topic == listener.topic) { + return true; + } + } + return false; +} + +Listener TopicStore::entry(QString topic) { + for (Listener listener : Listeners) { + if (listener.topic == topic) { + return listener; + } + } + + Listener l; + l.isNull = true; + return l; +} + +Listener TopicStore::changeNumSubscribed(QString topic, int changeBy) +{ + for (Listener listener : Listeners) { + if (listener.topic == topic) { + listener.numSubscribed += changeBy; + return listener; + } + } + + Listener l; + l.isNull = true; + return l; +} + +QVariant TopicStore::toVariant(const nt::Value &value) +{ + QVariant v; + + if (!value.IsValid()) return v; + + if (value.IsBoolean()) v = value.GetBoolean(); + else if (value.IsString()) v = QString::fromStdString(std::string(value.GetString())); + else if (value.IsDouble()) v = value.GetDouble(); + else if (value.IsFloat()) v = value.GetFloat(); + else if (value.IsInteger()) v = QVariant::fromValue(value.GetInteger()); + + else if (value.IsBooleanArray()) { + const std::span a = value.GetBooleanArray(); + QList newList; + for (const int i : a) { + newList << i; + } + v = QVariant::fromValue(newList); + } + else if (value.IsStringArray()) { + const std::span a = value.GetStringArray(); + QStringList newList; + for (const std::string &s : a) { + newList << QString::fromStdString(s); + } + v = QVariant::fromValue(newList); + } + else if (value.IsDoubleArray()) { + const std::span a = value.GetDoubleArray(); + QList newList; + for (const double d : a) { + newList << d; + } + v = QVariant::fromValue(newList); + } + else if (value.IsIntegerArray()) { + const std::span a = value.GetIntegerArray(); + QList newList; + for (const size_t i : a) { + newList << i; + } + v = QVariant::fromValue(newList); + } + + return v; +} + +nt::Value TopicStore::toValue(const QVariant &value) +{ + if (!value.isValid()) goto end; + + switch (value.typeId()) { + case QMetaType::Type::QString: + return nt::Value::MakeString(std::string_view{value.toString().toStdString()}); + case QMetaType::Type::QStringList: { + std::vector v; + for (const QString &s : value.toStringList()) { + v.emplace_back(s.toStdString()); + } + + return nt::Value::MakeStringArray(v); + } case QMetaType::Type::Bool: + return nt::Value::MakeBoolean(value.toBool()); + case QMetaType::Type::Double: + return nt::Value::MakeDouble(value.toDouble()); + case QMetaType::Type::Float: + return nt::Value::MakeFloat(value.toFloat()); + case QMetaType::Type::Int: + return nt::Value::MakeInteger(value.toInt()); + } + +end: + return nt::Value(); +} + +bool Listener::operator==(const Listener &other) const { + return (other.topic == this->topic) && + (other.isNull == this->isNull); +} + +void TopicStore::subscribe(QString ntTopic) { + Listener listener; + + listener = changeNumSubscribed(ntTopic); + if (listener.isNull) { + listener = { + ntTopic, + 0, + nt::ListenerCallback(), + 1, + false + }; + + nt::NetworkTableEntry entry = Globals::inst.GetEntry(ntTopic.toStdString()); + + topicEntryMap.insert(ntTopic, entry); + + nt::ListenerCallback updateWidget = [entry, ntTopic, this](const nt::Event &event = nt::Event()) { + nt::Value value; + if (!event.Is(nt::EventFlags::kValueAll)) { + value = entry.GetValue(); + } else { + value = event.GetValueEventData()->value; + } + + if (value.IsValid()) { + emit topicUpdate(ntTopic, toVariant(value)); + } + }; + + NT_Listener handle = Globals::inst.AddListener(entry, nt::EventFlags::kValueAll, updateWidget); + + listener.listenerHandle = handle; + listener.callback = updateWidget; + + Listeners.append(listener); + } else { + listener.callback(nt::Event()); + } +} + +void TopicStore::unsubscribe(QString ntTopic) { + if (!hasEntry(ntTopic)) return; + + Listener listener = changeNumSubscribed(ntTopic, -1); + + if (listener.numSubscribed <= 0) { + topicEntryMap.value(ntTopic).Unpublish(); + topicEntryMap.remove(ntTopic); + Listeners.removeAll(listener); + Globals::inst.RemoveListener(listener.listenerHandle); + } +} + +double TopicStore::getDoubleFromEntry(nt::NetworkTableEntry entry) { + nt::Value value = entry.GetValue(); + + if (value.IsBoolean()) { + return (double) entry.GetBoolean(0); + } else if (value.IsDouble()) { + return entry.GetDouble(0.); + } else if (value.IsInteger()) { + return (double) entry.GetInteger(0); + } + + return 0.; +} + +QVariant TopicStore::getValue(QString topic) { + Listener l = entry(topic); + if (l.isNull) return QVariant{}; + + nt::NetworkTableEntry entry = topicEntryMap.value(topic); + QVariant v = toVariant(entry.GetValue()); + return v; +} + +void TopicStore::setValue(QString topic, const QVariant &value) +{ + Listener l = entry(topic); + if (l.isNull) return; + + nt::NetworkTableEntry entry = topicEntryMap.value(topic); + entry.SetValue(toValue(value)); + +} + +QString TopicStore::typeString(QString topic) +{ + nt::NetworkTableEntry entry = Globals::inst.GetEntry(topic.toStdString()); + nt::NetworkTableType type = entry.GetType(); + + switch (type) { + case nt::NetworkTableType::kBoolean: return "bool"; + case nt::NetworkTableType::kDouble: return "double"; + case nt::NetworkTableType::kFloat: return "double"; + case nt::NetworkTableType::kString: return "string"; + case nt::NetworkTableType::kInteger: return "int"; + default: + return ""; + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 1f98df6a..00000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -enable_testing() - -project(Tests LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -macro(AddTest _name _src) - add_executable(${_name} ${_src}) - - target_link_libraries(${_name} PRIVATE QFRCDashboardLib Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::MultimediaWidgets Qt${QT_VERSION_MAJOR}::Charts ntcore BuildConfig Qt${QT_VERSION_MAJOR}::Test) - - target_include_directories(${_name} PRIVATE "$") - target_include_directories(${_name} PRIVATE ../lib/ui/) - target_include_directories(${_name} PRIVATE ../lib/include) - target_include_directories(${_name} PRIVATE ${_Dashboard_AUTOGEN}/include) - target_include_directories(${_name} PRIVATE ../lib/) - - add_test(NAME ${_name} COMMAND ${_name}) - - # include(GNUInstallDirs) - # install(TARGETS ${_name} - # BUNDLE DESTINATION . - # LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - # ) - - configure_file(test.json test.json COPYONLY) - -endmacro() - -AddTest(SaveLoad tst_saveload.cpp) -AddTest(WidgetDeletion tst_delete.cpp) -AddTest(WidgetUpdate tst_update.cpp) diff --git a/tests/test.json b/tests/test.json deleted file mode 100644 index 74122ea6..00000000 --- a/tests/test.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "server": { - "address": "0.0.0.0", - "port": 5810, - "topic": "", - "useTeamNumber": false - }, - "tabs": [ - { - "maxSize": [ - 5, - 5 - ], - "tabName": "Debug", - "widgets": [ - { - "Max_Y_Value": 10, - "Min_Y_Value": -10, - "Time_Scale_Seconds": 30, - "Topics": [ - ], - "Update_Frequency_Seconds": 1, - "X_Axis_Topic": { - "topic": "", - "useTime": true - }, - "geometry": [ - 0, - 0, - 2, - 1 - ], - "title": "Graph", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "widgetType": 13 - }, - { - "URL": "", - "geometry": [ - 0, - 1, - 1, - 1 - ], - "title": "Camera", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": "", - "widgetType": 6 - }, - { - "Location_Topic": "", - "States_Topic": "/death machine", - "geometry": [ - 1, - 1, - 1, - 1 - ], - "title": "Swerve", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "widgetType": 15 - }, - { - "Image": ":/2024Field.png", - "Robot_Length": 0.5, - "Robot_Width": 0.5, - "Topic": "/SmartDashboard/Field/Robot", - "geometry": [ - 0, - 3, - 1, - 1 - ], - "title": "Field", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": [ - 1, - 7, - 90 - ], - "widgetType": 11 - }, - { - "Topic": "/SmartDashboard/epic chooser", - "geometry": [ - 1, - 3, - 1, - 1 - ], - "title": "epic chooser", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": "", - "widgetType": 5 - }, - { - "Topic": "/FMSInfo", - "geometry": [ - 0, - 2, - 3, - 1 - ], - "title": "FMSInfo", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "widgetType": 14 - }, - { - "False_Color": "#ff0000", - "Shape": "Circle", - "Topic": "/epic/booleanMan", - "True_Color": "#00ffff", - "geometry": [ - 0, - 4, - 1, - 1 - ], - "title": "booleanMan", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": false, - "widgetType": 1 - }, - { - "Checkbox_Size": 30, - "Topic": "/epic/booleanMan", - "geometry": [ - 1, - 4, - 1, - 1 - ], - "title": "booleanMan", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": false, - "widgetType": 0 - }, - { - "Topic": "/epic/bruh/string", - "font": "Noto Sans,10,-1,5,400,0,0,0,0,0,0,0,0,0,0,1", - "geometry": [ - 2, - 0, - 1, - 1 - ], - "title": "string", - "titleFont": "Noto Sans Kannada,6,-1,5,400,0,0,0,0,0,0,0,0,0,0,1", - "value": "cool", - "widgetType": 2 - }, - { - "Colors": { - "cool": "#ff00ff", - "this is so epic": "#88FF22" - }, - "Shape": "Circle", - "Topic": "/epic/bruh/string", - "geometry": [ - 2, - 1, - 1, - 1 - ], - "title": "string", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": "cool", - "widgetType": 7 - }, - { - "Topic": "/Arm Motor Current", - "font": "Noto Sans,12,-1,5,400,0,0,0,0,0,0,0,0,0,0,1", - "geometry": [ - 2, - 3, - 1, - 1 - ], - "title": "Arm Motor Current", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": 58.177382755055774, - "widgetType": 3 - }, - { - "Maximum": 100, - "Minimum": 0, - "Starting_Angle": 90, - "Topic": "/Arm Motor Current", - "font": "Noto Sans,12,-1,5,400,0,0,0,0,0,0,0,0,0,0,1", - "geometry": [ - 2, - 4, - 1, - 1 - ], - "title": "Arm Motor Current", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": 58.177382755055774, - "widgetType": 4 - }, - { - "Topic": "/Arm Position", - "font": "Noto Sans,12,-1,5,400,0,0,0,0,0,0,0,0,0,0,1", - "geometry": [ - 3, - 0, - 1, - 1 - ], - "title": "Arm Position", - "titleFont": "Noto Sans,26,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": 547, - "widgetType": 8 - }, - { - "Maximum": 1000, - "Minimum": 0, - "Starting_Angle": 120, - "Topic": "/Arm Position", - "font": "Noto Sans,72,-1,5,400,0,0,0,0,0,0,0,0,0,0,1", - "geometry": [ - 3, - 1, - 1, - 1 - ], - "title": "Arm Position", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": 547, - "widgetType": 9 - }, - { - "Image": ":/2024Field.png", - "Robot_Length": 0.5, - "Robot_Width": 0.5, - "Topic": "/death machine", - "geometry": [ - 3, - 2, - 1, - 1 - ], - "title": "death machine", - "titleFont": "Noto Sans,12,-1,5,700,0,0,0,0,0,0,0,0,0,0,1", - "value": [ - 1, - -90, - -1, - 0, - 1, - 0, - -1, - -90 - ], - "widgetType": 11 - } - ] - } - ] -} diff --git a/tests/tst_delete.cpp b/tests/tst_delete.cpp deleted file mode 100644 index 52b4d55e..00000000 --- a/tests/tst_delete.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include "MainWindow.h" -#include "widgets/BaseWidget.h" -#include "networktables/NetworkTableInstance.h" - -// Test to ensure deletion is safe & doesn't cause segfaults -class WidgetDeletion : public QObject -{ - Q_OBJECT - -public: - WidgetDeletion(); - ~WidgetDeletion(); - -private: - MainWindow *m_window; - nt::NetworkTableInstance m_inst; - nt::NetworkTableEntry m_testEntry; - -private slots: - void initTestCase(); - void cleanupTestCase(); - void testDelete(); -}; - -WidgetDeletion::WidgetDeletion() { - m_window = new MainWindow; -} - -WidgetDeletion::~WidgetDeletion() {} - -void WidgetDeletion::initTestCase() { - QFile file("./test.json"); - file.open(QIODevice::ReadOnly | QIODevice::Text); - auto doc = QJsonDocument::fromJson(file.readAll()); - m_window->loadObject(doc); - - m_inst = nt::NetworkTableInstance::GetDefault(); - m_inst.StartServer("", "0.0.0.0"); - - m_testEntry = m_inst.GetEntry("/test"); - m_testEntry.SetBoolean(false); - - m_window->setNtSettings(ServerData{false, "0.0.0.0", NT_DEFAULT_PORT4}); - QTest::qWait(1000); -} - -void WidgetDeletion::cleanupTestCase() { - delete m_window; - m_testEntry.Unpublish(); - m_inst.Disconnect(); -} - -void WidgetDeletion::testDelete() { - TabWidget *tab = m_window->currentTab(); - BaseWidget *widget = tab->widgets().at(0); - tab->deleteWidget(widget); - m_testEntry.SetBoolean(true); - - QTest::qWait(1000); - Q_ASSERT(true); -} - -QTEST_MAIN(WidgetDeletion) - -#include "tst_delete.moc" diff --git a/tests/tst_saveload.cpp b/tests/tst_saveload.cpp deleted file mode 100644 index 8ee58aa1..00000000 --- a/tests/tst_saveload.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include "MainWindow.h" - -// add necessary includes here - -class SaveLoad : public QObject -{ - Q_OBJECT - -public: - SaveLoad(); - ~SaveLoad(); - -private: - MainWindow *m_window; - QJsonDocument m_doc; - -private slots: - void initTestCase(); - void cleanupTestCase(); - void testSave(); -}; - -SaveLoad::SaveLoad() { - m_window = new MainWindow; -} - -SaveLoad::~SaveLoad() {} - -void SaveLoad::initTestCase() { - QFile file("./test.json"); - file.open(QIODevice::ReadOnly | QIODevice::Text); - m_doc = QJsonDocument::fromJson(file.readAll()); - m_window->loadObject(m_doc); -} - -void SaveLoad::cleanupTestCase() { - delete m_window; -} - -void SaveLoad::testSave() { - QJsonDocument doc = m_window->saveObject(); - QCOMPARE(doc, m_doc); -} - -QTEST_MAIN(SaveLoad) - -#include "tst_saveload.moc" diff --git a/tests/tst_update.cpp b/tests/tst_update.cpp deleted file mode 100644 index ab9e92db..00000000 --- a/tests/tst_update.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include "MainWindow.h" -#include "widgets/BaseWidget.h" -#include "networktables/NetworkTableInstance.h" - -// Test to ensure deletion is safe & doesn't cause segfaults -class WidgetUpdate : public QObject -{ - Q_OBJECT - -public: - WidgetUpdate(); - ~WidgetUpdate(); - -private: - MainWindow *m_window; - nt::NetworkTableInstance m_inst; - nt::NetworkTableEntry m_testEntry; - -private slots: - void initTestCase(); - void cleanupTestCase(); - void testUpdate(); -}; - -WidgetUpdate::WidgetUpdate() { - m_window = new MainWindow; -} - -WidgetUpdate::~WidgetUpdate() {} - -void WidgetUpdate::initTestCase() { - QFile file("./test.json"); - file.open(QIODevice::ReadOnly | QIODevice::Text); - auto doc = QJsonDocument::fromJson(file.readAll()); - m_window->loadObject(doc); - - m_inst = nt::NetworkTableInstance::GetDefault(); - m_inst.StartServer("", "0.0.0.0"); - - m_testEntry = m_inst.GetEntry("/epic/booleanMan"); - m_testEntry.SetBoolean(false); - - m_window->setNtSettings(ServerData{false, "0.0.0.0", NT_DEFAULT_PORT4}); - QTest::qWait(1000); -} - -void WidgetUpdate::cleanupTestCase() { - delete m_window; - m_testEntry.Unpublish(); - m_inst.Disconnect(); -} - -void WidgetUpdate::testUpdate() { - TabWidget *tab = m_window->currentTab(); - BaseWidget *widget = tab->widgets().at(7); - m_testEntry.SetBoolean(true); - - QTest::qWait(2000); - - QCOMPARE(true, widget->property("value").toBool()); -} - -QTEST_MAIN(WidgetUpdate) - -#include "tst_update.moc" diff --git a/widgets/BaseWidget.qml b/widgets/BaseWidget.qml new file mode 100644 index 00000000..33de799b --- /dev/null +++ b/widgets/BaseWidget.qml @@ -0,0 +1,304 @@ +import QtQuick 6.7 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +import QFRCDashboard 1.0 +import QFDFlags 1.0 + +Rectangle { + id: rect + width: 100 + height: 100 + z: 2 + + radius: 12 + + property int item_titleFontSize: 20 + + property alias titleField: titleField + property alias rcMenu: rcMenu + property alias dragArea: dragArea + + color: Constants.palette.widgetBg + + Drag.active: dragArea.drag.active + + signal resizeBegin(var drag) + signal resizeEnd(var drag) + signal dragEnd(var drag) + signal cancelDrag + + onResizeBegin: drag => { + drag.source = this + rep.beginResize(drag) + } + + onResizeEnd: drag => { + drag.source = this + rep.endResize(drag) + } + + onDragEnd: drag => { + let source = Qt.rect(drag.source.mcolumn, drag.source.mrow, + drag.source.mcolumnSpan, + drag.source.mrowSpan) + rep.intersectionCheck(drag, source) + } + + onCancelDrag: rep.fullCancelDrag() + + property point beginDrag + property rect beginResize + + property bool caught: false + + property int mrow + property int mcolumn + property int mrowSpan + property int mcolumnSpan + + onMrowChanged: model.row = mrow + onMcolumnChanged: model.column = mcolumn + onMrowSpanChanged: model.rowSpan = mrowSpan + onMcolumnSpanChanged: model.colSpan = mcolumnSpan + + function remove() { + twm.remove(model.row, model.column) + } + + Component.onCompleted: { + tab.latestWidget = this + + mrow = model.row + mcolumn = model.column + mrowSpan = model.rowSpan + mcolumnSpan = model.colSpan + + for (var p in this) { + if (p.startsWith("item_") && typeof this[p] !== "function") { + let propName = p + let substr = propName.substring(5) + let prop = model.properties[substr] + this[p] = typeof prop === "undefined" ? this[propName] : prop + + if (substr === "topic") { + this.item_topicChanged.connect(() => { + model.topic = this.item_topic + }) + } else { + this[p + "Changed"].connect(() => { + let x = model.properties + x[substr] = this[propName] + model.properties = x + }) + } + } + } + } + + Menu { + id: rcMenu + + MenuItem { + text: "Delete" + onTriggered: twm.remove(model.idx) + } + + MenuItem { + text: "Configure" + onTriggered: openConf(rcMenu.parent) + } + + MenuItem { + text: "Copy" + onTriggered: copy(idx) + } + } + + MouseArea { + id: dragArea + z: 0 + + acceptedButtons: Qt.AllButtons + + anchors { + fill: parent + + margins: 6 + } + + drag.target: parent + + pressAndHoldInterval: 100 + + onPressed: mouse => { + if (mouse.button === Qt.RightButton) { + drag.target = null + rcMenu.popup() + } else if (mouse.button === Qt.LeftButton) { + drag.target = parent + + rect.beginDrag = Qt.point(parent.x, parent.y) + rect.Drag.hotSpot = Qt.point(mouse.x, mouse.y) + parent.z = 3 + + resizeBegin(rect.Drag) + } + } + + onReleased: { + if (drag.target === null) { + Layout.rowSpan = grid.rows + 1 + Layout.columnSpan = grid.columns + 1 + Layout.row = grid.rows + 1 + Layout.column = grid.columns + 1 + update() + + Layout.rowSpan = mrowSpan + Layout.columnSpan = mcolumnSpan + Layout.column = mcolumn + Layout.row = mrow + } else if (!rect.caught) { + backAnimX.from = rect.x + backAnimX.to = beginDrag.x + backAnimY.from = rect.y + backAnimY.to = beginDrag.y + backAnim.start() + + cancelDrag() + } else { + rect.Drag.drop() + + // dragEnd(rect.Drag) + parent.z = 2 + cancelDrag() + } + } + + ParallelAnimation { + id: backAnim + SmoothedAnimation { + id: backAnimX + target: rect + property: "x" + duration: 250 + } + SmoothedAnimation { + id: backAnimY + target: rect + property: "y" + duration: 250 + } + + onFinished: { + x = rect.beginDrag.x + y = rect.beginDrag.y + } + } + } + + ParallelAnimation { + id: resizeBackAnim + SmoothedAnimation { + id: resizeBackAnimX + target: rect + property: "x" + duration: 250 + } + SmoothedAnimation { + id: resizeBackAnimY + target: rect + property: "y" + duration: 250 + } + SmoothedAnimation { + id: resizeBackAnimWidth + target: rect + property: "width" + duration: 250 + } + SmoothedAnimation { + id: resizeBackAnimHeight + target: rect + property: "height" + duration: 250 + } + + onFinished: { + width = rect.beginResize.width + height = rect.beginResize.height + x = rect.beginResize.x + y = rect.beginResize.y + } + } + + function resetResize(mouse) { + rect.beginDrag = Qt.point(rect.x, rect.y) + rect.beginResize = Qt.rect(rect.x, rect.y, rect.width, rect.height) + rect.Drag.hotSpot = Qt.point(mouse.x, mouse.y) + rect.z = 3 + + resizeBegin(rect.Drag) + } + + function releaseResize() { + if (!rect.caught) { + resizeBackAnimX.from = rect.x + resizeBackAnimX.to = beginResize.x + resizeBackAnimY.from = rect.y + resizeBackAnimY.to = beginResize.y + resizeBackAnimWidth.from = rect.width + resizeBackAnimWidth.to = beginResize.width + resizeBackAnimHeight.from = rect.height + resizeBackAnimHeight.to = beginResize.height + resizeBackAnim.start() + + cancelDrag() + } else { + rect.Drag.drop() + + resizeEnd(rect.Drag) + + rect.z = 2 + } + } + + /* RESIZE ANCHORS */ + Repeater { + model: [QFDFlags.RIGHT, QFDFlags.LEFT, QFDFlags.TOP, QFDFlags.BOTTOM, QFDFlags.RIGHT + | QFDFlags.TOP, QFDFlags.RIGHT | QFDFlags.BOTTOM, QFDFlags.LEFT + | QFDFlags.TOP, QFDFlags.LEFT | QFDFlags.BOTTOM] + + ResizeAnchor { + required property int modelData + direction: modelData + } + } + + /* ACTUAL DATA */ + TextField { + id: titleField + font.pixelSize: item_titleFontSize + font.bold: true + + text: model.title + color: "#DDDDDD" + + onTextEdited: model.title = text + + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + background: Rectangle { + topLeftRadius: 12 + topRightRadius: 12 + color: Constants.accent + } + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } +} diff --git a/widgets/DoubleSpinBox.qml b/widgets/DoubleSpinBox.qml new file mode 100644 index 00000000..b7d32ebe --- /dev/null +++ b/widgets/DoubleSpinBox.qml @@ -0,0 +1,458 @@ +/* + MLDoubleSpinBox + https://github.com/mpaperno/maxLibQt + + COPYRIGHT: (c)2018 Maxim Paperno; All Right Reserved. + Contact: http://www.WorldDesign.com/contact + + LICENSE: + + Commercial License Usage + Licensees holding valid commercial licenses may use this file in + accordance with the terms contained in a written agreement between + you and the copyright holder. + + GNU General Public License Usage + Alternatively, this file may be used under the terms of the GNU + General Public License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + A copy of the GNU General Public License is available at . +*/ + +import QtQuick +import QtQuick.Controls + +/*! + \brief MLDoubleSpinBox is a drop-in replacement for QtQuick Controls 2 SpinBox which can handle double-precision numbers and long integers. + + Supports SpinBox API of Controls v2.4 (Qt 5.11) but can be used with any version of QtQuick Controls 2 (Qt v5.2+). + + The possible value range is defined by ECMAScript \e [Number] limits: +/- \c Number.MAX_VALUE for reals and between + \c Number.MIN_SAFE_INTEGER and \c Number.MAX_SAFE_INTEGER for integers. This includes being able to handle unsigned 32b ints, + something the base \e SpinBox can't do. + + It has mostly the same properties, methods, and signal as \e SpinBox, except the \p value/from/to/stepSize properties are doubles. + The only exception are the \c up and \c down group, which could be accessed directly via #spinBoxItem. + It also has a number of other custom properties not found in the default SpinBox (see inline documentation below). + + Use the #decimals property to control precision (default is 2). + + In addition to the regular \e SpinBox controls (arrow keys/wheel-scroll), it reacts to Page Up/Down keys and CTRL-scroll for page-sized steps (see #pageSteps property). + + Individual property documentation can be found inline. + + \note About the #prefix and #suffix properties: + \note + This SpinBox does add \p prefix and \p suffix properties, but it is not in an ideal way. Specifically, in an + editable spin box, the cursor is not placed in the correct position for editing the actual value (user can start typing + before/after/in middle of prefix/suffix). It does still work and will auto-correct properly after editing is + finished, but it is a bit awkward at best. They do work nicely in a non-editable spin box. + + \note + To achieve proper prefix/suffix support, the best way would be to use a custom QValidator, just like the Controls 1 + SpinBox does (or did... RIP). (search for QQuickSpinBoxValidator1) The other way is to use an input mask. + Unfortunately Qt support for input masks is rather limited, and as a result one has to use a very specific mask (with + the correct number of digits, notation type, localized, etc.). The number has to be typed/formatted exactly to the + mask. This is not very user-friendly either, except in specific circumstances. Also the DoubleValidator does not work with + input masks, so we need a custom validator in any case (see \e regExpValidator below). + + \note + The accompanying tests.qml file in this folder has an example of a customized spin box using an input mask with prefix + and suffix. It is pretty generalized, but for standard notation only. + + [Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number + +*/ + +Control { + id: control + objectName: "MLDoubleSpinBox" + + // Standard SpinBox API properties (v2.4) + property double value: 0.0 + property double from: 0.0 + property double to: 100.0 + property double stepSize: 1.0 + property bool editable: true + property bool wrap: true + property QtObject validator: doubleValidator //!< There are 2 validators available, see notes for each below. + property int inputMethodHints: Qt.ImhFormattedNumbersOnly + readonly property string displayText: textFromValue(value, effectiveLocale) + readonly property bool inputMethodComposing: textInputItem ? textInputItem.inputMethodComposing : false + + // Custom properties + property int decimals: 2 //!< Desired precision + property int notation: DoubleValidator.StandardNotation //!< For validator and text formatting + property string inputMask //!< Input mask for the text edit control (\sa TextInput::inputMask). + property bool selectByMouse: true //!< Whether to allow selection of text (bound to the text editor of the spinbox control). + property bool useLocaleFormat: true //!< Whether to format numbers according to the current locale. If false, use standard "C" format. + property bool showGroupSeparator: true //!< Whether to format numbers with the thousands separator visible (using current locale if useLocaleFormat is true). + property bool trimExtraZeros: true //!< Whether to remove trailing zeros from decimals. + property string prefix //!< Optional string to display before the value. See notes in the main comments above. + property string suffix //!< Optional string to display after the value. See notes in the main comments above. + property int pageSteps: 10 //!< How many steps in a "page" step (PAGE UP/DOWN keys or CTRL-Wheel). + property int buttonRepeatDelay: 300 //!< Milliseconds to delay before held +/- button repeat is activated. + property int buttonRepeatInterval: 100 //!< +/- button repeat interval while held (in milliseconds). + + readonly property string cleanText: getCleanText(displayText) //!< Holds the text of the spin box excluding any prefix, suffix, or leading or trailing whitespace. + readonly property bool acceptableInput: textInputItem && textInputItem.acceptableInput //!< Indicates if input is valid (it would be nicer if the validator would expose an "isValid" prop/method!). + readonly property real topValue: Math.max(from, to) //!< The effective maximum value + readonly property real botValue: Math.min(from, to) //!< The effective minimum value + + //! The SpinBox item. To use a custom one, replace the \p contentItem with a class derived from Controls 2.x SpinBox. + //! Or use any other \p contentItem (or even \e null) and (optionally) set the #textInputItem to some \e Item with a \c text property for a custom display. + readonly property SpinBox spinBoxItem: contentItem + //! Use the "native" text editor of the SpinBox to preserve look/feel. If you use a custom SpinBox, you may need to set this property also. If defined, it must have a \e text property. + property Item textInputItem: spinBoxItem ? spinBoxItem.contentItem : null + + //! Default numeric validator, strictly enforces \p notation type, does not allow for custom \p inputMask with non-numeric components. + readonly property QtObject doubleValidator: DoubleValidator { + top: control.topValue + bottom: control.botValue + decimals: Math.max(control.decimals, 0) + notation: control.notation + locale: control.effectiveLocale.name + } + + /*! This is an experimental validator using a RegExp instead of numerical validation (which we do anyway). + The advantage of using it is that: + \li It allows for scientific notation entry even if std. notation is specified (and vice versa) and will then re-format the entry as necessary after editing is finished. + \li Could also be used with a custom input mask, as demonstrated in tests.qml + To use it, just set \c validator: regExpValidator */ + readonly property QtObject regExpValidator: RegularExpressionValidator { regularExpression: control.doubleValidationRegEx(); } + + // signals + + signal valueModified() //!< Mimic SpinBox API (interactive change only, NOT emitted if \e value property is set directly). + + // QtQuick Control properties + + // By default wheel is enabled only if editor has active focus or item is not editable. + wheelEnabled: !editable || (textInputItem && textInputItem.activeFocus) + + // The spin box itself... it's really only here for its buttons and overall formatting, we ignore its actual value/etc. + contentItem: SpinBox { + width: control.availableWidth + height: control.availableHeight + editable: control.editable + inputMethodHints: control.inputMethodHints + validator: control.validator + from: -0x7FFFFFFF; to: 0x7FFFFFFF; // prevent interference with our real from/to values + // wrap peroperty is set below as a Binding in case SpinBox vesion is < 2.3 (Qt 5.10). + } + + // Public function API + + //! Increment value by one #stepSize + function increase() { + stepBy(1); + } + + //! Decrement value by one #stepSize + function decrease() { + stepBy(-1); + } + + /*! Adjust value by number of \p steps. (Each step size is determined by the spin box #stepSize property.) + \param type:int steps Number of steps to adjust by. Can be negative to decrement the value. + \param type:bool noWrap (optional) If true will prevent wrapping even if the spin box #wrap property is true. Default is false. + */ + function stepBy(steps, noWrap) { + // always use current editor value in case user has changed it w/out losing focus + setValue(textValue() + (stepSize * steps), noWrap); + } + + /*! Set the spin box value to \p newValue. This is generally preferable to setting the #value spin box property directly, but not required. + \param type:real newValue The value to set. + \param type:bool noWrap (optional) If true will prevent wrapping even if the spin box #wrap property is true. Default is false. + \param type:bool notModified (optional) If true will prevent the \e valueModified() signal from being emitted. Default is false. + \return type:bool True if value was updated (that is, it did not equal the old value), false otherwise. + */ + function setValue(newValue, noWrap, notModified) + { + if (!wrap || noWrap) + newValue = Math.max(Math.min(newValue, control.topValue), control.botValue); + else if (newValue < control.botValue) + newValue = control.topValue; + else if (newValue > control.topValue) + newValue = control.botValue; + + newValue = Number(newValue.toFixed(Math.max(decimals, 0))); // round + + if (value !== newValue) { + isValidated = true; + value = newValue; + isValidated = false; + if (!notModified) + valueModified(); + if (spinBoxItem) + spinBoxItem.value = 0; // reset this to prevent it from disabling the buttons or other weirdness + //console.log("setValue:", newValue.toFixed(control.decimals)); + return true; + } + return false; + } + + //! Reimplimented from SpinBox + function textFromValue(value, locale) + { + if (!locale) + locale = effectiveLocale; + + var text = value.toLocaleString(locale, (notation === DoubleValidator.StandardNotation ? 'f' : 'e'), Math.max(decimals, 0)); + + if (!showGroupSeparator && locale.name !== "C") + text = text.replace(new RegExp("\\" + locale.groupSeparator, "g"), ""); + if (trimExtraZeros) { + var pt = locale.decimalPoint; + var ex = new RegExp("\\" + pt + "0*$|(\\" + pt + "\\d*[1-9])(0+)$").exec(text); + if (ex) + text = text.replace(ex[0], ex[1] || ""); + } + + if (prefix) + text = prefix + text; + if (suffix) + text = text + suffix; + + return text; + } + + //! Reimplimented from SpinBox + function valueFromText(text, locale) + { + if (!locale) + locale = effectiveLocale; + // strip prefix/suffix, or custom pre-processor + text = getCleanText(text, locale); + // We need to clean the string before using Number::fromLocaleString because it throws errors when the input format isn't valid, eg. thousands separator in the wrong place. D'oh. + var re = "[^\\+\\-\\d\\" + locale.decimalPoint + locale.exponential + "]+"; + text = text.replace(new RegExp(re, "gi"), ""); + if (!text.length) + text = "0"; + //console.log("valueFromText:", text, locale.name, Number.fromLocaleString(locale, text)); + return Number.fromLocaleString(locale, text); + } + + /*! Return \p text stripped of any \e prefix or \e suffix and trimmed. Same as \e cleanText property. + Called by #valueFromText() before other cleanup operations. Could be reimplemented for custom replacements. */ + function getCleanText(text, locale) + { + text = String(text); + if (prefix) + text = text.replace(prefixRegEx, ""); + if (suffix) + text = text.replace(suffixRegEx, ""); + return text.trim(); + } + + //! Make \p string safe for use in RegExp as a literal. + function escapeRegExpChars(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + + //! Make \p string safe for use in Qt input mask as a literal. \sa QLineEdit::inputMask + function escapeInputMaskChars(string) { + return string.replace(/[{}\[\]\\> botValue); + } + } + + onValueChanged: { + if (!completed) + return; + if (!isValidated) + setValue(value, true, true); + updateUi(); + } + + // We need to override spin box arrow key events to distinguish from +/- button presses, otherwise we get double repeats. + onSpinBoxItemChanged: { + if (spinBoxItem) + spinBoxItem.Keys.forwardTo = [control]; + } + + Component.onCompleted: { + completed = true; + // An initial value may have been set, but not validated. Do that now. + if (!setValue(value, true, true)) + updateUi(); // in case it hasn't changed + } + + onWrapChanged: updateUi() + onNotationChanged: updateUi() + onTrimExtraZerosChanged: updateUi() + onShowGroupSeparatorChanged: updateUi() + onEffectiveLocaleChanged: updateUi() + Keys.onPressed: handleKeyEvent(event) + + Connections { + target: control.spinBoxItem ? control.spinBoxItem.up : null + onPressedChanged: control.toggleButtonPress(control.spinBoxItem.up.pressed, true) + } + + Connections { + target: control.spinBoxItem ? control.spinBoxItem.down : null + onPressedChanged: control.toggleButtonPress(control.spinBoxItem.down.pressed, false) + } + + Connections { + target: control.textInputItem + // Checking active focus works better than onEditingFinished because the latter doesn't fire if input is invalid (nor does it fix it up automatically). + onActiveFocusChanged: { + if (!control.textInputItem.activeFocus) + control.updateValueFromText(); + } + } + + // We use a binding here just in case the resident SpinBox is older than v2.3 + Binding { + target: control.spinBoxItem + when: control.spinBoxItem && typeof control.spinBoxItem.wrap !== "undefined" + property: "wrap" + value: control.wrap + } + + Binding { + target: control.textInputItem + property: "selectByMouse" + value: control.selectByMouse + } + + Binding { + target: control.textInputItem + property: "inputMask" + value: control.inputMask + } + + // Timer for firing the +/- button repeat events while they're held down. + Timer { + id: btnRepeatTimer + property bool delay: true + property bool increment: true + interval: delay ? control.buttonRepeatDelay : control.buttonRepeatInterval + repeat: true + onRunningChanged: delay = true + onTriggered: { + if (delay) + delay = false; + else if (increment) + control.increase(); + else + control.decrease(); + } + } + + // Wheel/scroll action detection area + MouseArea { + anchors.fill: control + z: control.contentItem.z + 1 + acceptedButtons: Qt.NoButton + enabled: control.wheelEnabled + onWheel: { + var delta = (wheel.angleDelta.y === 0.0 ? -wheel.angleDelta.x : wheel.angleDelta.y) / 120; + if (wheel.inverted) + delta *= -1; + if (wheel.modifiers & Qt.ControlModifier) + delta *= control.pageSteps; + control.stepBy(delta); + } + } + +} diff --git a/widgets/ResizeAnchor.qml b/widgets/ResizeAnchor.qml new file mode 100644 index 00000000..f2816cd8 --- /dev/null +++ b/widgets/ResizeAnchor.qml @@ -0,0 +1,114 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import QFRCDashboard 1.0 +import QFDFlags 1.0 + +Rectangle { + id: resizeAnchor + property int margin + + required property int direction + + property bool hasLeft: direction & QFDFlags.LEFT + property bool hasRight: direction & QFDFlags.RIGHT + property bool hasTop: direction & QFDFlags.TOP + property bool hasBottom: direction & QFDFlags.BOTTOM + + property bool horiz: hasLeft || hasRight + property bool vert: hasTop || hasBottom + + color: "transparent" + + anchors { + left: !hasRight ? parent.left : undefined + right: !hasLeft ? parent.right : undefined + top: !hasBottom ? parent.top : undefined + bottom: !hasTop ? parent.bottom : undefined + + leftMargin: horiz ? 0 : margin + rightMargin: horiz ? 0 : margin + topMargin: vert ? 0 : margin + bottomMargin: vert ? 0 : margin + } + + function redoMargin() { + margin = Math.min(parent.width, parent.height) / 14 + + anchors.leftMargin = horiz ? 0 : margin + anchors.rightMargin = horiz ? 0 : margin + anchors.topMargin = vert ? 0 : margin + anchors.bottomMargin = vert ? 0 : margin + + if (vert) height = margin + if (horiz) width = margin + } + + Component.onCompleted: { + redoMargin() + parent.onWidthChanged.connect(redoMargin) + parent.onHeightChanged.connect(redoMargin) + } + + MouseArea { + id: mouseArea + z: 1 + + anchors.fill: parent + + cursorShape: { + if (horiz && vert) { + if ((hasLeft && hasTop) || (hasRight && hasBottom)) { + return Qt.SizeFDiagCursor + } else { + return Qt.SizeBDiagCursor + } + } else if (horiz) { + return Qt.SizeHorCursor + } else { + return Qt.SizeVerCursor + } + } + + drag.target: parent + drag.axis: { + if (horiz && vert) { + return Drag.XAndYAxis + } else if (horiz) { + return Drag.XAxis + } else if (vert) { + return Drag.YAxis + } + } + + onPressed: (mouse) => resetResize(mouse) + + onReleased: releaseResize() + + onMouseXChanged: { + if (drag.active) { + if (hasRight) { + parent.parent.width = parent.parent.width + mouseX + } else if (hasLeft) { + parent.parent.width = parent.parent.width - mouseX + parent.parent.x = parent.parent.x + mouseX + } + + resizeBegin(rect.Drag) + } + } + + onMouseYChanged: { + if (drag.active) { + if (hasBottom) { + parent.parent.height = parent.parent.height + mouseY + } else if (hasTop) { + parent.parent.height = parent.parent.height - mouseY + parent.parent.y = parent.parent.y + mouseY + } + + resizeBegin(rect.Drag) + } + } + } +} diff --git a/widgets/misc/CameraView.qml b/widgets/misc/CameraView.qml new file mode 100644 index 00000000..1837e812 --- /dev/null +++ b/widgets/misc/CameraView.qml @@ -0,0 +1,91 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import QtMultimedia + +import QFRCDashboard + +BaseWidget { + property string item_host: "127.0.0.1" + + property int item_port: 1181 + property int portMax: 65535 + + property int item_quality: 0 + property int qualityMax: 100 + + property int item_fps: 0 + property int fpsMax: 240 + + property size item_resolution: Qt.size(0, 0) + + onItem_fpsChanged: player.resetSource() + onItem_portChanged: player.resetSource() + onItem_qualityChanged: player.resetSource() + onItem_resolutionChanged: player.resetSource() + onItem_hostChanged: player.resetSource() + + MenuItem { + id: reconnItem + text: "Reconnect" + onTriggered: { + player.reconnect() + } + } + + Component.onCompleted: { + rcMenu.addItem(reconnItem) + } + + Rectangle { + anchors { + top: titleField.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + + margins: 8 + } + + Timer { + id: timer + } + + MediaPlayer { + id: player + + source: "" + + function restartVideo() { + console.log("Restarting.") + player.play() + } + + function resetSource() { + source = Qt.url(item_host + ":" + item_port + "/stream.mjpg?" + (item_quality !== 0 ? "compression=" + item_quality + "&" : "") + (item_fps !== 0 ? "fps=" + item_fps + "&" : "") + (item_resolution !== Qt.size(0, 0) ? "resolution=" + item_resolution.width + "x" + item_resolution.height : "")) + } + + function reconnect() { + player.stop() + timer.interval = 100 + timer.repeat = false + timer.onTriggered.connect(restartVideo) + timer.start() + } + + onSourceChanged: { + console.log(source) + reconnect() + } + + videoOutput: video + onErrorOccurred: (error, errorString) => console.error( + "CameraView: error:", errorString) + } + + VideoOutput { + id: video + anchors.fill: parent + } + } +} diff --git a/widgets/primitive/BoolWidget.qml b/widgets/primitive/BoolWidget.qml new file mode 100644 index 00000000..fa351dbe --- /dev/null +++ b/widgets/primitive/BoolWidget.qml @@ -0,0 +1,73 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_checkboxSize: 20 + + property list item_test: [] + + property string testValueType: "color" + property string testValueName: "Color" + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Color Display" + onTriggered: { + model.type = "color" + } + } + } + + Component.onCompleted: { + rcMenu.addMenu(switchMenu) + } + + CheckBox { + id: control + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + checked = ntValue + } + } + + checked: false + + anchors { + centerIn: parent + } + + indicator.implicitHeight: item_checkboxSize + indicator.implicitWidth: item_checkboxSize + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + + item_topic = model.topic + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onToggled: topicStore.setValue(item_topic, checked) + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + + control.checked = topicStore.getValue(item_topic) + } +} diff --git a/widgets/primitive/ColorWidget.qml b/widgets/primitive/ColorWidget.qml new file mode 100644 index 00000000..3a03ba09 --- /dev/null +++ b/widgets/primitive/ColorWidget.qml @@ -0,0 +1,84 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Shapes 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property color item_falseColor: "#FF0000" + property color item_trueColor: "#00FF00" + + // @disable-check M311 + property var item_shape: "Rectangle" + + property list shapeChoices: ["Rectangle", "Circle", "Triangle"] + + property bool itemValue + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Checkbox" + onTriggered: { + model.type = "bool" + } + } + } + + function setColor() { + shape.itemColor = itemValue ? item_trueColor : item_falseColor + shape.itemShape = item_shape + shape.setColor() + } + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + itemValue = ntValue + setColor() + } + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + rcMenu.addMenu(switchMenu) + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onItem_falseColorChanged: setColor() + onItem_trueColorChanged: setColor() + onItem_shapeChanged: setColor() + + ShapeHandler { + id: shape + + itemShape: item_shape + + anchors { + top: titleField.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + + margins: 10 + } + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + updateTopic(model.topic, topicStore.getValue(model.topic)) + } +} diff --git a/widgets/primitive/DoubleDialWidget.qml b/widgets/primitive/DoubleDialWidget.qml new file mode 100644 index 00000000..4ad764c5 --- /dev/null +++ b/widgets/primitive/DoubleDialWidget.qml @@ -0,0 +1,162 @@ +import QtQuick +import QtQuick.Controls 6.6 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 15 + property double item_stepSize: 0.1 + + property double item_startAngle: 180 + property double item_endAngle: 540 + + property double item_lowerBound: 0 + property double item_upperBound: 1000.0 + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Spin Box" + onTriggered: { + model.type = "double" + } + } + } + + Component.onCompleted: { + rcMenu.addMenu(switchMenu) + } + + DoubleSpinBox { + id: spin + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + value = ntValue + dial.value = ntValue + } + } + + value: 0 + stepSize: item_stepSize + + from: item_lowerBound + to: item_upperBound + + anchors { + top: dial.bottom + topMargin: 8 + + left: parent.left + right: parent.right + + leftMargin: 10 + rightMargin: 10 + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onValueModified: { + dial.value = value + topicStore.setValue(item_topic, value) + } + + function setValue(val) { + value = val + topicStore.setValue(item_topic, value) + } + } + + Dial { + id: dial + + background: Rectangle { + x: dial.width / 2 - width / 2 + y: dial.height / 2 - height / 2 + implicitWidth: 140 + implicitHeight: 140 + width: Math.max(64, Math.min(dial.width, dial.height)) + height: width + color: "transparent" + radius: width / 2 + border.color: Constants.accent + opacity: dial.enabled ? 1 : 0.3 + } + + handle: Rectangle { + id: handleItem + x: dial.background.x + dial.background.width / 2 - width / 2 + y: dial.background.y + dial.background.height / 2 - height / 2 + width: Math.min(parent.width, parent.height) / 5 + height: Math.min(parent.width, parent.height) / 5 + color: Constants.accent + radius: 8 + antialiasing: true + opacity: dial.enabled ? 1 : 0.3 + transform: [ + Translate { + y: -Math.min( + dial.background.width, + dial.background.height) * 0.4 + handleItem.height / 2 + }, + Rotation { + angle: dial.angle + origin.x: handleItem.width / 2 + origin.y: handleItem.height / 2 + } + ] + } + + inputMode: Dial.Circular + + font.pixelSize: item_fontSize + + value: 0 + stepSize: item_stepSize + + height: parent.height / 3 + width: parent.width / 3 + + from: item_lowerBound + to: item_upperBound + + startAngle: item_startAngle + endAngle: item_endAngle + + anchors { + top: titleField.bottom + horizontalCenter: parent.horizontalCenter + + topMargin: 10 + } + + onMoved: { + spin.setValue(value) + } + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + + spin.value = topicStore.getValue(item_topic) + dial.value = topicStore.getValue(item_topic) + } +} diff --git a/widgets/primitive/DoubleWidget.qml b/widgets/primitive/DoubleWidget.qml new file mode 100644 index 00000000..625a1260 --- /dev/null +++ b/widgets/primitive/DoubleWidget.qml @@ -0,0 +1,81 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 15 + property double item_stepSize: 0.1 + + property double item_lowerBound: 0 + property double item_upperBound: 1000.0 + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Dial" + onTriggered: { + model.type = "doubleDial" + } + } + } + + Component.onCompleted: { + rcMenu.addMenu(switchMenu) + } + + DoubleSpinBox { + id: spin + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + value = ntValue + } + } + + value: 0 + stepSize: item_stepSize + from: item_lowerBound + to: item_upperBound + + anchors { + verticalCenter: parent.verticalCenter + topMargin: titleField.height + + left: parent.left + right: parent.right + + leftMargin: 10 + rightMargin: 10 + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onValueModified: { + topicStore.setValue(item_topic, value) + } + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + spin.value = topicStore.getValue(item_topic) + } +} diff --git a/widgets/primitive/EnumWidget.qml b/widgets/primitive/EnumWidget.qml new file mode 100644 index 00000000..a8e60f60 --- /dev/null +++ b/widgets/primitive/EnumWidget.qml @@ -0,0 +1,98 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Shapes 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + // @disable-check M311 + property var item_shape: "Rectangle" + + property list shapeChoices: ["Rectangle", "Circle", "Triangle"] + + property string itemValue + + property list item_colorMap: [] + + property string colorMapValueType: "color" + property string colorMapValueName: "Color" + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Text" + onTriggered: { + model.type = "string" + } + } + } + + function colorFromMap(value) { + for (let i = 0 ; i < item_colorMap.length; ++i) { + let obj = item_colorMap[i]; + + if (obj["Value"] === value) { + return obj[colorMapValueName] + } + } + + return "#000000" + } + + function setColor() { + shape.itemColor = colorFromMap(itemValue) + shape.itemShape = item_shape + shape.setColor() + } + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + itemValue = ntValue + setColor() + } + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + rcMenu.addMenu(switchMenu) + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onItem_colorMapChanged: setColor() + onItem_shapeChanged: setColor() + + ShapeHandler { + id: shape + + itemShape: item_shape + + anchors { + top: titleField.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + + margins: 10 + } + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + + updateTopic(model.topic, topicStore.getValue(model.topic)) + } +} diff --git a/widgets/primitive/IntDialWidget.qml b/widgets/primitive/IntDialWidget.qml new file mode 100644 index 00000000..8437d3ca --- /dev/null +++ b/widgets/primitive/IntDialWidget.qml @@ -0,0 +1,162 @@ +import QtQuick +import QtQuick.Controls 6.6 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 15 + property int item_stepSize: 1 + + property double item_startAngle: 180 + property double item_endAngle: 540 + + property int item_lowerBound: 0 + property int item_upperBound: 1000 + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Spin Box" + onTriggered: { + model.type = "int" + } + } + } + + Component.onCompleted: { + rcMenu.addMenu(switchMenu) + } + + SpinBox { + id: spin + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + value = ntValue + dial.value = ntValue + } + } + + value: 0 + stepSize: item_stepSize + + from: item_lowerBound + to: item_upperBound + + anchors { + top: dial.bottom + topMargin: 8 + + left: parent.left + right: parent.right + + leftMargin: 10 + rightMargin: 10 + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onValueModified: { + dial.value = value + topicStore.setValue(item_topic, value) + } + + function setValue(val) { + value = val + topicStore.setValue(item_topic, value) + } + } + + Dial { + id: dial + + background: Rectangle { + x: dial.width / 2 - width / 2 + y: dial.height / 2 - height / 2 + implicitWidth: 140 + implicitHeight: 140 + width: Math.max(64, Math.min(dial.width, dial.height)) + height: width + color: "transparent" + radius: width / 2 + border.color: Constants.accent + opacity: dial.enabled ? 1 : 0.3 + } + + handle: Rectangle { + id: handleItem + x: dial.background.x + dial.background.width / 2 - width / 2 + y: dial.background.y + dial.background.height / 2 - height / 2 + width: Math.min(parent.width, parent.height) / 5 + height: Math.min(parent.width, parent.height) / 5 + color: Constants.accent + radius: 8 + antialiasing: true + opacity: dial.enabled ? 1 : 0.3 + transform: [ + Translate { + y: -Math.min( + dial.background.width, + dial.background.height) * 0.4 + handleItem.height / 2 + }, + Rotation { + angle: dial.angle + origin.x: handleItem.width / 2 + origin.y: handleItem.height / 2 + } + ] + } + + inputMode: Dial.Circular + + font.pixelSize: item_fontSize + + value: 0 + stepSize: item_stepSize + + height: parent.height / 3 + width: parent.width / 3 + + from: item_lowerBound + to: item_upperBound + + startAngle: item_startAngle + endAngle: item_endAngle + + anchors { + top: titleField.bottom + horizontalCenter: parent.horizontalCenter + + topMargin: 10 + } + + onMoved: { + spin.setValue(parseInt(value)) + } + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + + spin.value = topicStore.getValue(item_topic) + dial.value = topicStore.getValue(item_topic) + } +} diff --git a/widgets/primitive/IntWidget.qml b/widgets/primitive/IntWidget.qml new file mode 100644 index 00000000..60dc6016 --- /dev/null +++ b/widgets/primitive/IntWidget.qml @@ -0,0 +1,74 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 15 + property int item_stepSize: 1 + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Dial" + onTriggered: { + model.type = "dial" + } + } + } + + Component.onCompleted: { + rcMenu.addMenu(switchMenu) + } + + SpinBox { + id: spin + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, ntValue) { + if (ntTopic === item_topic) { + value = ntValue + } + } + + value: 0 + stepSize: item_stepSize + + anchors { + verticalCenter: parent.verticalCenter + topMargin: titleField.height + + left: parent.left + right: parent.right + + leftMargin: 10 + rightMargin: 10 + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onValueModified: topicStore.setValue(item_topic, value) + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + spin.value = topicStore.getValue(item_topic) + } +} diff --git a/widgets/primitive/ShapeHandler.qml b/widgets/primitive/ShapeHandler.qml new file mode 100644 index 00000000..00753986 --- /dev/null +++ b/widgets/primitive/ShapeHandler.qml @@ -0,0 +1,121 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Shapes 2.15 + +import QFRCDashboard + +Shape { + id: shape + + function setColor() { + circle.setColor() + rect.setColor() + tri.setColor() + } + + property color itemColor + property string itemShape + + anchors { + top: titleField.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + + margins: 10 + } + + ShapePath { + id: tri + strokeWidth: 1 + + function setColor() { + strokeColor = itemShape === "Triangle" ? (shape.itemColor) : "transparent" + fillColor = itemShape === "Triangle" ? (shape.itemColor) : "transparent" + update() + } + + startX: 0 + startY: shape.height + + PathLine { + x: shape.width / 2 + y: 0 + } + + PathLine { + x: shape.width + y: shape.height + } + PathLine { + x: 0 + y: shape.height + } + } + + ShapePath { + id: rect + strokeWidth: 1 + + function setColor() { + strokeColor = itemShape === "Rectangle" ? (shape.itemColor) : "transparent" + fillColor = itemShape === "Rectangle" ? (shape.itemColor) : "transparent" + update() + } + + startX: 0 + startY: 0 + + PathLine { + x: shape.width + y: 0 + } + + PathLine { + x: shape.width + y: shape.height + } + PathLine { + x: 0 + y: shape.height + } + PathLine { + x: 0 + y: 0 + } + } + + ShapePath { + id: circle + strokeWidth: 1 + + function setColor() { + strokeColor = itemShape === "Circle" ? (shape.itemColor) : "transparent" + fillColor = itemShape === "Circle" ? (shape.itemColor) : "transparent" + update() + } + + startX: shape.width / 2 + startY: 0 + + PathArc { + x: shape.width / 2 + y: shape.width < shape.height ? shape.width : shape.height + + radiusX: Math.min(shape.width, shape.height) / 2 + radiusY: Math.min(shape.width, shape.height) / 2 + + useLargeArc: true + } + + PathArc { + x: shape.width / 2 + y: 0 + + radiusX: Math.min(shape.width, shape.height) / 2 + radiusY: Math.min(shape.width, shape.height) / 2 + + useLargeArc: true + } + } +} diff --git a/widgets/primitive/TextWidget.qml b/widgets/primitive/TextWidget.qml new file mode 100644 index 00000000..ee7d4668 --- /dev/null +++ b/widgets/primitive/TextWidget.qml @@ -0,0 +1,70 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 15 + + Menu { + id: switchMenu + title: "Switch Widget..." + + MenuItem { + text: "Enum" + onTriggered: { + model.type = "enum" + } + } + } + + Component.onCompleted: rcMenu.addMenu(switchMenu) + + TextField { + id: textField + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic) { + text = value + } + } + + text: "" + + anchors { + verticalCenter: parent.verticalCenter + topMargin: titleField.height + + left: parent.left + right: parent.right + + leftMargin: 10 + rightMargin: 10 + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + topicStore.unsubscribe(item_topic) + } + } + + onTextEdited: topicStore.setValue(item_topic, text) + } + + onItem_topicChanged: { + topicStore.unsubscribe(topic) + topicStore.subscribe(item_topic) + model.topic = item_topic + textField.text = topicStore.getValue(item_topic) + } +} diff --git a/widgets/sendable/Command.qml b/widgets/sendable/Command.qml new file mode 100644 index 00000000..ba64eca4 --- /dev/null +++ b/widgets/sendable/Command.qml @@ -0,0 +1,70 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 18 + + Button { + id: cmdButton + + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + + font.pixelSize: item_fontSize + + property bool running: false + property string name: "Command" + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/.name") { + name = value + } else if (ntTopic === item_topic + "/running") { + running = value + } + } + + onClicked: { + running = !running + topicStore.setValue(item_topic + "/running", running) + } + + text: name + + function update() { + topicStore.subscribe(item_topic + "/.name") + topicStore.subscribe(item_topic + "/running") + } + + function unsubscribe() { + topicStore.unsubscribe(item_topic + "/.name") + topicStore.unsubscribe(item_topic + "/running") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + update() + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + + onItem_topicChanged: { + cmdButton.unsubscribe() + model.topic = item_topic + + cmdButton.update() + } +} diff --git a/widgets/sendable/FMSInfo.qml b/widgets/sendable/FMSInfo.qml new file mode 100644 index 00000000..55c933e6 --- /dev/null +++ b/widgets/sendable/FMSInfo.qml @@ -0,0 +1,285 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +import QFDFlags +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 18 + + ColumnLayout { + anchors { + top: titleField.bottom + topMargin: 8 + + left: parent.left + right: parent.right + + leftMargin: 10 + rightMargin: 10 + } + + Text { + Layout.fillWidth: true + color: Constants.palette.text + + property int matchNumber: 0 + property string matchType: "Unknown" + property list matchTypeMap: ["Unknown", "Practice", "Quals", "Elims",]; + + id: match + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/MatchNumber") { + matchNumber = value + } else if (ntTopic === item_topic + "/MatchType") { + matchType = matchTypeMap[value] + } + } + + horizontalAlignment: Text.AlignHCenter + + text: matchType + " Match " + matchNumber + + function update() { + topicStore.subscribe(item_topic + "/MatchNumber") + topicStore.subscribe(item_topic + "/MatchType") + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/MatchNumber") + topicStore.unsubscribe(topic + "/MatchType") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + update() + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + + Text { + Layout.fillWidth: true + color: Constants.palette.text + + property int stationNumber: 1 + property string alliance: "Red" + + id: station + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/StationNumber") { + stationNumber = value + } else if (ntTopic === item_topic + "/IsRedAlliance") { + alliance = value ? "Red" : "Blue" + } + } + + horizontalAlignment: Text.AlignHCenter + + text: "Station: " + alliance + " " + stationNumber; + + function update() { + topicStore.subscribe(item_topic + "/StationNumber") + topicStore.subscribe(item_topic + "/IsRedAlliance") + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/StationNumber") + topicStore.unsubscribe(topic + "/IsRedAlliance") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + update() + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + + Text { + Layout.fillWidth: true + color: Constants.palette.text + + property string eventName: "Unknown Event" + + id: event + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/EventName") { + eventName = value + } + } + + horizontalAlignment: Text.AlignHCenter + + text: "Event: " + eventName; + + function update() { + topicStore.subscribe(item_topic + "/EventName") + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/EventName") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + update() + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + + Text { + Layout.fillWidth: true + color: Constants.palette.text + + property string gameSpecificMessage: "None" + + id: gsm + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/GameSpecificMessage") { + gameSpecificMessage = value + } + } + + horizontalAlignment: Text.AlignHCenter + + text: "Game Specific Message: " + gameSpecificMessage; + + function update() { + topicStore.subscribe(item_topic + "/GameSpecificMessage") + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/GameSpecificMessage") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + update() + } + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + + Text { + Layout.fillWidth: true + color: Constants.palette.text + + property string state: "Unknown" + + id: stateText + + font.pixelSize: item_fontSize + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/FMSControlData") { + let word = topicStore.toWord(value) + + state = "" + + if (word & QFDFlags.Auto) { + state += "Autonomous"; + } + else if (word & QFDFlags.Test) { + state += "Testing"; + } else { + state += "Teleop"; + } + + if (word & QFDFlags.Enabled) { + state += " Enabled"; + } else if (word & QFDFlags.EStop) { + state += " E-Stopped"; + } else { + state += " Disabled"; + } + } + } + + horizontalAlignment: Text.AlignHCenter + + text: "Robot State: " + state; + + function update() { + topicStore.subscribe(item_topic + "/FMSControlData") + updateTopic(model.topic + "/FMSControlData", topicStore.getValue(model.topic + "/FMSControlData")) + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/FMSControlData") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + update() + } + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + } + + onItem_topicChanged: { + match.unsubscribe() + station.unsubscribe() + event.unsubscribe() + stateText.unsubscribe() + gsm.unsubscribe() + + model.topic = item_topic + + match.update() + station.update() + event.update() + stateText.update() + gsm.update() + } +} diff --git a/widgets/sendable/Field2d.qml b/widgets/sendable/Field2d.qml new file mode 100644 index 00000000..aeb99c2d --- /dev/null +++ b/widgets/sendable/Field2d.qml @@ -0,0 +1,202 @@ +import QtQuick 6.2 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Shapes 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property bool item_useVerticalField: false + property bool item_mirrorForRedAlliance: false + + property double item_robotWidthMeters: 0.5 + property double item_robotLengthMeters: 0.5 + + property var item_field: "2024" + + property list fieldChoices: ["2024", "2023"] + + property double fieldWidth: 8.2296; + property double fieldLength: 8.2296 * 2.; + + property bool mirrorField: false + + property var item_robotShape: "Robot" + + property list robotShapeChoices: ["Robot", "Circle", "Rectangle"] + + property color item_robotColor: "#FF0000" + + function redraw() { + robot.redraw() + } + + onItem_robotLengthMetersChanged: redraw() + onItem_robotWidthMetersChanged: redraw() + + function updateMirror(topic, value) { + if (topic === "/FMSInfo/IsRedAlliance") { + mirrorField = value + } + } + + function unsubscribeMirror() { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateMirror) + topicStore.unsubscribe("/FMSInfo/IsRedAlliance") + } + } + + onItem_mirrorForRedAllianceChanged: { + if (item_mirrorForRedAlliance) { + topicStore.topicUpdate.connect(updateMirror) + topicStore.subscribe("/FMSInfo/IsRedAlliance") + mirrorField = topicStore.getValue("/FMSInfo/IsRedAlliance") + } else { + unsubscribeMirror() + mirrorField = false + } + } + + Component.onDestruction: { + unsubscribeMirror() + } + + Image { + id: field + + y: titleField.height + 10 + x: 8 + + width: parent.width - 16 + height: parent.height - titleField.height - 16 + + fillMode: Image.PreserveAspectFit + source: "qrc:/" + item_field + "Field" + (item_useVerticalField ? "Vertical" : "") + ".png" + onSourceChanged: robot.redraw() + + onPaintedGeometryChanged: robot.redraw() + + mirrorVertically: item_useVerticalField ? mirrorField : false + mirror: item_useVerticalField ? false : mirrorField + } + + Rectangle { + color: item_robotShape === "Robot" ? "transparent" : item_robotColor + border { + color: item_robotColor + width: 2 + } + + radius: item_robotShape === "Circle" ? Math.max(width, height) / 2 : 0 + + property double xMeters: 0 + property double yMeters: 0 + property double angleDeg: 0 + + id: robot + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/Robot") { + xMeters = value[0] + yMeters = value[1] + angleDeg = value[2] + redraw() + } + } + + function redraw() { + let meterRatio = field.paintedHeight / fieldWidth + + height = item_robotWidthMeters * meterRatio + width = item_robotLengthMeters * meterRatio + + let xPixels = xMeters * meterRatio + let yPixels = yMeters * meterRatio + + let realFieldX = field.x + (field.width - field.paintedWidth) / 2 + let realFieldY = field.y + (field.height - field.paintedHeight) / 2 + + let startPoint = item_useVerticalField ? Qt.point(realFieldX + field.paintedWidth - width, realFieldY + field.paintedHeight) + : Qt.point(realFieldX, realFieldY + field.paintedHeight) + + x = startPoint.x + xPixels + y = startPoint.y - yPixels - height + + rotation = -angleDeg + (item_useVerticalField ? 270 : 0) + + path.redraw(x, y, height, width, angleDeg) + } + + function update() { + topicStore.subscribe(item_topic + "/Robot") + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/Robot") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = model.topic + + update() + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + unsubscribe() + } + } + } + + Shape { + id: shape + + ShapePath { + id: path + strokeWidth: 3 + strokeColor: item_robotShape === "Robot" ? "light green" : "transparent" + + PathLine { + id: start + } + PathLine { + id: middle + } + PathLine { + id: end + } + + function redraw(x, y, h, w, rot) { + shape.x = x + shape.y = y + + shape.width = w + shape.height = h + + start.x = w + start.y = h / 2 + + middle.x = 0 + middle.y = h + + end.x = 0 + end.y = 0 + + shape.rotation = -rot + (item_useVerticalField ? 270 : 0) + } + } + } + + onItem_topicChanged: { + robot.unsubscribe() + + model.topic = item_topic + + robot.update() + } +} diff --git a/widgets/sendable/StringChooser.qml b/widgets/sendable/StringChooser.qml new file mode 100644 index 00000000..9c7744d8 --- /dev/null +++ b/widgets/sendable/StringChooser.qml @@ -0,0 +1,103 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +import QFRCDashboard + +BaseWidget { + property string item_topic + + property int item_fontSize: 18 + + ComboBox { + id: combo + anchors { + verticalCenter: parent.verticalCenter + + left: parent.left + right: parent.right + + margins: 8 + } + + delegate: ItemDelegate { + id: delegate + + width: combo.width + contentItem: Text { + text: modelData + color: "white" + font.pixelSize: item_fontSize + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + highlighted: combo.highlightedIndex === index + } + + font.pixelSize: item_fontSize + + property list choices + property string active + + property bool readyToUpdate: true + + model: choices + + function updateTopic(ntTopic, value) { + if (ntTopic === item_topic + "/options") { + choices = value + } else if (ntTopic === item_topic + "/active") { + if (!readyToUpdate) { + readyToUpdate = true; + return; + } + + active = value + currentIndex = indexOfValue(active) + } + } + + function connected(conn) { + if (conn) { + readyToUpdate = false; + + topicStore.setValue(item_topic + "/selected", currentText) + } + } + + function update() { + topicStore.subscribe(item_topic + "/options") + topicStore.subscribe(item_topic + "/active") + topicStore.subscribe(item_topic + "/selected") + } + + function unsubscribe() { + topicStore.unsubscribe(topic + "/options") + topicStore.unsubscribe(topic + "/active") + topicStore.unsubscribe(topic + "/selected") + } + + Component.onCompleted: { + topicStore.topicUpdate.connect(updateTopic) + item_topic = topic + + update() + } + + Component.onDestruction: { + if (topicStore !== null) { + topicStore.topicUpdate.disconnect(updateTopic) + } + } + + onCurrentIndexChanged: { + topicStore.setValue(item_topic + "/selected", valueAt(currentIndex)) + } + } + + onItem_topicChanged: { + combo.unsubscribe() + topic = item_topic + combo.update() + } +}