Compare commits
34 commits
Author | SHA1 | Date | |
---|---|---|---|
649d48c096 | |||
1604c102eb | |||
0793e85b47 | |||
06fe470e5c | |||
783afb54e7 | |||
97d648cac6 | |||
0e13a362f1 | |||
a1db66cf7a | |||
2f6b686859 | |||
6d7820cf01 | |||
a572ee58d3 | |||
ae31504772 | |||
63961f3741 | |||
c70d9140a4 | |||
b326f2e3e0 | |||
8974aed013 | |||
2d94767f96 | |||
c65f075638 | |||
4356e80e50 | |||
c45d9a71e8 | |||
fd7711aa5d | |||
c6a2d2acad | |||
ca349ad7b0 | |||
75f18095e0 | |||
092e645296 | |||
49670ebb0f | |||
115d0484a6 | |||
886b649a0d | |||
195bd7005e | |||
ae62ee3d27 | |||
cb5719ec0e | |||
3508208473 | |||
7a0712af1f | |||
11db0f0dbf |
|
@ -5,10 +5,13 @@ HEADER_HASH="$(cat "$PWD/.ci/license/header-hash.txt")"
|
|||
|
||||
echo "Getting branch changes"
|
||||
|
||||
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
|
||||
RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
|
||||
FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
|
||||
# BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
# COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
|
||||
# RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
|
||||
# FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
|
||||
|
||||
BASE=`git merge-base master HEAD`
|
||||
FILES=`git diff --name-only $BASE`
|
||||
|
||||
#FILES=$(git diff --name-only master)
|
||||
|
||||
|
|
2
.gitignore
vendored
|
@ -53,3 +53,5 @@ eden-windows-msvc
|
|||
artifacts
|
||||
*.AppImage*
|
||||
/install*
|
||||
/*.patch
|
||||
/.cache
|
||||
|
|
47
CMakeModules/EdenModule.cmake
Normal file
|
@ -0,0 +1,47 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
function(EdenModule)
|
||||
set(oneValueArgs
|
||||
NAME
|
||||
URI
|
||||
NATIVE
|
||||
)
|
||||
|
||||
set(multiValueArgs
|
||||
LIBRARIES
|
||||
QML_FILES
|
||||
SOURCES
|
||||
)
|
||||
|
||||
cmake_parse_arguments(MODULE "" "${oneValueArgs}" "${multiValueArgs}"
|
||||
"${ARGN}")
|
||||
|
||||
set(LIB_NAME Eden${MODULE_NAME})
|
||||
|
||||
add_library(${LIB_NAME} STATIC)
|
||||
|
||||
message(STATUS "URI for ${MODULE_NAME}: ${MODULE_URI}")
|
||||
|
||||
qt_add_qml_module(${LIB_NAME}
|
||||
URI ${MODULE_URI}
|
||||
NO_PLUGIN
|
||||
VERSION 0.1
|
||||
|
||||
QML_FILES ${MODULE_QML_FILES}
|
||||
SOURCES ${MODULE_SOURCES}
|
||||
|
||||
${MODULE_UNPARSED_ARGUMENTS}
|
||||
)
|
||||
|
||||
add_library(Eden::${MODULE_NAME} ALIAS ${LIB_NAME})
|
||||
|
||||
if (DEFINED MODULE_LIBRARIES)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE ${MODULE_LIBRARIES})
|
||||
endif()
|
||||
endfunction()
|
|
@ -245,6 +245,6 @@ include(CPMUtil)
|
|||
|
||||
Currently, `cpm-fetch.sh` defines the following directories for cpmfiles (max depth of 2, so subdirs are caught as well):
|
||||
|
||||
`externals src/yuzu src/dynarmic .`
|
||||
`externals src/qt_common src/dynarmic .`
|
||||
|
||||
Whenever you add a new cpmfile, update the script accordingly
|
|
@ -18,20 +18,20 @@ set_property(DIRECTORY APPEND PROPERTY
|
|||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
# Set compilation flags
|
||||
if (MSVC AND NOT CXX_CLANG)
|
||||
if (MSVC)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||
|
||||
# Silence "deprecation" warnings
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
|
||||
|
||||
# Avoid windows.h junk
|
||||
add_compile_definitions(NOMINMAX)
|
||||
add_definitions(-DNOMINMAX)
|
||||
|
||||
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
|
||||
add_compile_definitions(WIN32_LEAN_AND_MEAN)
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
|
||||
# Ensure that projects are built with Unicode support.
|
||||
add_compile_definitions(UNICODE _UNICODE)
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
|
||||
# /W4 - Level 4 warnings
|
||||
# /MP - Multi-threaded compilation
|
||||
|
@ -69,6 +69,10 @@ if (MSVC AND NOT CXX_CLANG)
|
|||
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
|
||||
/external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them.
|
||||
|
||||
# Warnings
|
||||
/W4
|
||||
/WX-
|
||||
|
||||
/we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4189 # 'identifier': local variable is initialized but not referenced
|
||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||
|
@ -93,14 +97,6 @@ if (MSVC AND NOT CXX_CLANG)
|
|||
/wd4702 # unreachable code (when used with LTO)
|
||||
)
|
||||
|
||||
if (NOT CXX_CLANG)
|
||||
add_compile_options(
|
||||
# Warnings
|
||||
/W4
|
||||
/WX-
|
||||
)
|
||||
endif()
|
||||
|
||||
if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS)
|
||||
# when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format
|
||||
# Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21
|
||||
|
@ -122,16 +118,11 @@ if (MSVC AND NOT CXX_CLANG)
|
|||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
||||
else()
|
||||
if (NOT MSVC)
|
||||
add_compile_options(
|
||||
-fwrapv
|
||||
)
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
-fwrapv
|
||||
|
||||
-Werror=all
|
||||
-Werror=extra
|
||||
-Werror=missing-declarations
|
||||
-Werror=shadow
|
||||
-Werror=unused
|
||||
|
||||
|
@ -141,19 +132,14 @@ else()
|
|||
-Wno-missing-field-initializers
|
||||
)
|
||||
|
||||
if (CXX_CLANG OR CXX_ICC) # Clang or AppleClang
|
||||
if (NOT MSVC)
|
||||
add_compile_options(
|
||||
-Werror=shadow-uncaptured-local
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=type-limits
|
||||
)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES Clang OR CMAKE_CXX_COMPILER_ID MATCHES IntelLLVM) # Clang or AppleClang
|
||||
add_compile_options(
|
||||
-Wno-braced-scalar-init
|
||||
-Wno-unused-private-field
|
||||
-Wno-nullability-completeness
|
||||
-Werror=shadow-uncaptured-local
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=type-limits
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -161,12 +147,12 @@ else()
|
|||
add_compile_options("-mcx16")
|
||||
endif()
|
||||
|
||||
if (APPLE AND CXX_CLANG)
|
||||
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
add_compile_options("-stdlib=libc++")
|
||||
endif()
|
||||
|
||||
# GCC bugs
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CXX_GCC)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# These diagnostics would be great if they worked, but are just completely broken
|
||||
# and produce bogus errors on external libraries like fmt.
|
||||
add_compile_options(
|
||||
|
@ -182,15 +168,15 @@ else()
|
|||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||
# by setting _FILE_OFFSET_BITS.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||
add_compile_definitions(_FILE_OFFSET_BITS=64)
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
if (MINGW)
|
||||
add_compile_definitions(MINGW_HAS_SECURE_API)
|
||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||
add_compile_options("-msse4.1")
|
||||
|
||||
if (MINGW_STATIC_BUILD)
|
||||
add_compile_definitions(QT_STATICPLUGIN)
|
||||
add_definitions(-DQT_STATICPLUGIN)
|
||||
add_compile_options("-static")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -234,7 +220,9 @@ if (YUZU_ROOM_STANDALONE)
|
|||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(yuzu)
|
||||
add_definitions(-DYUZU_QT_WIDGETS)
|
||||
add_subdirectory(qt_common)
|
||||
add_subdirectory(Eden)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
|
@ -245,5 +233,3 @@ if (ANDROID)
|
|||
add_subdirectory(android/app/src/main/jni)
|
||||
target_include_directories(yuzu-android PRIVATE android/app/src/main)
|
||||
endif()
|
||||
|
||||
include(GenerateDepHashes)
|
||||
|
|
22
src/Eden/CMakeLists.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui Quick QuickControls2)
|
||||
|
||||
qt_standard_project_setup(REQUIRES 6.7)
|
||||
|
||||
include(EdenModule)
|
||||
|
||||
include_directories(AFTER "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
add_subdirectory(Interface)
|
||||
add_subdirectory(Models)
|
||||
|
||||
add_subdirectory(Config)
|
||||
add_subdirectory(Constants)
|
||||
add_subdirectory(Items)
|
||||
add_subdirectory(Util)
|
||||
add_subdirectory(Main)
|
||||
|
||||
add_subdirectory(Native)
|
62
src/Eden/Config/CMakeLists.txt
Normal file
|
@ -0,0 +1,62 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
EdenModule(
|
||||
NAME Config
|
||||
URI Eden.Config
|
||||
|
||||
QML_FILES
|
||||
GlobalConfigureDialog.qml
|
||||
Setting.qml
|
||||
SectionHeader.qml
|
||||
|
||||
pages/SettingsList.qml
|
||||
pages/global/GlobalTab.qml
|
||||
pages/global/GlobalTabSwipeView.qml
|
||||
pages/global/GlobalGeneralPage.qml
|
||||
pages/global/GlobalSystemPage.qml
|
||||
pages/global/GlobalCpuPage.qml
|
||||
pages/global/GlobalGraphicsPage.qml
|
||||
pages/global/GlobalAudioPage.qml
|
||||
|
||||
pages/general/UiGeneralPage.qml
|
||||
pages/general/UiGameListPage.qml
|
||||
|
||||
pages/graphics/RendererPage.qml
|
||||
pages/graphics/RendererAdvancedPage.qml
|
||||
pages/graphics/RendererExtensionsPage.qml
|
||||
|
||||
pages/system/SystemGeneralPage.qml
|
||||
pages/system/SystemCorePage.qml
|
||||
pages/system/FileSystemPage.qml
|
||||
pages/system/AppletsPage.qml
|
||||
|
||||
pages/cpu/CpuGeneralPage.qml
|
||||
|
||||
pages/audio/AudioGeneralPage.qml
|
||||
|
||||
pages/global/GlobalDebugPage.qml
|
||||
|
||||
pages/debug/DebugGeneralPage.qml
|
||||
pages/debug/DebugGraphicsPage.qml
|
||||
pages/debug/DebugAdvancedPage.qml
|
||||
pages/debug/DebugCpuPage.qml
|
||||
|
||||
fields/ConfigCheckbox.qml
|
||||
fields/FieldLabel.qml
|
||||
fields/ConfigComboBox.qml
|
||||
fields/ConfigIntLine.qml
|
||||
fields/ConfigTimeEdit.qml
|
||||
fields/ConfigIntSpin.qml
|
||||
fields/ConfigHexEdit.qml
|
||||
fields/ConfigStringEdit.qml
|
||||
fields/FieldCheckbox.qml
|
||||
fields/ConfigIntSlider.qml
|
||||
fields/BaseField.qml
|
||||
|
||||
TestSetting.qml
|
||||
LIBRARIES Eden::Interface
|
||||
)
|
80
src/Eden/Config/GlobalConfigureDialog.qml
Normal file
|
@ -0,0 +1,80 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Items
|
||||
import Eden.Interface
|
||||
import Eden.Util
|
||||
|
||||
AnimatedDialog {
|
||||
property list<var> configs
|
||||
|
||||
preferredWidth: 1280
|
||||
|
||||
title: "Configuration"
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
|
||||
Component.onCompleted: configs = Util.searchItem(swipe, "BaseField")
|
||||
|
||||
onAccepted: {
|
||||
configs.forEach(config => {
|
||||
config.apply()
|
||||
// console.log(config.setting.label)
|
||||
})
|
||||
QtConfig.save()
|
||||
}
|
||||
onRejected: {
|
||||
configs.forEach(config => config.sync())
|
||||
QtConfig.reload()
|
||||
}
|
||||
|
||||
VerticalTabBar {
|
||||
id: tabBar
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 55
|
||||
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
}
|
||||
contentWidth: 100
|
||||
|
||||
currentIndex: swipe.currentIndex
|
||||
|
||||
Repeater {
|
||||
model: ["General", "System", "CPU", "Graphics", "Audio", "Debug", "Controls"]
|
||||
|
||||
SettingsTabButton {
|
||||
required property string modelData
|
||||
label: modelData
|
||||
onClicked: tabBar.currentIndex = TabBar.index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
orientation: Qt.Vertical
|
||||
anchors {
|
||||
left: tabBar.right
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
|
||||
leftMargin: 5
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
GlobalGeneralPage {}
|
||||
GlobalSystemPage {}
|
||||
GlobalCpuPage {}
|
||||
GlobalGraphicsPage {}
|
||||
GlobalAudioPage {}
|
||||
GlobalDebugPage {}
|
||||
}
|
||||
}
|
8
src/Eden/Config/SectionHeader.qml
Normal file
|
@ -0,0 +1,8 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Text {
|
||||
color: Constants.text
|
||||
font.pixelSize: 16
|
||||
}
|
92
src/Eden/Config/Setting.qml
Normal file
|
@ -0,0 +1,92 @@
|
|||
import QtQuick
|
||||
|
||||
import Qt.labs.qmlmodels
|
||||
|
||||
import Eden.Config
|
||||
|
||||
// TODO: make settings independently available (model vs setting?
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
role: "type"
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "bool"
|
||||
|
||||
ConfigCheckbox {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "enumCombo"
|
||||
|
||||
ConfigComboBox {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "intCombo"
|
||||
|
||||
ConfigComboBox {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "intLine"
|
||||
|
||||
ConfigIntLine {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "intSlider"
|
||||
|
||||
ConfigIntSlider {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "time"
|
||||
|
||||
ConfigTimeEdit {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "intSpin"
|
||||
|
||||
ConfigIntSpin {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "stringLine"
|
||||
|
||||
ConfigStringEdit {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "hex"
|
||||
|
||||
ConfigHexEdit {
|
||||
setting: model
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
}
|
39
src/Eden/Config/TestSetting.qml
Normal file
|
@ -0,0 +1,39 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Column {
|
||||
topPadding: 5
|
||||
leftPadding: 10
|
||||
|
||||
RowLayout {
|
||||
uniformCellSizes: true
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: model.label
|
||||
color: Constants.text
|
||||
font.pixelSize: 16
|
||||
|
||||
height: 40
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: model.value
|
||||
color: "lightblue"
|
||||
font.pixelSize: 14
|
||||
|
||||
height: 40
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: model.type + " " + typeof model.value + " " + model.other
|
||||
color: "lightgray"
|
||||
font.pixelSize: 12
|
||||
|
||||
height: 25
|
||||
}
|
||||
}
|
170
src/Eden/Config/fields/BaseField.qml
Normal file
|
@ -0,0 +1,170 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
Item {
|
||||
id: field
|
||||
property var setting
|
||||
property var value
|
||||
property bool showLabel: true
|
||||
property bool forceCheckbox: false
|
||||
|
||||
property alias enable: enable.checked
|
||||
property Item contentItem
|
||||
|
||||
readonly property string typeName: "BaseField"
|
||||
|
||||
clip: true
|
||||
height: content.height + (helpText.height + helpText.anchors.topMargin)
|
||||
|
||||
Component.onCompleted: sync()
|
||||
|
||||
function apply() {
|
||||
if (setting.value !== value) {
|
||||
setting.value = value
|
||||
}
|
||||
}
|
||||
|
||||
function sync() {
|
||||
if (value !== setting.value) {
|
||||
value = setting.value
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
height: 50
|
||||
|
||||
spacing: 0
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
z: 2
|
||||
|
||||
IconButton {
|
||||
label: "help"
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
|
||||
onClicked: helpText.toggle()
|
||||
icon.color: setting.tooltip !== "" ? Constants.text : Constants.dialog
|
||||
z: 2
|
||||
}
|
||||
|
||||
FieldCheckbox {
|
||||
id: enable
|
||||
setting: field.setting
|
||||
z: 2
|
||||
force: field.forceCheckbox
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
uniformCellSizes: true
|
||||
spacing: 0
|
||||
z: 2
|
||||
|
||||
FieldLabel {
|
||||
z: 2
|
||||
id: label
|
||||
setting: field.setting
|
||||
}
|
||||
|
||||
children: showLabel ? [label, contentItem] : [contentItem]
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Constants.dialog
|
||||
anchors.fill: content
|
||||
z: 0
|
||||
}
|
||||
|
||||
Text {
|
||||
id: helpText
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
right: parent.right
|
||||
rightMargin: 20
|
||||
|
||||
top: content.bottom
|
||||
topMargin: -height
|
||||
}
|
||||
|
||||
z: -1
|
||||
|
||||
text: setting.tooltip
|
||||
color: Constants.subText
|
||||
font.pixelSize: 12
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
visible: false
|
||||
opacity: 0
|
||||
|
||||
function toggle() {
|
||||
if (visible) {
|
||||
hideAnim.start()
|
||||
} else {
|
||||
showAnim.start()
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: showAnim
|
||||
|
||||
SmoothedAnimation {
|
||||
target: helpText
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
|
||||
velocity: 3
|
||||
}
|
||||
|
||||
SmoothedAnimation {
|
||||
target: helpText
|
||||
property: "anchors.topMargin"
|
||||
from: -helpText.height
|
||||
to: 0
|
||||
|
||||
duration: 300
|
||||
velocity: -1
|
||||
}
|
||||
|
||||
onStarted: helpText.visible = true
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: hideAnim
|
||||
|
||||
SmoothedAnimation {
|
||||
target: helpText
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
|
||||
velocity: 3
|
||||
}
|
||||
|
||||
SmoothedAnimation {
|
||||
target: helpText
|
||||
property: "anchors.topMargin"
|
||||
from: 0
|
||||
to: -helpText.height
|
||||
|
||||
duration: 300
|
||||
velocity: -1
|
||||
}
|
||||
|
||||
onFinished: helpText.visible = false
|
||||
}
|
||||
}
|
||||
}
|
27
src/Eden/Config/fields/ConfigCheckbox.qml
Normal file
|
@ -0,0 +1,27 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
BaseField {
|
||||
forceCheckbox: true
|
||||
// // TODO: global/custom
|
||||
// contentItem: CheckBox {
|
||||
// id: control
|
||||
|
||||
// Layout.rightMargin: 10
|
||||
// Layout.fillWidth: true
|
||||
|
||||
// font.pixelSize: 15
|
||||
// indicator.implicitHeight: 25
|
||||
// indicator.implicitWidth: 25
|
||||
|
||||
// text: setting.label
|
||||
// checked: setting.value
|
||||
|
||||
// onClicked: value = checked
|
||||
|
||||
// checkable: true
|
||||
// }
|
||||
}
|
33
src/Eden/Config/fields/ConfigComboBox.qml
Normal file
|
@ -0,0 +1,33 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls.Material.impl
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Config
|
||||
|
||||
BaseField {
|
||||
contentItem: ComboBox {
|
||||
id: control
|
||||
enabled: enable
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
|
||||
font.pixelSize: 14
|
||||
model: setting.combo
|
||||
currentIndex: value
|
||||
|
||||
background: MaterialTextContainer {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
|
||||
outlineColor: (enabled
|
||||
&& control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
|
||||
focusedOutlineColor: control.Material.accentColor
|
||||
controlHasActiveFocus: control.activeFocus
|
||||
controlHasText: true
|
||||
horizontalPadding: control.Material.textFieldHorizontalPadding
|
||||
}
|
||||
}
|
||||
}
|
26
src/Eden/Config/fields/ConfigHexEdit.qml
Normal file
|
@ -0,0 +1,26 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
BaseField {
|
||||
contentItem: BetterTextField {
|
||||
enabled: enable
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /[0-9a-fA-F]{0,8}/
|
||||
}
|
||||
|
||||
font.pixelSize: 15
|
||||
|
||||
text: Number(value).toString(16)
|
||||
suffix: setting.suffix
|
||||
|
||||
onTextEdited: value = Number("0x" + text)
|
||||
}
|
||||
}
|
28
src/Eden/Config/fields/ConfigIntLine.qml
Normal file
|
@ -0,0 +1,28 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
BaseField {
|
||||
contentItem: BetterTextField {
|
||||
enabled: enable
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
validator: IntValidator {
|
||||
bottom: setting.min
|
||||
top: setting.max
|
||||
}
|
||||
|
||||
font.pixelSize: 15
|
||||
|
||||
text: value
|
||||
suffix: setting.suffix
|
||||
|
||||
onTextEdited: value = parseInt(text)
|
||||
}
|
||||
}
|
40
src/Eden/Config/fields/ConfigIntSlider.qml
Normal file
|
@ -0,0 +1,40 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
// Lots of cancer but idrc
|
||||
BaseField {
|
||||
id: field
|
||||
contentItem: RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Slider {
|
||||
Layout.fillWidth: true
|
||||
|
||||
from: setting.min
|
||||
to: setting.max
|
||||
stepSize: 1
|
||||
|
||||
value: field.value
|
||||
|
||||
onMoved: field.value = value
|
||||
|
||||
Layout.rightMargin: 10
|
||||
|
||||
snapMode: Slider.SnapAlways
|
||||
}
|
||||
|
||||
Text {
|
||||
font.pixelSize: 14
|
||||
color: Constants.text
|
||||
|
||||
text: field.value + setting.suffix
|
||||
|
||||
Layout.rightMargin: 10
|
||||
}
|
||||
}
|
||||
}
|
26
src/Eden/Config/fields/ConfigIntSpin.qml
Normal file
|
@ -0,0 +1,26 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
BaseField {
|
||||
id: field
|
||||
contentItem: BetterSpinBox {
|
||||
enabled: enable
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
|
||||
from: setting.min
|
||||
to: setting.max
|
||||
|
||||
font.pixelSize: 15
|
||||
|
||||
value: field.value
|
||||
label: setting.suffix
|
||||
|
||||
onValueModified: field.value = value
|
||||
}
|
||||
}
|
22
src/Eden/Config/fields/ConfigStringEdit.qml
Normal file
|
@ -0,0 +1,22 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
BaseField {
|
||||
contentItem: BetterTextField {
|
||||
enabled: enable
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
|
||||
font.pixelSize: 15
|
||||
|
||||
text: value
|
||||
suffix: setting.suffix
|
||||
|
||||
onTextEdited: value = text
|
||||
}
|
||||
}
|
27
src/Eden/Config/fields/ConfigTimeEdit.qml
Normal file
|
@ -0,0 +1,27 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Items
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
|
||||
BaseField {
|
||||
// TODO: real impl
|
||||
contentItem: BetterTextField {
|
||||
enabled: enable
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
validator: IntValidator {
|
||||
bottom: setting.min
|
||||
top: setting.max
|
||||
}
|
||||
|
||||
font.pixelSize: 15
|
||||
|
||||
text: value
|
||||
suffix: setting.suffix
|
||||
}
|
||||
}
|
20
src/Eden/Config/fields/FieldCheckbox.qml
Normal file
|
@ -0,0 +1,20 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
CheckBox {
|
||||
property bool force: false
|
||||
property var setting
|
||||
property var other: setting.other === null ? setting : setting.other
|
||||
|
||||
indicator.implicitHeight: 25
|
||||
indicator.implicitWidth: 25
|
||||
|
||||
checked: visible ? other.value : true
|
||||
onClicked: if (visible)
|
||||
other.value = checked
|
||||
|
||||
visible: setting.other !== null || force
|
||||
}
|
18
src/Eden/Config/fields/FieldLabel.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Text {
|
||||
property var setting
|
||||
|
||||
text: setting.label
|
||||
color: Constants.text
|
||||
font.pixelSize: 14
|
||||
|
||||
height: 50
|
||||
ToolTip.text: setting.tooltip
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
1
src/Eden/Config/icons.qrc
Normal file
|
@ -0,0 +1 @@
|
|||
<RCC/>
|
47
src/Eden/Config/pages/SettingsList.qml
Normal file
|
@ -0,0 +1,47 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Config
|
||||
import Eden.Constants
|
||||
import Eden.Interface
|
||||
|
||||
ColumnLayout {
|
||||
required property int category
|
||||
|
||||
property bool inset: false
|
||||
property string header: ""
|
||||
property list<string> idInclude: []
|
||||
property list<string> idExclude: []
|
||||
|
||||
SectionHeader {
|
||||
text: header
|
||||
visible: header != ""
|
||||
}
|
||||
|
||||
ListView {
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
interactive: false
|
||||
|
||||
implicitHeight: contentHeight
|
||||
delegate: Setting {}
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 5
|
||||
spacing: 8
|
||||
|
||||
model: SettingsInterface.category(category, idInclude, idExclude)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
|
||||
border {
|
||||
color: inset ? Constants.text : "transparent"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/audio/AudioGeneralPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.Audio
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/cpu/CpuGeneralPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.Cpu
|
||||
}
|
||||
}
|
||||
}
|
19
src/Eden/Config/pages/debug/DebugAdvancedPage.qml
Normal file
|
@ -0,0 +1,19 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
// TODO: filter
|
||||
SettingsList {
|
||||
category: SettingsCategories.Debugging
|
||||
}
|
||||
}
|
||||
}
|
18
src/Eden/Config/pages/debug/DebugCpuPage.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.CpuDebug
|
||||
}
|
||||
}
|
||||
}
|
28
src/Eden/Config/pages/debug/DebugGeneralPage.qml
Normal file
|
@ -0,0 +1,28 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
// TODO: split
|
||||
SettingsList {
|
||||
category: SettingsCategories.Debugging
|
||||
}
|
||||
|
||||
// TODO: wrong category?
|
||||
SettingsList {
|
||||
category: SettingsCategories.Miscellaneous
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
src/Eden/Config/pages/debug/DebugGraphicsPage.qml
Normal file
|
@ -0,0 +1,20 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
Layout.fillWidth: true
|
||||
|
||||
category: SettingsCategories.DebuggingGraphics
|
||||
}
|
||||
}
|
||||
}
|
18
src/Eden/Config/pages/general/UiGameListPage.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
// TODO: language, theme
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.UiGameList
|
||||
}
|
||||
}
|
||||
}
|
23
src/Eden/Config/pages/general/UiGeneralPage.qml
Normal file
|
@ -0,0 +1,23 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.UiGeneral
|
||||
// onContentHeightChanged: console.log(height, parent.height)
|
||||
}
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.Linux
|
||||
visible: Qt.platform.os === "linux"
|
||||
}
|
||||
}
|
||||
}
|
16
src/Eden/Config/pages/global/GlobalAudioPage.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Config
|
||||
|
||||
GlobalTab {
|
||||
property alias swipe: swipe
|
||||
|
||||
tabs: ["Audio"]
|
||||
|
||||
GlobalTabSwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
AudioGeneralPage {}
|
||||
}
|
||||
}
|
15
src/Eden/Config/pages/global/GlobalCpuPage.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Config
|
||||
|
||||
GlobalTab {
|
||||
property alias swipe: swipe
|
||||
tabs: ["CPU"]
|
||||
|
||||
GlobalTabSwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
CpuGeneralPage {}
|
||||
}
|
||||
}
|
18
src/Eden/Config/pages/global/GlobalDebugPage.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Config
|
||||
|
||||
GlobalTab {
|
||||
property alias swipe: swipe
|
||||
tabs: ["General", "Graphics", "Advanced", "CPU"]
|
||||
|
||||
GlobalTabSwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
DebugGeneralPage {}
|
||||
DebugGraphicsPage {}
|
||||
DebugAdvancedPage {}
|
||||
DebugCpuPage {}
|
||||
}
|
||||
}
|
18
src/Eden/Config/pages/global/GlobalGeneralPage.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Config
|
||||
|
||||
GlobalTab {
|
||||
property alias swipe: swipe
|
||||
tabs: ["General", "Hotkeys", "Game List"]
|
||||
|
||||
GlobalTabSwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
// TODO: platform-specific stuff
|
||||
UiGeneralPage {}
|
||||
Item {}
|
||||
UiGameListPage {}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/global/GlobalGraphicsPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Config
|
||||
|
||||
GlobalTab {
|
||||
property alias swipe: swipe
|
||||
tabs: ["Graphics", "Advanced", "Extensions"]
|
||||
|
||||
GlobalTabSwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
RendererPage {}
|
||||
RendererAdvancedPage {}
|
||||
RendererExtensionsPage {}
|
||||
}
|
||||
}
|
19
src/Eden/Config/pages/global/GlobalSystemPage.qml
Normal file
|
@ -0,0 +1,19 @@
|
|||
import QtQuick
|
||||
|
||||
import Eden.Config
|
||||
|
||||
GlobalTab {
|
||||
property alias swipe: swipe
|
||||
tabs: ["System", "Core", "Profiles", "Filesystem", "Applets"]
|
||||
|
||||
GlobalTabSwipeView {
|
||||
id: swipe
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
SystemGeneralPage {}
|
||||
SystemCorePage {}
|
||||
Item {}
|
||||
FileSystemPage {}
|
||||
AppletsPage {}
|
||||
}
|
||||
}
|
34
src/Eden/Config/pages/global/GlobalTab.qml
Normal file
|
@ -0,0 +1,34 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Item {
|
||||
required property list<string> tabs
|
||||
property alias tabBar: tabBar
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
currentIndex: swipe.currentIndex
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: tabs
|
||||
|
||||
TabButton {
|
||||
font.pixelSize: 16
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: tabBar.Material.backgroundColor
|
||||
radius: 8
|
||||
}
|
||||
}
|
||||
}
|
16
src/Eden/Config/pages/global/GlobalTabSwipeView.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
SwipeView {
|
||||
anchors {
|
||||
top: tabBar.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
|
||||
leftMargin: 20
|
||||
topMargin: 10
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/graphics/RendererAdvancedPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.RendererAdvanced
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/graphics/RendererExtensionsPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.RendererExtensions
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/graphics/RendererPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.Renderer
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/system/AppletsPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.LibraryApplet
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/system/FileSystemPage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.DataStorage
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Config/pages/system/SystemCorePage.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.Core
|
||||
}
|
||||
}
|
||||
}
|
22
src/Eden/Config/pages/system/SystemGeneralPage.qml
Normal file
|
@ -0,0 +1,22 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Interface
|
||||
import Eden.Config
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
ColumnLayout {
|
||||
width: scroll.width - scroll.effectiveScrollBarWidth
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.Network
|
||||
}
|
||||
|
||||
SettingsList {
|
||||
category: SettingsCategories.System
|
||||
idExclude: ["custom_rtc", "custom_rtc_offset", "current_user"]
|
||||
}
|
||||
}
|
||||
}
|
16
src/Eden/Constants/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set_source_files_properties(Constants.qml
|
||||
PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE true
|
||||
)
|
||||
|
||||
EdenModule(
|
||||
NAME Constants
|
||||
URI Eden.Constants
|
||||
QML_FILES Constants.qml
|
||||
)
|
24
src/Eden/Constants/Constants.qml
Normal file
|
@ -0,0 +1,24 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int width: 1200
|
||||
readonly property int height: 1000
|
||||
|
||||
property color accent: "#FF4444"
|
||||
property color accentPressed: "#ff5252"
|
||||
|
||||
readonly property color bg: "#111111"
|
||||
readonly property color dialog: "#222222"
|
||||
readonly property color dialogButton: "#000000"
|
||||
readonly property color sub: "#181818"
|
||||
|
||||
readonly property color button: "#1E1E1E"
|
||||
readonly property color buttonHighlighted: "#4A4A4A"
|
||||
|
||||
readonly property color text: "#EEEEEE"
|
||||
readonly property color subText: "#AAAAAA"
|
||||
|
||||
property real scalar: 1.0
|
||||
}
|
24
src/Eden/Interface/CMakeLists.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||
|
||||
qt_add_library(EdenInterface STATIC)
|
||||
qt_add_qml_module(EdenInterface
|
||||
URI Eden.Interface
|
||||
NO_PLUGIN
|
||||
SOURCES
|
||||
SettingsInterface.h SettingsInterface.cpp
|
||||
QMLSetting.h QMLSetting.cpp
|
||||
MetaObjectHelper.h
|
||||
QMLConfig.h
|
||||
SOURCES TitleManager.h TitleManager.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(EdenInterface PUBLIC Qt6::Quick)
|
||||
target_link_libraries(EdenInterface PRIVATE Qt6::Core)
|
||||
|
||||
add_library(Eden::Interface ALIAS EdenInterface)
|
22
src/Eden/Interface/MetaObjectHelper.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef METAOBJECTHELPER_H
|
||||
#define METAOBJECTHELPER_H
|
||||
|
||||
#include <QMetaObject>
|
||||
#include <QObject>
|
||||
#include <QQmlProperty>
|
||||
|
||||
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
|
26
src/Eden/Interface/QMLConfig.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef QMLCONFIG_H
|
||||
#define QMLCONFIG_H
|
||||
|
||||
#include "qt_common/qt_config.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QMLConfig : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
QtConfig *m_config;
|
||||
|
||||
public:
|
||||
QMLConfig()
|
||||
: m_config{new QtConfig}
|
||||
{}
|
||||
|
||||
Q_INVOKABLE inline void reload() {
|
||||
m_config->ReloadAllValues();
|
||||
}
|
||||
Q_INVOKABLE inline void save() {
|
||||
m_config->SaveAllValues();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QMLCONFIG_H
|
263
src/Eden/Interface/QMLSetting.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
#include "QMLSetting.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
QMLSetting::QMLSetting(Settings::BasicSetting *setting, QObject *parent, RequestType request)
|
||||
: QObject(parent)
|
||||
, m_setting(setting)
|
||||
{
|
||||
|
||||
// TODO: restore/touch
|
||||
/*
|
||||
if (!Settings::IsConfiguringGlobal() && managed) {
|
||||
restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
|
||||
|
||||
touch = [this]() {
|
||||
LOG_DEBUG(Frontend, "Enabling custom setting for \"{}\"", setting.GetLabel());
|
||||
restore_button->setEnabled(true);
|
||||
restore_button->setVisible(true);
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
const auto type = setting->TypeId();
|
||||
|
||||
request = [&]() {
|
||||
if (request != RequestType::Default) {
|
||||
return request;
|
||||
}
|
||||
switch (setting->Specialization() & Settings::SpecializationTypeMask) {
|
||||
case Settings::Specialization::Default:
|
||||
return RequestType::Default;
|
||||
case Settings::Specialization::Time:
|
||||
return RequestType::DateTimeEdit;
|
||||
case Settings::Specialization::Hex:
|
||||
return RequestType::HexEdit;
|
||||
case Settings::Specialization::RuntimeList:
|
||||
// managed = false;
|
||||
[[fallthrough]];
|
||||
case Settings::Specialization::List:
|
||||
return RequestType::ComboBox;
|
||||
case Settings::Specialization::Scalar:
|
||||
return RequestType::Slider;
|
||||
case Settings::Specialization::Countable:
|
||||
return RequestType::SpinBox;
|
||||
case Settings::Specialization::Radio:
|
||||
return RequestType::RadioGroup;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return request;
|
||||
}();
|
||||
|
||||
if (type == typeid(bool)) {
|
||||
m_type = "bool";
|
||||
m_metaType = QMetaType::Bool;
|
||||
} else if (setting->IsEnum()) {
|
||||
m_metaType = QMetaType::UInt;
|
||||
|
||||
if (request == RequestType::RadioGroup) {
|
||||
m_type = "radio";
|
||||
// TODO: Add the options and whatnot
|
||||
// see CreateRadioGroup
|
||||
} else {
|
||||
m_type = "enumCombo";
|
||||
}
|
||||
} else if (setting->IsIntegral()) {
|
||||
m_metaType = QMetaType::UInt;
|
||||
|
||||
switch (request) {
|
||||
case RequestType::Slider:
|
||||
case RequestType::ReverseSlider:
|
||||
// TODO: Reversal and multiplier
|
||||
m_type = "intSlider";
|
||||
break;
|
||||
case RequestType::Default:
|
||||
case RequestType::LineEdit:
|
||||
m_type = "intSpin";
|
||||
break;
|
||||
case RequestType::DateTimeEdit:
|
||||
// TODO: disabled/restrict
|
||||
m_type = "time";
|
||||
break;
|
||||
case RequestType::SpinBox:
|
||||
// TODO: suffix
|
||||
m_type = "intSpin";
|
||||
break;
|
||||
case RequestType::HexEdit:
|
||||
m_type = "hex";
|
||||
break;
|
||||
case RequestType::ComboBox:
|
||||
// TODO: Add the options and whatnot
|
||||
// see CreateComboBox
|
||||
m_type = "intCombo";
|
||||
break;
|
||||
default:
|
||||
// UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
} else if (setting->IsFloatingPoint()) {
|
||||
m_metaType = QMetaType::Double;
|
||||
|
||||
switch (request) {
|
||||
case RequestType::Default:
|
||||
case RequestType::SpinBox:
|
||||
// TODO: suffix
|
||||
m_type = "doubleSpin";
|
||||
break;
|
||||
case RequestType::Slider:
|
||||
case RequestType::ReverseSlider:
|
||||
// TODO: multiplier, suffix, reversal
|
||||
m_type = "doubleSlider";
|
||||
break;
|
||||
default:
|
||||
// UNIMPLEMENTED assert
|
||||
// UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
} else if (type == typeid(std::string)) {
|
||||
m_metaType = QMetaType::QString;
|
||||
|
||||
switch (request) {
|
||||
case RequestType::Default:
|
||||
case RequestType::LineEdit:
|
||||
m_type = "stringLine";
|
||||
break;
|
||||
case RequestType::ComboBox:
|
||||
m_type = "stringCombo";
|
||||
break;
|
||||
default:
|
||||
// UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QMLSetting::value() const
|
||||
{
|
||||
QVariant var = QVariant::fromValue(QString::fromStdString(m_setting->ToString()));
|
||||
var.convert(QMetaType(m_metaType));
|
||||
return var;
|
||||
}
|
||||
|
||||
void QMLSetting::setValue(const QVariant &newValue)
|
||||
{
|
||||
QVariant var = newValue;
|
||||
var.convert(QMetaType(m_metaType));
|
||||
|
||||
m_setting->LoadString(var.toString().toStdString());
|
||||
emit valueChanged();
|
||||
}
|
||||
|
||||
bool QMLSetting::global() const
|
||||
{
|
||||
return m_setting->UsingGlobal();
|
||||
}
|
||||
|
||||
void QMLSetting::setGlobal(bool newGlobal)
|
||||
{
|
||||
m_setting->SetGlobal(newGlobal);
|
||||
emit globalChanged();
|
||||
emit valueChanged();
|
||||
}
|
||||
|
||||
void QMLSetting::restore()
|
||||
{
|
||||
std::string toSet = Settings::IsConfiguringGlobal() ? m_setting->DefaultToString() : m_setting->ToStringGlobal();
|
||||
setValue(QString::fromStdString(toSet));
|
||||
setGlobal(false);
|
||||
}
|
||||
|
||||
QMLSetting *QMLSetting::other() const
|
||||
{
|
||||
return m_other;
|
||||
}
|
||||
|
||||
void QMLSetting::setOther(QMLSetting *newOther)
|
||||
{
|
||||
if (m_other == newOther)
|
||||
return;
|
||||
m_other = newOther;
|
||||
emit otherChanged();
|
||||
}
|
||||
|
||||
u32 QMLSetting::max() const
|
||||
{
|
||||
return std::strtol(m_setting->MaxVal().c_str(), nullptr, 0);
|
||||
}
|
||||
|
||||
u32 QMLSetting::min() const
|
||||
{
|
||||
return std::strtol(m_setting->MinVal().c_str(), nullptr, 0);
|
||||
}
|
||||
|
||||
QString QMLSetting::suffix() const
|
||||
{
|
||||
return m_suffix;
|
||||
}
|
||||
|
||||
void QMLSetting::setSuffix(const QString &newSuffix)
|
||||
{
|
||||
if (m_suffix == newSuffix)
|
||||
return;
|
||||
m_suffix = newSuffix;
|
||||
emit suffixChanged();
|
||||
}
|
||||
|
||||
QStringList QMLSetting::combo() const
|
||||
{
|
||||
return m_combo;
|
||||
}
|
||||
|
||||
void QMLSetting::setCombo(const QStringList &newCombo)
|
||||
{
|
||||
if (m_combo == newCombo)
|
||||
return;
|
||||
m_combo = newCombo;
|
||||
emit comboChanged();
|
||||
}
|
||||
|
||||
QString QMLSetting::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void QMLSetting::setType(const QString &newType)
|
||||
{
|
||||
if (m_type == newType)
|
||||
return;
|
||||
m_type = newType;
|
||||
emit typeChanged();
|
||||
}
|
||||
|
||||
u32 QMLSetting::id() const
|
||||
{
|
||||
return m_setting->Id();
|
||||
}
|
||||
|
||||
QString QMLSetting::tooltip() const
|
||||
{
|
||||
return m_tooltip;
|
||||
}
|
||||
|
||||
void QMLSetting::setTooltip(const QString &newTooltip)
|
||||
{
|
||||
if (m_tooltip == newTooltip)
|
||||
return;
|
||||
m_tooltip = newTooltip;
|
||||
emit tooltipChanged();
|
||||
}
|
||||
|
||||
QString QMLSetting::label() const
|
||||
{
|
||||
return m_label.isEmpty() ? QString::fromStdString(m_setting->GetLabel()) : m_label;
|
||||
}
|
||||
|
||||
void QMLSetting::setLabel(const QString &newLabel)
|
||||
{
|
||||
if (m_label == newLabel)
|
||||
return;
|
||||
m_label = newLabel;
|
||||
emit labelChanged();
|
||||
}
|
104
src/Eden/Interface/QMLSetting.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
#ifndef QMLSETTING_H
|
||||
#define QMLSETTING_H
|
||||
|
||||
#include <QObject>
|
||||
#include "common/settings_common.h"
|
||||
|
||||
enum class RequestType {
|
||||
Default,
|
||||
ComboBox,
|
||||
SpinBox,
|
||||
Slider,
|
||||
ReverseSlider,
|
||||
LineEdit,
|
||||
HexEdit,
|
||||
DateTimeEdit,
|
||||
RadioGroup,
|
||||
MaxEnum,
|
||||
};
|
||||
|
||||
class QMLSetting : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QMLSetting(Settings::BasicSetting *setting, QObject *parent = nullptr, RequestType request = RequestType::Default);
|
||||
|
||||
QVariant value() const;
|
||||
void setValue(const QVariant &newValue);
|
||||
|
||||
bool global() const;
|
||||
void setGlobal(bool newGlobal);
|
||||
|
||||
QString label() const;
|
||||
void setLabel(const QString &newLabel);
|
||||
|
||||
QString tooltip() const;
|
||||
void setTooltip(const QString &newTooltip);
|
||||
|
||||
u32 id() const;
|
||||
|
||||
QString type() const;
|
||||
void setType(const QString &newType);
|
||||
|
||||
QStringList combo() const;
|
||||
void setCombo(const QStringList &newCombo);
|
||||
|
||||
QString suffix() const;
|
||||
void setSuffix(const QString &newSuffix);
|
||||
|
||||
// TODO: float versions
|
||||
u32 min() const;
|
||||
u32 max() const;
|
||||
|
||||
QMLSetting *other() const;
|
||||
void setOther(QMLSetting *newOther);
|
||||
|
||||
public slots:
|
||||
void restore();
|
||||
|
||||
signals:
|
||||
void valueChanged();
|
||||
void globalChanged();
|
||||
|
||||
void labelChanged();
|
||||
|
||||
void tooltipChanged();
|
||||
|
||||
void typeChanged();
|
||||
|
||||
void comboChanged();
|
||||
|
||||
void suffixChanged();
|
||||
|
||||
void otherChanged();
|
||||
|
||||
private:
|
||||
Settings::BasicSetting *m_setting;
|
||||
|
||||
QMLSetting *m_other;
|
||||
|
||||
QString m_label;
|
||||
QString m_tooltip;
|
||||
QString m_type;
|
||||
QStringList m_combo;
|
||||
QString m_suffix;
|
||||
|
||||
QMetaType::Type m_metaType;
|
||||
|
||||
Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged FINAL)
|
||||
Q_PROPERTY(bool global READ global WRITE setGlobal NOTIFY globalChanged FINAL)
|
||||
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged FINAL)
|
||||
Q_PROPERTY(QString tooltip READ tooltip WRITE setTooltip NOTIFY tooltipChanged FINAL)
|
||||
Q_PROPERTY(u32 id READ id FINAL CONSTANT)
|
||||
Q_PROPERTY(QString type READ type WRITE setType NOTIFY typeChanged FINAL)
|
||||
Q_PROPERTY(QStringList combo READ combo WRITE setCombo NOTIFY comboChanged FINAL)
|
||||
Q_PROPERTY(QString suffix READ suffix WRITE setSuffix NOTIFY suffixChanged FINAL)
|
||||
|
||||
Q_PROPERTY(u32 min READ min FINAL CONSTANT)
|
||||
Q_PROPERTY(u32 max READ max FINAL CONSTANT)
|
||||
Q_PROPERTY(QMLSetting *other READ other WRITE setOther NOTIFY otherChanged FINAL)
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QMLSetting *)
|
||||
|
||||
#endif // QMLSETTING_H
|
124
src/Eden/Interface/SettingsInterface.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include "SettingsInterface.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "qt_common/uisettings.h"
|
||||
|
||||
SettingsInterface::SettingsInterface(QObject* parent)
|
||||
: QObject{parent}
|
||||
, translations{ConfigurationShared::InitializeTranslations(parent)}
|
||||
, combobox_translations{ConfigurationShared::ComboboxEnumeration(parent)}
|
||||
{
|
||||
}
|
||||
|
||||
SettingsModel *SettingsInterface::category(SettingsCategories::Category category,
|
||||
QList<QString> idInclude,
|
||||
QList<QString> idExclude)
|
||||
{
|
||||
std::vector<Settings::BasicSetting *> settings = Settings::values.linkage.by_category[static_cast<Settings::Category>(category)];
|
||||
std::vector<Settings::BasicSetting *> uisettings = UISettings::values.linkage.by_category[static_cast<Settings::Category>(category)];
|
||||
|
||||
settings.insert(settings.end(), uisettings.begin(), uisettings.end());
|
||||
|
||||
QList<QMLSetting *> settingsList;
|
||||
for (Settings::BasicSetting *setting : settings) {
|
||||
// paired settings get ignored
|
||||
if (setting->Specialization() == Settings::Specialization::Paired) {
|
||||
LOG_DEBUG(Frontend, "\"{}\" has specialization Paired: ignoring", setting->GetLabel());
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((idInclude.empty() || idInclude.contains(setting->GetLabel()))
|
||||
&& (idExclude.empty() || !idExclude.contains(setting->GetLabel()))) {
|
||||
settingsList.append(this->getSetting(setting));
|
||||
}
|
||||
}
|
||||
|
||||
SettingsModel *model = new SettingsModel(this);
|
||||
model->append(settingsList);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
int SettingsInterface::id(const QString &key)
|
||||
{
|
||||
return setting(key)->id();
|
||||
}
|
||||
|
||||
bool SettingsInterface::global() const
|
||||
{
|
||||
return Settings::IsConfiguringGlobal();
|
||||
}
|
||||
|
||||
void SettingsInterface::setGlobal(bool newGlobal)
|
||||
{
|
||||
Settings::SetConfiguringGlobal(newGlobal);
|
||||
}
|
||||
|
||||
QMLSetting *SettingsInterface::getSetting(Settings::BasicSetting *setting)
|
||||
{
|
||||
if (setting == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_settings.contains(setting->GetLabel())) {
|
||||
return m_settings.value(setting->GetLabel());
|
||||
}
|
||||
|
||||
const int id = setting->Id();
|
||||
|
||||
const auto [label, tooltip] = [&]() {
|
||||
const auto& setting_label = setting->GetLabel();
|
||||
if (translations->contains(id)) {
|
||||
return std::pair{translations->at(id).first, translations->at(id).second};
|
||||
}
|
||||
|
||||
LOG_WARNING(Frontend, "Translation table lacks entry for \"{}\"", setting_label);
|
||||
return std::pair{QString::fromStdString(setting_label), QString()};
|
||||
}();
|
||||
|
||||
const auto type = setting->EnumIndex();
|
||||
QStringList items;
|
||||
|
||||
const ConfigurationShared::ComboboxTranslations* enumeration{nullptr};
|
||||
if (combobox_translations->contains(type)) {
|
||||
enumeration = &combobox_translations->at(type);
|
||||
for (const auto& [_, name] : *enumeration) {
|
||||
items << name;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Suffix (fr)
|
||||
QString suffix = "";
|
||||
|
||||
if ((setting->Specialization() & Settings::SpecializationAttributeMask) ==
|
||||
Settings::Specialization::Percentage) {
|
||||
suffix = "%";
|
||||
}
|
||||
|
||||
// paired setting (I/A)
|
||||
QMLSetting *other = this->getSetting(setting->PairedSetting());
|
||||
|
||||
QMLSetting *qsetting = new QMLSetting(setting, this);
|
||||
qsetting->setLabel(label);
|
||||
qsetting->setTooltip(tooltip);
|
||||
qsetting->setCombo(items);
|
||||
qsetting->setSuffix(suffix);
|
||||
qsetting->setOther(other);
|
||||
|
||||
m_settings.insert(setting->GetLabel(), qsetting);
|
||||
|
||||
return qsetting;
|
||||
}
|
||||
|
||||
QMLSetting *SettingsInterface::setting(const QString &key)
|
||||
{
|
||||
std::string skey = key.toStdString();
|
||||
if (!m_settings.contains(skey)) {
|
||||
Settings::BasicSetting *basicSetting = Settings::values.linkage.by_key.contains(skey) ?
|
||||
Settings::values.linkage.by_key[skey] :
|
||||
UISettings::values.linkage.by_key[skey];
|
||||
return getSetting(basicSetting);
|
||||
} else {
|
||||
return m_settings.value(skey);
|
||||
}
|
||||
}
|
83
src/Eden/Interface/SettingsInterface.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef SETTINGSINTERFACE_H
|
||||
#define SETTINGSINTERFACE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "QMLSetting.h"
|
||||
#include "qt_common/shared_translation.h"
|
||||
#include "Models/SettingsModel.h"
|
||||
|
||||
namespace SettingsCategories {
|
||||
Q_NAMESPACE
|
||||
|
||||
enum class Category {
|
||||
Android = static_cast<u32>(Settings::Category::Android),
|
||||
Audio = static_cast<u32>(Settings::Category::Audio),
|
||||
Core = static_cast<u32>(Settings::Category::Core),
|
||||
Cpu = static_cast<u32>(Settings::Category::Cpu),
|
||||
CpuDebug = static_cast<u32>(Settings::Category::CpuDebug),
|
||||
CpuUnsafe = static_cast<u32>(Settings::Category::CpuUnsafe),
|
||||
Overlay = static_cast<u32>(Settings::Category::Overlay),
|
||||
Renderer = static_cast<u32>(Settings::Category::Renderer),
|
||||
RendererAdvanced = static_cast<u32>(Settings::Category::RendererAdvanced),
|
||||
RendererExtensions = static_cast<u32>(Settings::Category::RendererExtensions),
|
||||
RendererDebug = static_cast<u32>(Settings::Category::RendererDebug),
|
||||
System = static_cast<u32>(Settings::Category::System),
|
||||
SystemAudio = static_cast<u32>(Settings::Category::SystemAudio),
|
||||
DataStorage = static_cast<u32>(Settings::Category::DataStorage),
|
||||
Debugging = static_cast<u32>(Settings::Category::Debugging),
|
||||
DebuggingGraphics = static_cast<u32>(Settings::Category::DebuggingGraphics),
|
||||
GpuDriver = static_cast<u32>(Settings::Category::GpuDriver),
|
||||
Miscellaneous = static_cast<u32>(Settings::Category::Miscellaneous),
|
||||
Network = static_cast<u32>(Settings::Category::Network),
|
||||
WebService = static_cast<u32>(Settings::Category::WebService),
|
||||
AddOns = static_cast<u32>(Settings::Category::AddOns),
|
||||
Controls = static_cast<u32>(Settings::Category::Controls),
|
||||
Ui = static_cast<u32>(Settings::Category::Ui),
|
||||
UiAudio = static_cast<u32>(Settings::Category::UiAudio),
|
||||
UiGeneral = static_cast<u32>(Settings::Category::UiGeneral),
|
||||
UiLayout = static_cast<u32>(Settings::Category::UiLayout),
|
||||
UiGameList = static_cast<u32>(Settings::Category::UiGameList),
|
||||
Screenshots = static_cast<u32>(Settings::Category::Screenshots),
|
||||
Shortcuts = static_cast<u32>(Settings::Category::Shortcuts),
|
||||
Multiplayer = static_cast<u32>(Settings::Category::Multiplayer),
|
||||
Services = static_cast<u32>(Settings::Category::Services),
|
||||
Paths = static_cast<u32>(Settings::Category::Paths),
|
||||
Linux = static_cast<u32>(Settings::Category::Linux),
|
||||
LibraryApplet = static_cast<u32>(Settings::Category::LibraryApplet),
|
||||
MaxEnum = static_cast<u32>(Settings::Category::MaxEnum),
|
||||
};
|
||||
Q_ENUM_NS(Category)
|
||||
}
|
||||
|
||||
class SettingsInterface : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
public:
|
||||
explicit SettingsInterface(QObject* parent = nullptr);
|
||||
|
||||
QMLSetting *getSetting(Settings::BasicSetting *setting);
|
||||
Q_INVOKABLE QMLSetting *setting(const QString &key);
|
||||
Q_INVOKABLE SettingsModel *category(SettingsCategories::Category category,
|
||||
QList<QString> idInclude = {},
|
||||
QList<QString> idExclude = {});
|
||||
|
||||
Q_INVOKABLE int id(const QString &key);
|
||||
|
||||
bool global() const;
|
||||
void setGlobal(bool newGlobal);
|
||||
|
||||
signals:
|
||||
void globalChanged();
|
||||
|
||||
private:
|
||||
QMap<std::string, QMLSetting *> m_settings;
|
||||
|
||||
std::unique_ptr<ConfigurationShared::TranslationMap> translations;
|
||||
std::unique_ptr<ConfigurationShared::ComboboxTranslationMap> combobox_translations;
|
||||
|
||||
Q_PROPERTY(bool global READ global WRITE setGlobal NOTIFY globalChanged FINAL)
|
||||
};
|
||||
|
||||
#endif // SETTINGSINTERFACE_H
|
40
src/Eden/Interface/TitleManager.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "TitleManager.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
TitleManager::TitleManager(QObject *parent) {}
|
||||
|
||||
const QString TitleManager::title() const
|
||||
{
|
||||
static const std::string description = std::string(Common::g_build_version);
|
||||
static const std::string build_id = std::string(Common::g_build_id);
|
||||
static const std::string compiler = std::string(Common::g_compiler_id);
|
||||
|
||||
std::string yuzu_title;
|
||||
if (Common::g_is_dev_build) {
|
||||
yuzu_title = fmt::format("Eden Nightly | {}-{} | {}", description, build_id, compiler);
|
||||
} else {
|
||||
yuzu_title = fmt::format("Eden | {} | {}", description, compiler);
|
||||
}
|
||||
|
||||
const auto override_title = fmt::format(fmt::runtime(
|
||||
std::string(Common::g_title_bar_format_idle)),
|
||||
build_id);
|
||||
const auto window_title = override_title.empty() ? yuzu_title : override_title;
|
||||
|
||||
// TODO(crueter): Running
|
||||
|
||||
return QString::fromStdString(window_title);
|
||||
// if (title_name.empty()) {
|
||||
// return QString::fromStdString(window_title);
|
||||
// } else {
|
||||
// const auto run_title = [window_title, title_name, title_version, gpu_vendor]() {
|
||||
// if (title_version.empty()) {
|
||||
// return fmt::format("{} | {} | {}", window_title, title_name, gpu_vendor);
|
||||
// }
|
||||
// return fmt::format("{} | {} | {} | {}", window_title, title_name, title_version,
|
||||
// gpu_vendor);
|
||||
// }();
|
||||
// setWindowTitle(QString::fromStdString(run_title));
|
||||
// }
|
||||
}
|
18
src/Eden/Interface/TitleManager.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef TITLEMANAGER_H
|
||||
#define TITLEMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TitleManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
|
||||
public:
|
||||
explicit TitleManager(QObject *parent = nullptr);
|
||||
|
||||
const QString title() const;
|
||||
signals:
|
||||
void titleChanged();
|
||||
};
|
||||
|
||||
#endif // TITLEMANAGER_H
|
79
src/Eden/Items/AnimatedDialog.qml
Normal file
|
@ -0,0 +1,79 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Dialog {
|
||||
id: dia
|
||||
|
||||
property int preferredWidth: Overlay.overlay.width / 2
|
||||
property int preferredHeight: Overlay.overlay.height / 1.25
|
||||
|
||||
width: Math.min(preferredWidth, Overlay.overlay.width)
|
||||
height: Math.min(preferredHeight, Overlay.overlay.height)
|
||||
|
||||
property int radius: 12
|
||||
property bool colorful: false
|
||||
|
||||
anchors.centerIn: Overlay.overlay
|
||||
|
||||
enter: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
duration: 200
|
||||
|
||||
from: 0.0
|
||||
to: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
exit: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
duration: 200
|
||||
|
||||
from: 1.0
|
||||
to: 0.0
|
||||
}
|
||||
}
|
||||
|
||||
header: Rectangle {
|
||||
topLeftRadius: dia.radius
|
||||
topRightRadius: dia.radius
|
||||
|
||||
color: colorful ? Qt.alpha(Constants.accent, 0.5) : Constants.dialog
|
||||
|
||||
height: 50
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Math.round(25)
|
||||
|
||||
text: title
|
||||
color: Constants.text
|
||||
|
||||
font.bold: true
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: dia.radius
|
||||
|
||||
color: Constants.dialog
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: control
|
||||
|
||||
background: Item {}
|
||||
|
||||
delegate: Button {
|
||||
id: btn
|
||||
}
|
||||
}
|
||||
Overlay.modal: Item {}
|
||||
Overlay.modeless: Item {}
|
||||
}
|
59
src/Eden/Items/BetterMenu.qml
Normal file
|
@ -0,0 +1,59 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Menu {
|
||||
background: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 40
|
||||
color: Constants.button
|
||||
|
||||
radius: 10
|
||||
}
|
||||
|
||||
function fixAmpersands(originalText) {
|
||||
var regex = /&(\w)/g
|
||||
return originalText.replace(regex, "<u>$1</u>")
|
||||
}
|
||||
|
||||
delegate: MenuItem {
|
||||
id: control
|
||||
|
||||
font.pixelSize: 14
|
||||
|
||||
background: Rectangle {
|
||||
color: control.down || control.hovered
|
||||
|| control.highlighted ? Constants.buttonHighlighted : Constants.button
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Text {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 5 + (control.checkable ? control.indicator.width : 0)
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
text: fixAmpersands(control.text)
|
||||
color: Constants.text
|
||||
font: control.font
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 5
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Component.onCompleted: if (control.action != null
|
||||
&& typeof control.action.shortcut !== 'undefined')
|
||||
text = control.action.shortcut
|
||||
|
||||
color: Constants.text
|
||||
font: control.font
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
src/Eden/Items/BetterMenuBar.qml
Normal file
|
@ -0,0 +1,33 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
MenuBar {
|
||||
background: Rectangle {
|
||||
implicitHeight: 30
|
||||
color: Constants.button
|
||||
}
|
||||
|
||||
function fixAmpersands(originalText) {
|
||||
var regex = /&(\w)/g
|
||||
return originalText.replace(regex, "<u>$1</u>")
|
||||
}
|
||||
|
||||
delegate: MenuBarItem {
|
||||
id: control
|
||||
|
||||
font.pixelSize: 16
|
||||
|
||||
background: Rectangle {
|
||||
color: control.down || control.hovered
|
||||
|| control.highlighted ? Constants.buttonHighlighted : Constants.button
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
text: fixAmpersands(control.text)
|
||||
color: Constants.text
|
||||
font: control.font
|
||||
}
|
||||
}
|
||||
}
|
22
src/Eden/Items/CMakeLists.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
EdenModule(
|
||||
NAME Items
|
||||
URI Eden.Items
|
||||
QML_FILES
|
||||
StatusBarButton.qml
|
||||
BetterMenu.qml
|
||||
AnimatedDialog.qml
|
||||
BetterMenuBar.qml
|
||||
|
||||
SettingsTabButton.qml
|
||||
IconButton.qml
|
||||
VerticalTabBar.qml
|
||||
fields/BetterSpinBox.qml
|
||||
fields/BetterTextField.qml
|
||||
fields/FieldFooter.qml
|
||||
)
|
23
src/Eden/Items/IconButton.qml
Normal file
|
@ -0,0 +1,23 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Button {
|
||||
required property string label
|
||||
|
||||
bottomInset: 0
|
||||
topInset: 0
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
|
||||
width: icon.width
|
||||
height: icon.height
|
||||
|
||||
icon.source: "qrc:/icons/" + label.toLowerCase() + ".svg"
|
||||
icon.width: 45
|
||||
icon.height: 45
|
||||
icon.color: Constants.text
|
||||
|
||||
background: Item {}
|
||||
}
|
40
src/Eden/Items/SettingsTabButton.qml
Normal file
|
@ -0,0 +1,40 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
TabButton {
|
||||
required property string label
|
||||
|
||||
id: button
|
||||
|
||||
implicitHeight: 100
|
||||
width: 95
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
IconButton {
|
||||
label: button.label
|
||||
|
||||
Layout.maximumHeight: 60
|
||||
Layout.maximumWidth: 65
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
|
||||
onClicked: button.clicked()
|
||||
}
|
||||
|
||||
Text {
|
||||
font.pixelSize: 16
|
||||
text: label
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
|
||||
color: Constants.text
|
||||
}
|
||||
}
|
||||
|
||||
// background: Rectangle {
|
||||
// color: button.Material.backgroundColor
|
||||
// }
|
||||
}
|
36
src/Eden/Items/StatusBarButton.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
MouseArea {
|
||||
id: button
|
||||
|
||||
required property string text
|
||||
property color textColor: Constants.text
|
||||
|
||||
implicitHeight: 20
|
||||
implicitWidth: txt.width
|
||||
|
||||
hoverEnabled: true
|
||||
onHoveredChanged: rect.color = containsMouse ? Constants.buttonHighlighted : "transparent"
|
||||
|
||||
Rectangle {
|
||||
id: rect
|
||||
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
|
||||
Text {
|
||||
id: txt
|
||||
|
||||
font.pixelSize: 12
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
|
||||
color: button.textColor
|
||||
text: button.text
|
||||
}
|
||||
}
|
||||
}
|
46
src/Eden/Items/VerticalTabBar.qml
Normal file
|
@ -0,0 +1,46 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
TabBar {
|
||||
clip: true
|
||||
id: control
|
||||
|
||||
contentItem: ListView {
|
||||
model: control.contentModel
|
||||
currentIndex: control.currentIndex
|
||||
|
||||
spacing: control.spacing
|
||||
orientation: ListView.Vertical
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
flickableDirection: Flickable.AutoFlickIfNeeded
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
highlightMoveDuration: 300
|
||||
highlightRangeMode: ListView.ApplyRange
|
||||
preferredHighlightBegin: 40
|
||||
preferredHighlightEnd: height - 40
|
||||
|
||||
highlight: Item {
|
||||
z: 2
|
||||
Rectangle {
|
||||
radius: 5
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
height: parent.height / 2
|
||||
width: 5
|
||||
|
||||
color: Constants.accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: control.Material.backgroundColor
|
||||
radius: 8
|
||||
}
|
||||
}
|
68
src/Eden/Items/fields/BetterSpinBox.qml
Normal file
|
@ -0,0 +1,68 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Controls.impl
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
SpinBox {
|
||||
id: control
|
||||
property string label: ""
|
||||
|
||||
from: -0x7FFFFFFF
|
||||
to: 0x7FFFFFFF
|
||||
|
||||
contentItem: BetterTextField {
|
||||
text: parent.textFromValue(parent.value, parent.locale)
|
||||
|
||||
placeholderText: parent.label
|
||||
|
||||
width: parent.width
|
||||
|
||||
font: parent.font
|
||||
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
|
||||
onEditingFinished: {
|
||||
control.value = parseFloat(text.replace(/,/g, ""))
|
||||
valueModified()
|
||||
}
|
||||
}
|
||||
|
||||
up.indicator: IconLabel {
|
||||
icon {
|
||||
source: "qrc:/icons/forward.svg"
|
||||
}
|
||||
|
||||
x: control.mirrored ? 0 : control.width - width
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
height: parent.height
|
||||
width: height / 2
|
||||
}
|
||||
|
||||
down.indicator: IconLabel {
|
||||
icon {
|
||||
source: "qrc:/icons/back.svg"
|
||||
}
|
||||
|
||||
x: control.mirrored ? control.width - width : 0
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
height: parent.height
|
||||
width: height / 2
|
||||
}
|
||||
background: Item {}
|
||||
|
||||
FieldFooter {
|
||||
anchors {
|
||||
bottom: contentItem.bottom
|
||||
}
|
||||
}
|
||||
}
|
38
src/Eden/Items/fields/BetterTextField.qml
Normal file
|
@ -0,0 +1,38 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Items
|
||||
|
||||
TextField {
|
||||
property string suffix: ""
|
||||
|
||||
placeholderTextColor: enabled && activeFocus ? Constants.accent : Qt.darker(
|
||||
Constants.text, 1.3)
|
||||
|
||||
color: enabled ? Constants.text : Qt.darker(Constants.text, 1.5)
|
||||
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
|
||||
FieldFooter {}
|
||||
|
||||
horizontalAlignment: "AlignHCenter"
|
||||
|
||||
Text {
|
||||
id: txt
|
||||
text: suffix
|
||||
|
||||
font.pixelSize: 14
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
|
||||
rightMargin: 5
|
||||
}
|
||||
|
||||
color: "gray"
|
||||
}
|
||||
}
|
20
src/Eden/Items/fields/FieldFooter.qml
Normal file
|
@ -0,0 +1,20 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import Eden.Constants
|
||||
|
||||
Rectangle {
|
||||
height: 2
|
||||
color: enabled ? Constants.text : Qt.darker(Constants.text, 1.5)
|
||||
width: parent.width
|
||||
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
22
src/Eden/Main/CMakeLists.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
EdenModule(
|
||||
NAME Main
|
||||
URI Eden.Main
|
||||
QML_FILES
|
||||
Main.qml
|
||||
StatusBar.qml
|
||||
GameList.qml
|
||||
|
||||
GameGridCard.qml
|
||||
GameGrid.qml
|
||||
MarqueeText.qml
|
||||
|
||||
GameCarouselCard.qml
|
||||
GameCarousel.qml
|
||||
LIBRARIES Eden::Interface
|
||||
)
|
87
src/Eden/Main/GameCarousel.qml
Normal file
|
@ -0,0 +1,87 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform
|
||||
import QtCore
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Interface
|
||||
|
||||
ListView {
|
||||
id: carousel
|
||||
|
||||
focus: true
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
model: EdenGameList
|
||||
orientation: ListView.Horizontal
|
||||
clip: false
|
||||
flickDeceleration: 1500
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
spacing: 20
|
||||
|
||||
keyNavigationWraps: true
|
||||
|
||||
function increment() {
|
||||
incrementCurrentIndex()
|
||||
if (currentIndex === count)
|
||||
currentIndex = 0
|
||||
}
|
||||
|
||||
function decrement() {
|
||||
decrementCurrentIndex()
|
||||
if (currentIndex === -1)
|
||||
currentIndex = count - 1
|
||||
}
|
||||
|
||||
// TODO(crueter): handle move/displace/add (requires thread worker on game list and a bunch of other shit)
|
||||
Rectangle {
|
||||
id: hg
|
||||
clip: false
|
||||
z: 3
|
||||
|
||||
property var item: carousel.currentItem
|
||||
|
||||
anchors {
|
||||
centerIn: parent
|
||||
}
|
||||
|
||||
height: item === null ? 0 : item.height + 10
|
||||
width: item === null ? 0 : item.width + 10
|
||||
|
||||
color: "transparent"
|
||||
border {
|
||||
color: "deepskyblue"
|
||||
width: 4
|
||||
}
|
||||
|
||||
radius: 8
|
||||
|
||||
MarqueeText {
|
||||
id: container
|
||||
anchors.bottom: hg.top
|
||||
anchors.left: hg.left
|
||||
anchors.right: hg.right
|
||||
|
||||
canMarquee: true
|
||||
text: hg.item === null ? "" : toTitleCase(hg.item.title)
|
||||
|
||||
font.pixelSize: 22
|
||||
font.family: "Monospace"
|
||||
|
||||
color: "lightblue"
|
||||
background: Constants.bg
|
||||
}
|
||||
}
|
||||
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange
|
||||
preferredHighlightBegin: currentItem === null ? 0 : x + width / 2 - currentItem.width / 2
|
||||
preferredHighlightEnd: currentItem === null ? 0 : x + width / 2 + currentItem.width / 2
|
||||
|
||||
highlightMoveDuration: 300
|
||||
delegate: GameCarouselCard {
|
||||
id: game
|
||||
width: 300
|
||||
height: 300
|
||||
}
|
||||
}
|
39
src/Eden/Main/GameCarouselCard.qml
Normal file
|
@ -0,0 +1,39 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform
|
||||
import QtCore
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Item {
|
||||
property string title: model.name.replace(/-/g, " ")
|
||||
|
||||
id: wrapper
|
||||
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border {
|
||||
width: 4
|
||||
color: PathView.isCurrentItem ? "deepskyblue" : "transparent"
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "file://" + model.path
|
||||
|
||||
clip: true
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
|
||||
margins: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
src/Eden/Main/GameGrid.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform
|
||||
import QtCore
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Interface
|
||||
|
||||
GridView {
|
||||
property var setting
|
||||
id: grid
|
||||
|
||||
property int cellSize: Math.floor(width / setting.value)
|
||||
|
||||
highlightFollowsCurrentItem: true
|
||||
clip: true
|
||||
|
||||
cellWidth: cellSize
|
||||
cellHeight: cellSize + 20
|
||||
|
||||
model: EdenGameList
|
||||
|
||||
delegate: GameGridCard {
|
||||
id: game
|
||||
|
||||
width: grid.cellSize - 20
|
||||
height: grid.cellHeight - 20
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "transparent"
|
||||
z: 5
|
||||
|
||||
radius: 16
|
||||
border {
|
||||
color: "deepskyblue"
|
||||
width: 4
|
||||
}
|
||||
}
|
||||
|
||||
focus: true
|
||||
focusPolicy: "StrongFocus"
|
||||
}
|
79
src/Eden/Main/GameGridCard.qml
Normal file
|
@ -0,0 +1,79 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform
|
||||
import QtCore
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Rectangle {
|
||||
id: wrapper
|
||||
|
||||
color: Constants.dialog
|
||||
radius: 16
|
||||
|
||||
Image {
|
||||
id: image
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "file://" + model.path
|
||||
|
||||
clip: true
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: nameText.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
|
||||
margins: 10
|
||||
}
|
||||
|
||||
height: parent.height
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
hoverEnabled: true
|
||||
|
||||
z: 3
|
||||
|
||||
x: (parent.width - parent.paintedWidth) / 2
|
||||
y: (parent.height - parent.paintedHeight) / 2
|
||||
|
||||
width: parent.paintedWidth
|
||||
height: parent.paintedHeight
|
||||
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse) {
|
||||
wrapper.GridView.view.currentIndex = index
|
||||
wrapper.GridView.view.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MarqueeText {
|
||||
id: nameText
|
||||
clip: true
|
||||
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 5
|
||||
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
|
||||
leftMargin: 5
|
||||
rightMargin: 5
|
||||
}
|
||||
|
||||
text: toTitleCase(model.name.replace(/-/g, " "))
|
||||
|
||||
font.pixelSize: 18
|
||||
font.family: "Monospace"
|
||||
|
||||
color: "lightblue"
|
||||
background: Constants.dialog
|
||||
|
||||
canMarquee: wrapper.GridView.isCurrentItem
|
||||
}
|
||||
}
|
127
src/Eden/Main/GameList.qml
Normal file
|
@ -0,0 +1,127 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform
|
||||
import QtCore
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Interface
|
||||
|
||||
// import Eden.Native.Gamepad
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property var setting: SettingsInterface.setting("grid_columns")
|
||||
|
||||
property int gx: 0
|
||||
property int gy: 0
|
||||
|
||||
readonly property int deadzone: 8000
|
||||
readonly property int repeatTimeMs: 125
|
||||
|
||||
color: Constants.bg
|
||||
|
||||
// TODO: use the original yuzu backend for dis
|
||||
// Gamepad {
|
||||
// id: gamepad
|
||||
|
||||
// // onUpPressed: grid.moveCurrentIndexUp()
|
||||
// // onDownPressed: grid.moveCurrentIndexDown()
|
||||
// // onLeftPressed: grid.moveCurrentIndexLeft()
|
||||
// // onRightPressed: grid.moveCurrentIndexRight()
|
||||
// onLeftPressed: carousel.decrement()
|
||||
// onRightPressed: carousel.increment()
|
||||
// onAPressed: console.log("A pressed")
|
||||
// onLeftStickMoved: (x, y) => {
|
||||
// gx = x
|
||||
// gy = y
|
||||
// }
|
||||
// }
|
||||
|
||||
// Timer {
|
||||
// interval: repeatTimeMs
|
||||
// running: true
|
||||
// repeat: true
|
||||
// onTriggered: {
|
||||
// if (gx > deadzone) {
|
||||
// gamepad.rightPressed()
|
||||
// } else if (gx < -deadzone) {
|
||||
// gamepad.leftPressed()
|
||||
// }
|
||||
|
||||
// if (gy > deadzone) {
|
||||
// gamepad.downPressed()
|
||||
// } else if (gy < -deadzone) {
|
||||
// gamepad.upPressed()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Timer {
|
||||
// interval: 16
|
||||
// running: true
|
||||
// repeat: true
|
||||
// onTriggered: gamepad.pollEvents()
|
||||
// }
|
||||
FolderDialog {
|
||||
id: openDir
|
||||
folder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
onAccepted: {
|
||||
button.visible = false
|
||||
view.anchors.bottom = root.bottom
|
||||
EdenGameList.addDir(folder)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: view
|
||||
|
||||
anchors {
|
||||
bottom: button.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
margins: 8
|
||||
}
|
||||
|
||||
GameGrid {
|
||||
setting: root.setting
|
||||
|
||||
id: grid
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
// GameCarousel {
|
||||
// id: carousel
|
||||
|
||||
// height: 300
|
||||
|
||||
// anchors {
|
||||
// right: view.right
|
||||
// left: view.left
|
||||
|
||||
// verticalCenter: view.verticalCenter
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
Button {
|
||||
id: button
|
||||
font.pixelSize: 25
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
|
||||
bottom: parent.bottom
|
||||
|
||||
margins: 8
|
||||
}
|
||||
|
||||
text: "Add Directory"
|
||||
onClicked: openDir.open()
|
||||
|
||||
background: Rectangle {
|
||||
color: button.pressed ? Constants.accentPressed : Constants.accent
|
||||
radius: 5
|
||||
}
|
||||
}
|
||||
}
|
207
src/Eden/Main/Main.qml
Normal file
|
@ -0,0 +1,207 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import Eden.Config
|
||||
import Eden.Items
|
||||
import Eden.Constants
|
||||
|
||||
ApplicationWindow {
|
||||
width: Constants.width
|
||||
height: Constants.height
|
||||
visible: true
|
||||
title: TitleManager.title
|
||||
|
||||
Material.theme: Material.Dark
|
||||
Material.accent: Material.Red
|
||||
|
||||
Material.roundedScale: Material.NotRounded
|
||||
|
||||
GameList {
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: status.top
|
||||
}
|
||||
}
|
||||
|
||||
/** Dialogs */
|
||||
GlobalConfigureDialog {
|
||||
id: globalConfig
|
||||
}
|
||||
|
||||
menuBar: BetterMenuBar {
|
||||
BetterMenu {
|
||||
title: qsTr("&File")
|
||||
contentWidth: 225
|
||||
|
||||
Action {
|
||||
text: qsTr("&Install files to NAND...")
|
||||
}
|
||||
MenuSeparator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("L&oad File...")
|
||||
shortcut: "Ctrl+O"
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Load &Folder...")
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
BetterMenu {
|
||||
title: "&Recent Files"
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("Load/Remove &Amiibo...")
|
||||
shortcut: "F2"
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("Open &eden Directory")
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("E&xit")
|
||||
shortcut: "Ctrl+Q"
|
||||
}
|
||||
}
|
||||
BetterMenu {
|
||||
title: qsTr("&Emulation")
|
||||
contentWidth: 240
|
||||
|
||||
Action {
|
||||
text: qsTr("&Pause")
|
||||
shortcut: "F4"
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("&Stop")
|
||||
shortcut: "F5"
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("&Restart")
|
||||
shortcut: "F6"
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("Con&figure...")
|
||||
shortcut: "Ctrl+,"
|
||||
onTriggered: globalConfig.open()
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Configure &Current Game...")
|
||||
shortcut: "Ctrl+."
|
||||
}
|
||||
}
|
||||
|
||||
BetterMenu {
|
||||
title: qsTr("&View")
|
||||
contentWidth: 260
|
||||
|
||||
Action {
|
||||
text: qsTr("F&ullscreen")
|
||||
shortcut: "F11"
|
||||
checkable: true
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Single &Window Mode")
|
||||
checkable: true
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Display D&ock Widget Headers")
|
||||
checkable: true
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Show &Filter Bar")
|
||||
shortcut: "Ctrl+F"
|
||||
checkable: true
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Show &Status Bar")
|
||||
shortcut: "Ctrl+S"
|
||||
checkable: true
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
}
|
||||
|
||||
BetterMenu {
|
||||
title: qsTr("&Tools")
|
||||
contentWidth: 225
|
||||
|
||||
Action {
|
||||
text: qsTr("Install &Decryption Keys")
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Install &Firmware")
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("&Verify Installed Contents")
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
BetterMenu {
|
||||
title: qsTr("&Amiibo")
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Open A&lbum")
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Open &Mii Editor")
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Open Co&ntroller Menu")
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Open &Home Menu")
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("&Capture Screenshot")
|
||||
shortcut: "Ctrl+P"
|
||||
}
|
||||
|
||||
BetterMenu {
|
||||
title: "&TAS"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBar {
|
||||
id: status
|
||||
|
||||
height: 30
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
129
src/Eden/Main/MarqueeText.qml
Normal file
|
@ -0,0 +1,129 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Eden.Constants
|
||||
|
||||
Item {
|
||||
id: container
|
||||
|
||||
anchors {}
|
||||
|
||||
height: txt.contentHeight
|
||||
clip: true
|
||||
|
||||
// TODO(crueter): util?
|
||||
function toTitleCase(str) {
|
||||
return str.replace(/\w\S*/g, text => text.charAt(0).toUpperCase(
|
||||
) + text.substring(1).toLowerCase())
|
||||
}
|
||||
|
||||
property string text
|
||||
property string spacing: " "
|
||||
property string combined: text + spacing
|
||||
property string display: animate ? combined.substring(
|
||||
step) + combined.substring(
|
||||
0, step) : text
|
||||
property int step: 0
|
||||
property bool animate: canMarquee && txt.contentWidth > parent.width
|
||||
|
||||
property bool canMarquee: false
|
||||
|
||||
property font font
|
||||
property color color
|
||||
property color background
|
||||
|
||||
onCanMarqueeChanged: checkMarquee()
|
||||
|
||||
function checkMarquee() {
|
||||
if (canMarquee && txt.contentWidth > width) {
|
||||
step = 0
|
||||
delay.start()
|
||||
} else {
|
||||
delay.stop()
|
||||
marquee.stop()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: marquee
|
||||
|
||||
interval: 150
|
||||
running: false
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
parent.step = (parent.step + 1) % parent.combined.length
|
||||
if (parent.step === 0) {
|
||||
stop()
|
||||
delay.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delay
|
||||
interval: 1500
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
marquee.start()
|
||||
}
|
||||
}
|
||||
|
||||
// fake container to gauge contentWidth
|
||||
Text {
|
||||
id: txt
|
||||
visible: false
|
||||
text: parent.text
|
||||
font: container.font
|
||||
|
||||
onContentWidthChanged: container.checkMarquee()
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
|
||||
color: container.color
|
||||
font: container.font
|
||||
text: parent.display
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
z: 2
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: marquee.running ? container.background : "transparent"
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.1
|
||||
color: "transparent"
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.9
|
||||
color: "transparent"
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: marquee.running ? container.background : "transparent"
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
160
src/Eden/Main/StatusBar.qml
Normal file
|
@ -0,0 +1,160 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Eden.Constants
|
||||
import Eden.Items
|
||||
|
||||
ToolBar {
|
||||
id: toolbar
|
||||
|
||||
property string graphicsBackend: "vulkan"
|
||||
property string gpuAccuracy: "high"
|
||||
property string consoleMode: "docked"
|
||||
property int adapting: 5
|
||||
property int antialiasing: 0
|
||||
property int volume: 100
|
||||
|
||||
property string firmwareVersion: "16.0.3"
|
||||
|
||||
implicitHeight: 30
|
||||
|
||||
background: Rectangle {
|
||||
color: Constants.bg
|
||||
}
|
||||
|
||||
// TODO: reduce duplicate code
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
|
||||
leftMargin: 10
|
||||
}
|
||||
|
||||
StatusBarButton {
|
||||
property alias value: toolbar.graphicsBackend
|
||||
|
||||
text: value.toUpperCase()
|
||||
|
||||
textColor: value === "vulkan" ? "orange" : "lightblue"
|
||||
|
||||
onClicked: {
|
||||
if (value === "vulkan") {
|
||||
value = "opengl"
|
||||
} else {
|
||||
value = "vulkan"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBarButton {
|
||||
property alias value: toolbar.gpuAccuracy
|
||||
|
||||
text: value.toUpperCase()
|
||||
|
||||
textColor: value === "high" ? "orange" : "lightgreen"
|
||||
|
||||
onClicked: {
|
||||
if (value === "high") {
|
||||
value = "normal"
|
||||
} else {
|
||||
value = "high"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBarButton {
|
||||
property alias value: toolbar.consoleMode
|
||||
|
||||
text: value.toUpperCase()
|
||||
|
||||
textColor: Constants.text
|
||||
|
||||
onClicked: {
|
||||
if (value === "docked") {
|
||||
value = "handheld"
|
||||
} else {
|
||||
value = "docked"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBarButton {
|
||||
property list<string> choices: ["nearest", "bilinear", "bicubic", "gaussian", "scaleforce", "fsr"]
|
||||
|
||||
property alias index: toolbar.adapting
|
||||
property string value: choices[index]
|
||||
|
||||
text: value.toUpperCase()
|
||||
|
||||
onClicked: {
|
||||
let newIndex = index + 1
|
||||
if (newIndex >= choices.length) {
|
||||
newIndex = 0
|
||||
}
|
||||
|
||||
index = newIndex
|
||||
}
|
||||
}
|
||||
|
||||
StatusBarButton {
|
||||
property list<string> choices: ["no aa", "fxaa", "msaa"]
|
||||
|
||||
property alias index: toolbar.antialiasing
|
||||
property string value: choices[index]
|
||||
|
||||
text: value.toUpperCase()
|
||||
|
||||
onClicked: {
|
||||
let newIndex = index + 1
|
||||
if (newIndex >= choices.length) {
|
||||
newIndex = 0
|
||||
}
|
||||
|
||||
index = newIndex
|
||||
}
|
||||
}
|
||||
|
||||
StatusBarButton {
|
||||
id: volumeButton
|
||||
|
||||
property alias value: toolbar.volume
|
||||
|
||||
text: "VOLUME: " + value + "%"
|
||||
|
||||
onClicked: {
|
||||
volumeSlider.open()
|
||||
}
|
||||
|
||||
onWheel: wheel => {
|
||||
value += wheel.angleDelta.y / 120
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: volumeSlider
|
||||
|
||||
width: 200
|
||||
height: 50
|
||||
|
||||
x: volumeButton.x
|
||||
y: volumeButton.y - height
|
||||
|
||||
focus: true
|
||||
|
||||
Slider {
|
||||
value: volumeButton.value
|
||||
onMoved: volumeButton.value = value
|
||||
|
||||
from: 0
|
||||
to: 200
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
17
src/Eden/Models/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
qt_add_library(EdenModels STATIC
|
||||
GameListModel.h GameListModel.cpp
|
||||
SettingsModel.h SettingsModel.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(EdenModels
|
||||
PRIVATE
|
||||
Qt6::Gui
|
||||
)
|
||||
|
||||
add_library(Eden::Models ALIAS EdenModels)
|
67
src/Eden/Models/GameListModel.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "GameListModel.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
|
||||
const QStringList GameListModel::ValidSuffixes{"jpg", "png", "webp", "jpeg"};
|
||||
|
||||
GameListModel::GameListModel(QObject *parent) {
|
||||
QHash<int, QByteArray> rez = QStandardItemModel::roleNames();
|
||||
rez.insert(GLMRoleTypes::NAME, "name");
|
||||
rez.insert(GLMRoleTypes::PATH, "path");
|
||||
rez.insert(GLMRoleTypes::FILESIZE, "size");
|
||||
|
||||
QStandardItemModel::setItemRoleNames(rez);
|
||||
}
|
||||
|
||||
QVariant GameListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role == GLMRoleTypes::NAME) {
|
||||
return itemFromIndex(index)->text();
|
||||
}
|
||||
|
||||
return QStandardItemModel::data(index, role);
|
||||
}
|
||||
|
||||
void GameListModel::addDir(const QString &toAdd)
|
||||
{
|
||||
QString name = toAdd;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
name.replace("file:///", "");
|
||||
#else
|
||||
name.replace("file://", "");
|
||||
#endif
|
||||
|
||||
m_dirs << name;
|
||||
reload();
|
||||
}
|
||||
|
||||
void GameListModel::removeDir(const QString &toRemove)
|
||||
{
|
||||
m_dirs.removeAll(toRemove);
|
||||
reload();
|
||||
}
|
||||
|
||||
void GameListModel::reload()
|
||||
{
|
||||
clear();
|
||||
for (const QString &dir : std::as_const(m_dirs)) {
|
||||
qDebug() << dir;
|
||||
for (const auto &entry : QDirListing(dir, QDirListing::IteratorFlag::FilesOnly)) {
|
||||
if (ValidSuffixes.contains(entry.completeSuffix().toLower())) {
|
||||
QString path = entry.absoluteFilePath();
|
||||
QString name = entry.baseName();
|
||||
qreal size = entry.size();
|
||||
QString sizeString = QLocale::system().formattedDataSize(size);
|
||||
|
||||
QStandardItem *game = new QStandardItem(name);
|
||||
game->setData(path, GLMRoleTypes::PATH);
|
||||
game->setData(sizeString, GLMRoleTypes::FILESIZE);
|
||||
|
||||
invisibleRootItem()->appendRow(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
src/Eden/Models/GameListModel.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef GAMELISTMODEL_H
|
||||
#define GAMELISTMODEL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
typedef struct Game {
|
||||
QString absPath;
|
||||
QString name;
|
||||
QString fileSize;
|
||||
} Game;
|
||||
|
||||
class GameListModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum GLMRoleTypes {
|
||||
NAME = Qt::UserRole + 1,
|
||||
PATH,
|
||||
FILESIZE
|
||||
};
|
||||
|
||||
GameListModel(QObject *parent = nullptr);
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
Q_INVOKABLE void addDir(const QString &toAdd);
|
||||
Q_INVOKABLE void removeDir(const QString &toRemove);
|
||||
|
||||
static const QStringList ValidSuffixes;
|
||||
|
||||
private:
|
||||
QStringList m_dirs;
|
||||
QList<Game> m_data;
|
||||
|
||||
void reload();
|
||||
};
|
||||
|
||||
#endif // GAMELISTMODEL_H
|
90
src/Eden/Models/SettingsModel.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include "SettingsModel.h"
|
||||
|
||||
SettingsModel::SettingsModel(QObject* parent) : QAbstractListModel(parent) {}
|
||||
|
||||
int SettingsModel::rowCount(const QModelIndex& parent) const {
|
||||
return m_data.count();
|
||||
}
|
||||
|
||||
QVariant SettingsModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
QMLSetting *s = m_data[index.row()];
|
||||
|
||||
switch (role) {
|
||||
case LABEL:
|
||||
return s->label();
|
||||
case TOOLTIP:
|
||||
return s->tooltip();
|
||||
case VALUE:
|
||||
return s->value();
|
||||
case ID:
|
||||
return s->id();
|
||||
case TYPE:
|
||||
return s->type();
|
||||
case COMBO:
|
||||
return s->combo();
|
||||
case SUFFIX:
|
||||
return s->suffix();
|
||||
case MIN:
|
||||
return s->min();
|
||||
case MAX:
|
||||
return s->max();
|
||||
case OTHER:
|
||||
return QVariant::fromValue(s->other());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (data(index, role) != value) {
|
||||
QMLSetting *s = m_data[index.row()];
|
||||
|
||||
switch (role) {
|
||||
case VALUE:
|
||||
s->setValue(value);
|
||||
|
||||
break;
|
||||
}
|
||||
emit dataChanged(index, index, {role});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void SettingsModel::append(QMLSetting *setting)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_data << setting;
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void SettingsModel::append(QList<QMLSetting *> settings)
|
||||
{
|
||||
for (QMLSetting *setting : settings) {
|
||||
append(setting);
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> SettingsModel::roleNames() const
|
||||
{
|
||||
QHash<int,QByteArray> rez;
|
||||
rez[LABEL] = "label";
|
||||
rez[TOOLTIP] = "tooltip";
|
||||
rez[VALUE] = "value";
|
||||
rez[ID] = "id";
|
||||
rez[TYPE] = "type";
|
||||
rez[COMBO] = "combo";
|
||||
rez[SUFFIX] = "suffix";
|
||||
rez[MIN] = "min";
|
||||
rez[MAX] = "max";
|
||||
rez[OTHER] = "other";
|
||||
|
||||
return rez;
|
||||
}
|
42
src/Eden/Models/SettingsModel.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef SETTINGSMODEL_H
|
||||
#define SETTINGSMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include "Interface/QMLSetting.h"
|
||||
|
||||
class SettingsModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum SMTypes {
|
||||
LABEL = Qt::UserRole + 1,
|
||||
TOOLTIP,
|
||||
ID,
|
||||
VALUE,
|
||||
TYPE,
|
||||
COMBO, // combobox translations
|
||||
SUFFIX,
|
||||
MIN,
|
||||
MAX,
|
||||
OTHER
|
||||
};
|
||||
|
||||
explicit SettingsModel(QObject* parent = nullptr);
|
||||
|
||||
// Basic functionality:
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
|
||||
void append(QMLSetting *setting);
|
||||
void append(QList<QMLSetting *> settings);
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
QList<QMLSetting *> m_data;
|
||||
};
|
||||
|
||||
#endif // SETTINGSMODEL_H
|
65
src/Eden/Native/CMakeLists.txt
Normal file
|
@ -0,0 +1,65 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
qt_add_executable(eden
|
||||
main.cpp
|
||||
icons.qrc
|
||||
)
|
||||
|
||||
set(MODULES
|
||||
Eden::Util
|
||||
Eden::Items
|
||||
Eden::Config
|
||||
Eden::Interface
|
||||
Eden::Constants
|
||||
Eden::Main
|
||||
Eden::Models
|
||||
)
|
||||
|
||||
# if (ENABLE_SDL2)
|
||||
# add_subdirectory(Gamepad)
|
||||
# set(MODULES ${MODULES} Eden::Gamepad)
|
||||
# endif()
|
||||
|
||||
target_link_libraries(eden
|
||||
PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Widgets
|
||||
Qt6::Gui
|
||||
Qt6::Quick
|
||||
Qt6::QuickControls2
|
||||
|
||||
${MODULES}
|
||||
)
|
||||
|
||||
target_link_libraries(eden PRIVATE common core input_common frontend_common qt_common network video_core)
|
||||
target_link_libraries(eden PRIVATE Boost::headers glad fmt)
|
||||
target_link_libraries(eden PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
target_link_libraries(eden PRIVATE Vulkan::Headers)
|
||||
|
||||
target_compile_definitions(eden PRIVATE
|
||||
# Use QStringBuilder for string concatenation to reduce
|
||||
# the overall number of temporary strings created.
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
|
||||
# Disable implicit type narrowing in signal/slot connect() calls.
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||
|
||||
# Disable unsafe overloads of QProcess' start() function.
|
||||
-DQT_NO_PROCESS_COMBINED_ARGUMENT_START
|
||||
|
||||
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
||||
-DQT_NO_URL_CAST_FROM_STRING
|
||||
)
|
||||
|
||||
set_target_properties(eden PROPERTIES OUTPUT_NAME "eden")
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS eden
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
11
src/Eden/Native/Gamepad/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
EdenModule(
|
||||
NAME Gamepad
|
||||
URI Eden.Native.Gamepad
|
||||
SOURCES gamepad.h gamepad.cpp
|
||||
)
|
79
src/Eden/Native/Gamepad/gamepad.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "gamepad.h"
|
||||
|
||||
// TODO(crueter): This is just temporary
|
||||
Gamepad::Gamepad(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
SDL_Init(SDL_INIT_GAMECONTROLLER);
|
||||
}
|
||||
|
||||
Gamepad::~Gamepad()
|
||||
{
|
||||
if (controller)
|
||||
SDL_GameControllerClose(controller);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void Gamepad::openController(int deviceIndex)
|
||||
{
|
||||
if (controller) {
|
||||
closeController();
|
||||
}
|
||||
|
||||
controller = SDL_GameControllerOpen(deviceIndex);
|
||||
}
|
||||
|
||||
void Gamepad::closeController()
|
||||
{
|
||||
if (controller) {
|
||||
SDL_GameControllerClose(controller);
|
||||
controller = nullptr;
|
||||
}
|
||||
}
|
||||
void Gamepad::pollEvents()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
openController(event.cdevice.which);
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
if (controller
|
||||
&& event.cdevice.which
|
||||
== SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))) {
|
||||
closeController();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
switch (event.cbutton.button) {
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP:
|
||||
emit upPressed();
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
|
||||
emit downPressed();
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
|
||||
emit leftPressed();
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
|
||||
emit rightPressed();
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_A:
|
||||
emit aPressed();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
if (event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX
|
||||
|| event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY) {
|
||||
int x = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
int y = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
emit leftStickMoved(x, y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
31
src/Eden/Native/Gamepad/gamepad.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include <QElapsedTimer>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
class Gamepad : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
public:
|
||||
explicit Gamepad(QObject *parent = nullptr);
|
||||
~Gamepad();
|
||||
|
||||
Q_INVOKABLE void pollEvents();
|
||||
|
||||
signals:
|
||||
void upPressed();
|
||||
void downPressed();
|
||||
void leftPressed();
|
||||
void rightPressed();
|
||||
void aPressed();
|
||||
|
||||
void leftStickMoved(int x, int y);
|
||||
|
||||
private:
|
||||
SDL_GameController *controller = nullptr;
|
||||
|
||||
void closeController();
|
||||
void openController(int deviceIndex);
|
||||
};
|
15
src/Eden/Native/icons.qrc
Normal file
|
@ -0,0 +1,15 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/audio.svg</file>
|
||||
<file>icons/controls.svg</file>
|
||||
<file>icons/cpu.svg</file>
|
||||
<file>icons/general.svg</file>
|
||||
<file>icons/graphics.svg</file>
|
||||
<file>icons/system.svg</file>
|
||||
<file>icons/debug.svg</file>
|
||||
<file>icons/forward.svg</file>
|
||||
<file>icons/back.svg</file>
|
||||
<file>icons/help.svg</file>
|
||||
<file alias="icons/eden.svg">../../../dist/dev.eden_emu.eden.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
1
src/Eden/Native/icons/audio.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131ZM120-360v-240h160l200-200v640L280-360H120Zm440 40v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320ZM400-606l-86 86H200v80h114l86 86v-252ZM300-480Z"/></svg>
|
After Width: | Height: | Size: 378 B |
39
src/Eden/Native/icons/back.svg
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="20"
|
||||
viewBox="0 -960 227.99999 400"
|
||||
width="11.4"
|
||||
fill="#ffffff"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="back.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="24.291667"
|
||||
inkscape:cx="24.061749"
|
||||
inkscape:cy="17.413379"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
d="M 200,-560 228,-588.5 56.5,-760 228,-931.5 200,-960 0,-760 Z"
|
||||
id="path1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.5" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
src/Eden/Native/icons/controls.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M189-160q-60 0-102.5-43T42-307q0-9 1-18t3-18l84-336q14-54 57-87.5t98-33.5h390q55 0 98 33.5t57 87.5l84 336q2 9 3.5 18.5T919-306q0 61-43.5 103.5T771-160q-42 0-78-22t-54-60l-28-58q-5-10-15-15t-21-5H385q-11 0-21 5t-15 15l-28 58q-18 38-54 60t-78 22Zm3-80q19 0 34.5-10t23.5-27l28-57q15-31 44-48.5t63-17.5h190q34 0 63 18t45 48l28 57q8 17 23.5 27t34.5 10q28 0 48-18.5t21-46.5q0 1-2-19l-84-335q-7-27-28-44t-49-17H285q-28 0-49.5 17T208-659l-84 335q-2 6-2 18 0 28 20.5 47t49.5 19Zm348-280q17 0 28.5-11.5T580-560q0-17-11.5-28.5T540-600q-17 0-28.5 11.5T500-560q0 17 11.5 28.5T540-520Zm80-80q17 0 28.5-11.5T660-640q0-17-11.5-28.5T620-680q-17 0-28.5 11.5T580-640q0 17 11.5 28.5T620-600Zm0 160q17 0 28.5-11.5T660-480q0-17-11.5-28.5T620-520q-17 0-28.5 11.5T580-480q0 17 11.5 28.5T620-440Zm80-80q17 0 28.5-11.5T740-560q0-17-11.5-28.5T700-600q-17 0-28.5 11.5T660-560q0 17 11.5 28.5T700-520Zm-360 60q13 0 21.5-8.5T370-490v-40h40q13 0 21.5-8.5T440-560q0-13-8.5-21.5T410-590h-40v-40q0-13-8.5-21.5T340-660q-13 0-21.5 8.5T310-630v40h-40q-13 0-21.5 8.5T240-560q0 13 8.5 21.5T270-530h40v40q0 13 8.5 21.5T340-460Zm140-20Z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
src/Eden/Native/icons/cpu.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M360-360v-240h240v240H360Zm80-80h80v-80h-80v80Zm-80 320v-80h-80q-33 0-56.5-23.5T200-280v-80h-80v-80h80v-80h-80v-80h80v-80q0-33 23.5-56.5T280-760h80v-80h80v80h80v-80h80v80h80q33 0 56.5 23.5T760-680v80h80v80h-80v80h80v80h-80v80q0 33-23.5 56.5T680-200h-80v80h-80v-80h-80v80h-80Zm320-160v-400H280v400h400ZM480-480Z"/></svg>
|
After Width: | Height: | Size: 435 B |
1
src/Eden/Native/icons/debug.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-200q66 0 113-47t47-113v-160q0-66-47-113t-113-47q-66 0-113 47t-47 113v160q0 66 47 113t113 47Zm-80-120h160v-80H400v80Zm0-160h160v-80H400v80Zm80 40Zm0 320q-65 0-120.5-32T272-240H160v-80h84q-3-20-3.5-40t-.5-40h-80v-80h80q0-20 .5-40t3.5-40h-84v-80h112q14-23 31.5-43t40.5-35l-64-66 56-56 86 86q28-9 57-9t57 9l88-86 56 56-66 66q23 15 41.5 34.5T688-640h112v80h-84q3 20 3.5 40t.5 40h80v80h-80q0 20-.5 40t-3.5 40h84v80H688q-32 56-87.5 88T480-120Z"/></svg>
|
After Width: | Height: | Size: 566 B |
39
src/Eden/Native/icons/forward.svg
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="20"
|
||||
viewBox="0 -960 227.99999 400"
|
||||
width="11.4"
|
||||
fill="#ffffff"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="forward.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="24.291667"
|
||||
inkscape:cx="24.020583"
|
||||
inkscape:cy="24"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
d="M 28,-560 0,-588.5 171.5,-760 0,-931.5 28,-960 228,-760 Z"
|
||||
id="path1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.5" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
src/Eden/Native/icons/general.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>
|
After Width: | Height: | Size: 771 B |
60
src/Eden/Native/icons/graphics.svg
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
height="800px"
|
||||
width="800px"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="graphics.svg"
|
||||
inkscape:version="1.4.1 (93de688d07, 2025-03-30)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs5" /><sodipodi:namedview
|
||||
id="namedview5"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.52947631"
|
||||
inkscape:cx="404.17295"
|
||||
inkscape:cy="224.75038"
|
||||
inkscape:window-width="956"
|
||||
inkscape:window-height="1150"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<g
|
||||
id="g5"
|
||||
style="fill:#000000;fill-opacity:1">
|
||||
<g
|
||||
id="g4"
|
||||
style="fill:#000000;fill-opacity:1">
|
||||
<g
|
||||
id="g3"
|
||||
style="fill:#000000;fill-opacity:1">
|
||||
<path
|
||||
d="M490.667,96H85.333V74.667c0-11.782-9.551-21.333-21.333-21.333H21.333C9.551,53.333,0,62.885,0,74.667 C0,86.449,9.551,96,21.333,96h21.333v21.333v256V416c0,11.782,9.551,21.333,21.333,21.333c11.782,0,21.333-9.551,21.333-21.333 v-21.333H128v42.667c0,11.782,9.551,21.333,21.333,21.333c11.782,0,21.333-9.551,21.333-21.333v-42.667h42.667v42.667 c0,11.782,9.551,21.333,21.333,21.333c11.782,0,21.333-9.551,21.333-21.333v-42.667h42.667v42.667 c0,11.782,9.551,21.333,21.333,21.333s21.333-9.551,21.333-21.333v-42.667h149.333c11.782,0,21.333-9.551,21.333-21.333v-256 C512,105.551,502.449,96,490.667,96z M469.333,352h-384V138.667h384V352z"
|
||||
id="path1"
|
||||
style="fill:#000000;fill-opacity:1" />
|
||||
<path
|
||||
d="M362.667,330.667c47.131,0,85.333-38.202,85.333-85.333S409.798,160,362.667,160s-85.333,38.202-85.333,85.333 S315.535,330.667,362.667,330.667z M362.667,202.667c23.567,0,42.667,19.099,42.667,42.667S386.234,288,362.667,288 S320,268.901,320,245.333S339.099,202.667,362.667,202.667z"
|
||||
id="path2"
|
||||
style="fill:#000000;fill-opacity:1" />
|
||||
<path
|
||||
d="M192,330.667h42.667c11.782,0,21.333-9.551,21.333-21.333v-128c0-11.782-9.551-21.333-21.333-21.333H192 c-47.131,0-85.333,38.202-85.333,85.333S144.869,330.667,192,330.667z M192,202.667h21.333V288H192 c-23.567,0-42.667-19.099-42.667-42.667S168.433,202.667,192,202.667z"
|
||||
id="path3"
|
||||
style="fill:#000000;fill-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
1
src/Eden/Native/icons/help.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#FFFFFF"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
After Width: | Height: | Size: 695 B |
1
src/Eden/Native/icons/system.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M560-200q17 0 28.5-11.5T600-240q0-17-11.5-28.5T560-280q-17 0-28.5 11.5T520-240q0 17 11.5 28.5T560-200Zm120 0q17 0 28.5-11.5T720-240q0-17-11.5-28.5T680-280q-17 0-28.5 11.5T640-240q0 17 11.5 28.5T680-200ZM120-440v-360q0-33 23.5-56.5T200-880h560q33 0 56.5 23.5T840-800v360h-80v-360H200v360h-80Zm80 80v200h560v-200H200Zm0 280q-33 0-56.5-23.5T120-160v-280h720v280q0 33-23.5 56.5T760-80H200Zm0-360h560-560Zm0 80h560-560Z"/></svg>
|
After Width: | Height: | Size: 539 B |
69
src/Eden/Native/main.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include "Interface/QMLConfig.h"
|
||||
#include "Interface/SettingsInterface.h"
|
||||
#include "Interface/TitleManager.h"
|
||||
#include "Models/GameListModel.h"
|
||||
#include "core/core.h"
|
||||
|
||||
#include <QQuickStyle>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QQuickStyle::setStyle(QObject::tr("Material"));
|
||||
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("eden-emu"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("eden"));
|
||||
QApplication::setDesktopFileName(QStringLiteral("org.eden-emu.eden"));
|
||||
QGuiApplication::setWindowIcon(QIcon(":/icons/eden.svg"));
|
||||
|
||||
/// Settings, etc
|
||||
Settings::SetConfiguringGlobal(true);
|
||||
QMLConfig *config = new QMLConfig;
|
||||
|
||||
// // TODO: Save all values on launch and per game etc
|
||||
// app.connect(&app, &QCoreApplication::aboutToQuit, &app, [config]() {
|
||||
// config->save();
|
||||
// });
|
||||
|
||||
/// Expose Enums
|
||||
|
||||
// Core
|
||||
std::unique_ptr<Core::System> system = std::make_unique<Core::System>();
|
||||
|
||||
/// CONTEXT
|
||||
QQmlApplicationEngine engine;
|
||||
auto ctx = engine.rootContext();
|
||||
|
||||
ctx->setContextProperty(QStringLiteral("QtConfig"), QVariant::fromValue(config));
|
||||
|
||||
// Enums
|
||||
qmlRegisterUncreatableMetaObject(SettingsCategories::staticMetaObject, "Eden.Interface", 1, 0, "SettingsCategories", QString());
|
||||
|
||||
// Directory List
|
||||
GameListModel *gameListModel = new GameListModel(&app);
|
||||
ctx->setContextProperty(QStringLiteral("EdenGameList"), gameListModel);
|
||||
|
||||
// Settings Interface
|
||||
SettingsInterface *interface = new SettingsInterface(&engine);
|
||||
ctx->setContextProperty(QStringLiteral("SettingsInterface"), interface);
|
||||
|
||||
// Title Manager
|
||||
TitleManager *title = new TitleManager(&engine);
|
||||
ctx->setContextProperty(QStringLiteral("TitleManager"), title);
|
||||
|
||||
/// LOAD
|
||||
QObject::connect(
|
||||
&engine,
|
||||
&QQmlApplicationEngine::objectCreationFailed,
|
||||
&app,
|
||||
[]() { QCoreApplication::exit(-1); },
|
||||
Qt::QueuedConnection);
|
||||
|
||||
engine.loadFromModule("Eden.Main", "Main");
|
||||
|
||||
return app.exec();
|
||||
}
|
17
src/Eden/Util/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set_source_files_properties(Util.qml
|
||||
PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE true
|
||||
)
|
||||
|
||||
EdenModule(
|
||||
NAME Util
|
||||
URI Eden.Util
|
||||
QML_FILES
|
||||
Util.qml
|
||||
)
|