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"
|
echo "Getting branch changes"
|
||||||
|
|
||||||
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
# BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||||
COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
|
# COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
|
||||||
RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
|
# RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
|
||||||
FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
|
# 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)
|
#FILES=$(git diff --name-only master)
|
||||||
|
|
||||||
|
|
2
.gitignore
vendored
|
@ -53,3 +53,5 @@ eden-windows-msvc
|
||||||
artifacts
|
artifacts
|
||||||
*.AppImage*
|
*.AppImage*
|
||||||
/install*
|
/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):
|
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
|
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>)
|
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||||
|
|
||||||
# Set compilation flags
|
# Set compilation flags
|
||||||
if (MSVC AND NOT CXX_CLANG)
|
if (MSVC)
|
||||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||||
|
|
||||||
# Silence "deprecation" warnings
|
# 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
|
# 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.
|
# 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.
|
# Ensure that projects are built with Unicode support.
|
||||||
add_compile_definitions(UNICODE _UNICODE)
|
add_definitions(-DUNICODE -D_UNICODE)
|
||||||
|
|
||||||
# /W4 - Level 4 warnings
|
# /W4 - Level 4 warnings
|
||||||
# /MP - Multi-threaded compilation
|
# /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: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.
|
/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
|
/we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||||
/we4189 # 'identifier': local variable is initialized but not referenced
|
/we4189 # 'identifier': local variable is initialized but not referenced
|
||||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
/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)
|
/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)
|
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
|
# 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
|
# 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_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
||||||
else()
|
else()
|
||||||
if (NOT MSVC)
|
|
||||||
add_compile_options(
|
|
||||||
-fwrapv
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
|
-fwrapv
|
||||||
|
|
||||||
-Werror=all
|
-Werror=all
|
||||||
-Werror=extra
|
-Werror=extra
|
||||||
-Werror=missing-declarations
|
|
||||||
-Werror=shadow
|
-Werror=shadow
|
||||||
-Werror=unused
|
-Werror=unused
|
||||||
|
|
||||||
|
@ -141,19 +132,14 @@ else()
|
||||||
-Wno-missing-field-initializers
|
-Wno-missing-field-initializers
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CXX_CLANG OR CXX_ICC) # Clang or AppleClang
|
if (CMAKE_CXX_COMPILER_ID MATCHES Clang OR CMAKE_CXX_COMPILER_ID MATCHES IntelLLVM) # Clang or AppleClang
|
||||||
if (NOT MSVC)
|
|
||||||
add_compile_options(
|
|
||||||
-Werror=shadow-uncaptured-local
|
|
||||||
-Werror=implicit-fallthrough
|
|
||||||
-Werror=type-limits
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Wno-braced-scalar-init
|
-Wno-braced-scalar-init
|
||||||
-Wno-unused-private-field
|
-Wno-unused-private-field
|
||||||
-Wno-nullability-completeness
|
-Wno-nullability-completeness
|
||||||
|
-Werror=shadow-uncaptured-local
|
||||||
|
-Werror=implicit-fallthrough
|
||||||
|
-Werror=type-limits
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -161,12 +147,12 @@ else()
|
||||||
add_compile_options("-mcx16")
|
add_compile_options("-mcx16")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE AND CXX_CLANG)
|
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||||
add_compile_options("-stdlib=libc++")
|
add_compile_options("-stdlib=libc++")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# GCC bugs
|
# 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
|
# These diagnostics would be great if they worked, but are just completely broken
|
||||||
# and produce bogus errors on external libraries like fmt.
|
# and produce bogus errors on external libraries like fmt.
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
|
@ -182,15 +168,15 @@ else()
|
||||||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||||
# by setting _FILE_OFFSET_BITS.
|
# by setting _FILE_OFFSET_BITS.
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||||
add_compile_definitions(_FILE_OFFSET_BITS=64)
|
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
add_compile_definitions(MINGW_HAS_SECURE_API)
|
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||||
add_compile_options("-msse4.1")
|
add_compile_options("-msse4.1")
|
||||||
|
|
||||||
if (MINGW_STATIC_BUILD)
|
if (MINGW_STATIC_BUILD)
|
||||||
add_compile_definitions(QT_STATICPLUGIN)
|
add_definitions(-DQT_STATICPLUGIN)
|
||||||
add_compile_options("-static")
|
add_compile_options("-static")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -234,7 +220,9 @@ if (YUZU_ROOM_STANDALONE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
add_subdirectory(yuzu)
|
add_definitions(-DYUZU_QT_WIDGETS)
|
||||||
|
add_subdirectory(qt_common)
|
||||||
|
add_subdirectory(Eden)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
|
@ -245,5 +233,3 @@ if (ANDROID)
|
||||||
add_subdirectory(android/app/src/main/jni)
|
add_subdirectory(android/app/src/main/jni)
|
||||||
target_include_directories(yuzu-android PRIVATE android/app/src/main)
|
target_include_directories(yuzu-android PRIVATE android/app/src/main)
|
||||||
endif()
|
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
|
||||||
|
)
|