Compare commits

...

4 commits

Author SHA1 Message Date
bf4dbef16c
carousel/list view
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-17 00:29:05 -04:00
a8c7b007be
Working configuration
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-16 21:53:19 -04:00
fba3a7e6f6
tmp
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-16 20:55:36 -04:00
a99fd021d7
merge
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-16 19:05:20 -04:00
102 changed files with 5711 additions and 2 deletions

1
.gitignore vendored
View file

@ -44,3 +44,4 @@ eden-windows-msvc
artifacts
*.AppImage*
*.patch
.cache

View file

@ -114,7 +114,6 @@ else()
-Werror=all
-Werror=extra
-Werror=missing-declarations
-Werror=shadow
-Werror=unused
@ -212,7 +211,7 @@ if (YUZU_ROOM_STANDALONE)
endif()
if (ENABLE_QT)
add_subdirectory(yuzu)
add_subdirectory(eden)
endif()
if (ENABLE_WEB_SERVICE)

82
src/eden/.gitignore vendored Normal file
View file

@ -0,0 +1,82 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
*.qbs.user*
CMakeLists.txt.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
# Directories with generated files
.moc/
.obj/
.pch/
.rcc/
.uic/
/build*/

83
src/eden/CMakeLists.txt Normal file
View file

@ -0,0 +1,83 @@
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)
qt_add_executable(yuzu
main.cpp
icons.qrc
)
add_subdirectory(qml)
add_subdirectory(interface)
add_subdirectory(models)
set(PLUGINS
edenMainplugin
edenItemsplugin
edenConfigplugin
edenInterfaceplugin
edenConstantsplugin
edenUtilplugin
)
if (ENABLE_SDL2)
add_subdirectory(gamepad)
set(PLUGINS ${PLUGINS} edenGamepadplugin)
endif()
target_link_libraries(yuzu
PRIVATE
Qt6::Core
Qt6::Widgets
Qt6::Gui
Qt6::Quick
Qt6::QuickControls2
edenModels
# Explicitly link to static plugins
${PLUGINS}
)
set(NATIVE_MODULES yuzu edenInterface)
foreach(MODULE ${NATIVE_MODULES})
target_link_libraries(${MODULE} PRIVATE common core input_common frontend_common network video_core)
target_link_libraries(${MODULE} PRIVATE Boost::headers glad fmt)
target_link_libraries(${MODULE} PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
target_link_libraries(${MODULE} PRIVATE Vulkan::Headers)
if (NOT WIN32)
target_include_directories(${MODULE} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(${MODULE} PRIVATE Qt6::DBus)
endif()
target_compile_definitions(${MODULE} 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
)
endforeach()
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
include(GNUInstallDirs)
install(TARGETS yuzu
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

View file

@ -0,0 +1,9 @@
set(CMAKE_AUTOMOC ON)
qt_add_library(edenGamepad STATIC)
qt_add_qml_module(edenGamepad
URI org.eden_emu.gamepad
VERSION 1.0
SOURCES gamepad.h gamepad.cpp
)

View file

@ -0,0 +1,78 @@
#include "gamepad.h"
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;
}
}
}

View 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);
};

14
src/eden/icons.qrc Normal file
View file

@ -0,0 +1,14 @@
<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>
</qresource>
</RCC>

1
src/eden/icons/audio.svg Normal file
View 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/icons/back.svg Normal file
View 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

View 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/icons/cpu.svg Normal file
View 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/icons/debug.svg Normal file
View 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

View 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

View 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

View 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/icons/help.svg Normal file
View 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

View 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

View file

@ -0,0 +1,14 @@
qt_add_library(edenInterface STATIC)
qt_add_qml_module(edenInterface
URI org.eden_emu.interface
VERSION 1.0
SOURCES
SettingsInterface.h SettingsInterface.cpp
uisettings.h uisettings.cpp
qt_config.h qt_config.cpp
shared_translation.h shared_translation.cpp
QMLSetting.h QMLSetting.cpp
MetaObjectHelper.h
SOURCES QMLConfig.h
)

View 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

View file

@ -0,0 +1,26 @@
#ifndef QMLCONFIG_H
#define QMLCONFIG_H
#include "eden/interface/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

View file

@ -0,0 +1,261 @@
#include "QMLSetting.h"
#include "common/settings.h"
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();
}

View file

@ -0,0 +1,105 @@
#ifndef QMLSETTING_H
#define QMLSETTING_H
#include <QObject>
#include <QtQmlIntegration>
#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

View file

@ -0,0 +1,124 @@
#include "SettingsInterface.h"
#include "common/settings.h"
#include "common/logging/log.h"
#include "uisettings.h"
SettingsInterface::SettingsInterface(QObject* parent)
: QObject{parent}
, translations{ConfigurationShared::InitializeTranslations(parent)}
, combobox_translations{ConfigurationShared::ComboboxEnumeration(parent)}
{}
// TODO: idExclude
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);
}
}

View file

@ -0,0 +1,84 @@
#ifndef SETTINGSINTERFACE_H
#define SETTINGSINTERFACE_H
#include <QObject>
#include <QtQmlIntegration/QtQmlIntegration>
#include "QMLSetting.h"
#include "shared_translation.h"
#include "eden/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_SINGLETON
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

View file

@ -0,0 +1,562 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "qt_config.h"
#include "common/logging/log.h"
#include "input_common/main.h"
#include "uisettings.h"
const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = {
Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
Qt::Key_Q, Qt::Key_E,
};
const std::array<int, Settings::NativeMotion::NumMotions> QtConfig::default_motions = {
Qt::Key_7,
Qt::Key_8,
};
const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{
{
Qt::Key_W,
Qt::Key_S,
Qt::Key_A,
Qt::Key_D,
},
{
Qt::Key_I,
Qt::Key_K,
Qt::Key_J,
Qt::Key_L,
},
}};
const std::array<int, 2> QtConfig::default_stick_mod = {
Qt::Key_Shift,
0,
};
const std::array<int, 2> QtConfig::default_ringcon_analogs{{
Qt::Key_A,
Qt::Key_D,
}};
QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type)
: Config(config_type) {
Initialize(config_name);
if (config_type != ConfigType::InputProfile) {
ReadQtValues();
SaveQtValues();
}
}
QtConfig::~QtConfig() {
if (global) {
QtConfig::SaveAllValues();
}
}
void QtConfig::ReloadAllValues() {
Reload();
ReadQtValues();
SaveQtValues();
}
void QtConfig::SaveAllValues() {
SaveValues();
SaveQtValues();
}
void QtConfig::ReadQtValues() {
if (global) {
ReadUIValues();
}
ReadQtControlValues();
}
void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
std::string player_prefix;
if (type != ConfigType::InputProfile) {
player_prefix.append("player_").append(ToString(player_index)).append("_");
}
auto& player = Settings::values.players.GetValue()[player_index];
if (IsCustomConfig()) {
const auto profile_name =
ReadStringSetting(std::string(player_prefix).append("profile_name"));
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true)[player_index];
player.profile_name = "";
return;
}
}
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
auto& player_buttons = player.buttons[i];
player_buttons = ReadStringSetting(
std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
if (player_buttons.empty()) {
player_buttons = default_param;
}
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& player_analogs = player.analogs[i];
player_analogs = ReadStringSetting(
std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
if (player_analogs.empty()) {
player_analogs = default_param;
}
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
auto& player_motions = player.motions[i];
player_motions = ReadStringSetting(
std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
if (player_motions.empty()) {
player_motions = default_param;
}
}
}
void QtConfig::ReadHidbusValues() {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
auto& ringcon_analogs = Settings::values.ringcon_analogs;
ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
if (ringcon_analogs.empty()) {
ringcon_analogs = default_param;
}
}
void QtConfig::ReadDebugControlValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
debug_pad_buttons = ReadStringSetting(
std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
if (debug_pad_buttons.empty()) {
debug_pad_buttons = default_param;
}
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
debug_pad_analogs = ReadStringSetting(
std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
if (debug_pad_analogs.empty()) {
debug_pad_analogs = default_param;
}
}
}
void QtConfig::ReadQtControlValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
Settings::values.players.SetGlobal(!IsCustomConfig());
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
ReadQtPlayerValues(p);
}
if (IsCustomConfig()) {
EndGroup();
return;
}
ReadDebugControlValues();
ReadHidbusValues();
EndGroup();
}
void QtConfig::ReadPathValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
UISettings::values.game_dir_deprecated =
ReadStringSetting(std::string("gameListRootDir"), std::string("."));
UISettings::values.game_dir_deprecated_deepscan =
ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false));
const int gamedirs_size = BeginArray(std::string("gamedirs"));
for (int i = 0; i < gamedirs_size; ++i) {
SetArrayIndex(i);
UISettings::GameDir game_dir;
game_dir.path = ReadStringSetting(std::string("path"));
game_dir.deep_scan =
ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false));
game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true));
UISettings::values.game_dirs.append(game_dir);
}
EndArray();
// Create NAND and SD card directories if empty, these are not removable through the UI,
// also carries over old game list settings if present
if (UISettings::values.game_dirs.empty()) {
UISettings::GameDir game_dir;
game_dir.path = std::string("SDMC");
game_dir.expanded = true;
UISettings::values.game_dirs.append(game_dir);
game_dir.path = std::string("UserNAND");
UISettings::values.game_dirs.append(game_dir);
game_dir.path = std::string("SysNAND");
UISettings::values.game_dirs.append(game_dir);
if (UISettings::values.game_dir_deprecated != std::string(".")) {
game_dir.path = UISettings::values.game_dir_deprecated;
game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
UISettings::values.game_dirs.append(game_dir);
}
}
UISettings::values.recent_files =
QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
.split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
ReadCategory(Settings::Category::Paths);
EndGroup();
}
void QtConfig::ReadShortcutValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) {
BeginGroup(group);
BeginGroup(name);
// No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
// for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
// a file dialog in windowed mode
UISettings::values.shortcuts.push_back(
{name,
group,
{ReadStringSetting(std::string("KeySeq"), shortcut.keyseq),
ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq),
shortcut.context,
ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}});
EndGroup(); // name
EndGroup(); // group
}
EndGroup();
}
void QtConfig::ReadUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
UISettings::values.theme = ReadStringSetting(
std::string("theme"),
std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second));
ReadUIGamelistValues();
ReadUILayoutValues();
ReadPathValues();
ReadScreenshotValues();
ReadShortcutValues();
ReadMultiplayerValues();
ReadCategory(Settings::Category::Ui);
ReadCategory(Settings::Category::UiGeneral);
EndGroup();
}
void QtConfig::ReadUIGamelistValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
ReadCategory(Settings::Category::UiGameList);
// const int favorites_size = BeginArray("favorites");
// for (int i = 0; i < favorites_size; i++) {
// SetArrayIndex(i);
// UISettings::values.favorited_ids.append(
// ReadUnsignedIntegerSetting(std::string("program_id")));
// }
// EndArray();
EndGroup();
}
void QtConfig::ReadUILayoutValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
ReadCategory(Settings::Category::UiLayout);
EndGroup();
}
void QtConfig::ReadMultiplayerValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer));
ReadCategory(Settings::Category::Multiplayer);
// Read ban list back
int size = BeginArray(std::string("username_ban_list"));
UISettings::values.multiplayer_ban_list.first.resize(size);
for (int i = 0; i < size; ++i) {
SetArrayIndex(i);
UISettings::values.multiplayer_ban_list.first[i] =
ReadStringSetting(std::string("username"), std::string(""));
}
EndArray();
size = BeginArray(std::string("ip_ban_list"));
UISettings::values.multiplayer_ban_list.second.resize(size);
for (int i = 0; i < size; ++i) {
UISettings::values.multiplayer_ban_list.second[i] =
ReadStringSetting("username", std::string(""));
}
EndArray();
EndGroup();
}
void QtConfig::SaveQtValues()
{
if (global) {
LOG_DEBUG(Config, "Saving global Qt configuration values");
SaveUIValues();
} else {
LOG_DEBUG(Config, "Saving Qt configuration values");
}
SaveQtControlValues();
WriteToIni();
}
void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
std::string player_prefix;
if (type != ConfigType::InputProfile) {
player_prefix = std::string("player_").append(ToString(player_index)).append("_");
}
const auto& player = Settings::values.players.GetValue()[player_index];
if (IsCustomConfig() && player.profile_name.empty()) {
// No custom profile selected
return;
}
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
player.buttons[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
player.analogs[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
player.motions[i], std::make_optional(default_param));
}
}
void QtConfig::SaveDebugControlValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
Settings::values.debug_pad_buttons[i],
std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
Settings::values.debug_pad_analogs[i],
std::make_optional(default_param));
}
}
void QtConfig::SaveHidbusValues() {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
std::make_optional(default_param));
}
void QtConfig::SaveQtControlValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
Settings::values.players.SetGlobal(!IsCustomConfig());
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
SaveQtPlayerValues(p);
}
if (IsCustomConfig()) {
EndGroup();
return;
}
SaveDebugControlValues();
SaveHidbusValues();
EndGroup();
}
void QtConfig::SavePathValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
WriteCategory(Settings::Category::Paths);
WriteStringSetting(std::string("romsPath"), UISettings::values.roms_path);
BeginArray(std::string("gamedirs"));
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i);
const auto& game_dir = UISettings::values.game_dirs[i];
WriteStringSetting(std::string("path"), game_dir.path);
WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
std::make_optional(false));
WriteBooleanSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
}
EndArray();
WriteStringSetting(std::string("recentFiles"),
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
EndGroup();
}
void QtConfig::SaveShortcutValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
// Lengths of UISettings::values.shortcuts & default_hotkeys are same.
// However, their ordering must also be the same.
for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) {
const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut;
BeginGroup(group);
BeginGroup(name);
WriteStringSetting(std::string("KeySeq"), shortcut.keyseq,
std::make_optional(default_hotkey.keyseq));
WriteStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
std::make_optional(default_hotkey.controller_keyseq));
WriteIntegerSetting(std::string("Context"), shortcut.context,
std::make_optional(default_hotkey.context));
WriteBooleanSetting(std::string("Repeat"), shortcut.repeat,
std::make_optional(default_hotkey.repeat));
EndGroup(); // name
EndGroup(); // group
}
EndGroup();
}
void QtConfig::SaveUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
WriteCategory(Settings::Category::Ui);
WriteCategory(Settings::Category::UiGeneral);
WriteStringSetting(
std::string("theme"), UISettings::values.theme,
std::make_optional(std::string(
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
SaveUIGamelistValues();
SaveUILayoutValues();
SavePathValues();
SaveScreenshotValues();
SaveShortcutValues();
SaveMultiplayerValues();
EndGroup();
}
void QtConfig::SaveUIGamelistValues()
{
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
WriteCategory(Settings::Category::UiGameList);
// BeginArray(std::string("favorites"));
// for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
// SetArrayIndex(i);
// WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
// }
// EndArray(); // favorites
EndGroup();
}
void QtConfig::SaveUILayoutValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout));
WriteCategory(Settings::Category::UiLayout);
EndGroup();
}
void QtConfig::SaveMultiplayerValues() {
BeginGroup(std::string("Multiplayer"));
WriteCategory(Settings::Category::Multiplayer);
// Write ban list
BeginArray(std::string("username_ban_list"));
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
WriteStringSetting(std::string("username"),
UISettings::values.multiplayer_ban_list.first[i]);
}
EndArray(); // username_ban_list
BeginArray(std::string("ip_ban_list"));
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
WriteStringSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
}
EndArray(); // ip_ban_list
EndGroup();
}
std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (!map[category].empty()) {
return map[category];
}
return UISettings::values.linkage.by_category[category];
}
void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) {
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
ReadPlayerValues(player_index);
ReadQtPlayerValues(player_index);
EndGroup();
}
void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) {
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
LOG_DEBUG(Config, "Saving players control configuration values");
SavePlayerValues(player_index);
SaveQtPlayerValues(player_index);
EndGroup();
WriteToIni();
}

View file

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QMetaType>
#include "frontend_common/config.h"
class QtConfig final : public Config {
public:
explicit QtConfig(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig);
~QtConfig() override;
void ReloadAllValues() override;
void SaveAllValues() override;
void ReadQtControlPlayerValues(std::size_t player_index);
void SaveQtControlPlayerValues(std::size_t player_index);
protected:
void ReadQtValues();
void ReadQtPlayerValues(std::size_t player_index);
void ReadQtControlValues();
void ReadHidbusValues() override;
void ReadDebugControlValues() override;
void ReadPathValues() override;
void ReadShortcutValues() override;
void ReadUIValues() override;
void ReadUIGamelistValues() override;
void ReadUILayoutValues() override;
void ReadMultiplayerValues() override;
void SaveQtValues();
void SaveQtPlayerValues(std::size_t player_index);
void SaveQtControlValues();
void SaveHidbusValues() override;
void SaveDebugControlValues() override;
void SavePathValues() override;
void SaveShortcutValues() override;
void SaveUIValues() override;
void SaveUIGamelistValues() override;
void SaveUILayoutValues() override;
void SaveMultiplayerValues() override;
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
public:
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<int, 2> default_stick_mod;
static const std::array<int, 2> default_ringcon_analogs;
};

View file

@ -0,0 +1,692 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "eden/interface/shared_translation.h"
#include <QCoreApplication>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "common/time_zone.h"
#include "yuzu/uisettings.h"
#include <map>
#include <memory>
#include <tuple>
#include <utility>
namespace ConfigurationShared {
std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
{
std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>();
const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
// A setting can be ignored by giving it a blank name
// Applets
INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QString());
INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QString());
INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QString());
INSERT(Settings, error_applet_mode, tr("Error"), QString());
INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QString());
INSERT(Settings, player_select_applet_mode, tr("Player select"), QString());
INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QString());
INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QString());
INSERT(Settings, web_applet_mode, tr("Online web"), QString());
INSERT(Settings, shop_applet_mode, tr("Shop"), QString());
INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QString());
INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QString());
INSERT(Settings, login_share_applet_mode, tr("Login share"), QString());
INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QString());
INSERT(Settings, my_page_applet_mode, tr("My page"), QString());
// Audio
INSERT(Settings, sink_id, tr("Output Engine:"), QString());
INSERT(Settings, audio_output_device_id, tr("Output Device:"), QString());
INSERT(Settings, audio_input_device_id, tr("Input Device:"), QString());
INSERT(Settings, audio_muted, tr("Mute audio"), QString());
INSERT(Settings, volume, tr("Volume:"), QString());
INSERT(Settings, dump_audio_commands, QString(), QString());
INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"), QString());
// Core
INSERT(
Settings,
use_multi_core,
tr("Multicore CPU Emulation"),
tr("This option increases CPU emulation thread use from 1 to the Switchs maximum of 4.\n"
"This is mainly a debug option and shouldnt be disabled."));
INSERT(
Settings,
memory_layout_mode,
tr("Memory Layout"),
tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the "
"developer kit's 8/6GB.\nIts doesnt improve stability or performance and is intended "
"to let big texture mods fit in emulated RAM.\nEnabling it will increase memory "
"use. It is not recommended to enable unless a specific game with a texture mod needs "
"it."));
INSERT(Settings, use_speed_limit, QString(), QString());
INSERT(Settings,
speed_limit,
tr("Limit Speed Percent"),
tr("Controls the game's maximum rendering speed, but its up to each game if it runs "
"faster or not.\n200% for a 30 FPS game is 60 FPS, and for a "
"60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the "
"maximum your PC can reach."));
INSERT(Settings,
sync_core_speed,
tr("Synchronize Core Speed"),
tr("Synchronizes CPU core speed with the game's maximum rendering speed to boost FPS "
"without affecting game speed (animations, physics, etc.).\n"
"Compatibility varies by game; many (especially older ones) may not respond well.\n"
"Can help reduce stuttering at lower framerates."));
// Cpu
INSERT(Settings,
cpu_accuracy,
tr("Accuracy:"),
tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless "
"you know what you are doing."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString());
INSERT(Settings, use_fast_cpu_time, QString(), QString());
INSERT(Settings,
fast_cpu_time,
tr("Fast CPU Time"),
tr("Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see reduced performance, "
"and certain games may behave improperly.\nUse Boost (1700MHz) to run at the Switch's highest native "
"clock, or Fast (2000MHz) to run at 2x clock."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString());
// Cpu Debug
// Cpu Unsafe
INSERT(
Settings,
cpuopt_unsafe_unfuse_fma,
tr("Unfuse FMA (improve performance on CPUs without FMA)"),
tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on "
"CPUs without native FMA support."));
INSERT(
Settings,
cpuopt_unsafe_reduce_fp_error,
tr("Faster FRSQRTE and FRECPE"),
tr("This option improves the speed of some approximate floating-point functions by using "
"less accurate native approximations."));
INSERT(Settings,
cpuopt_unsafe_ignore_standard_fpcr,
tr("Faster ASIMD instructions (32 bits only)"),
tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes."));
INSERT(Settings,
cpuopt_unsafe_inaccurate_nan,
tr("Inaccurate NaN handling"),
tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "
"accuracy of certain floating-point instructions."));
INSERT(Settings,
cpuopt_unsafe_fastmem_check,
tr("Disable address space checks"),
tr("This option improves speed by eliminating a safety check before every memory "
"read/write in guest.\nDisabling it may allow a game to read/write the emulator's "
"memory."));
INSERT(
Settings,
cpuopt_unsafe_ignore_global_monitor,
tr("Ignore global monitor"),
tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
"safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
"other race conditions."));
// Renderer
INSERT(
Settings,
renderer_backend,
tr("API:"),
tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases."));
INSERT(Settings,
vulkan_device,
tr("Device:"),
tr("This setting selects the GPU to use with the Vulkan backend."));
INSERT(Settings,
shader_backend,
tr("Shader Backend:"),
tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in "
"performance and the best in rendering accuracy.\n"
"GLASM is a deprecated NVIDIA-only backend that offers much better shader building "
"performance at the cost of FPS and rendering accuracy.\n"
"SPIR-V compiles the fastest, but yields poor results on most GPU drivers."));
INSERT(Settings,
resolution_setup,
tr("Resolution:"),
tr("Forces the game to render at a different resolution.\nHigher resolutions require "
"much more VRAM and bandwidth.\n"
"Options lower than 1X can cause rendering issues."));
INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QString());
INSERT(Settings,
fsr_sharpening_slider,
tr("FSR Sharpness:"),
tr("Determines how sharpened the image will look while using FSRs dynamic contrast."));
INSERT(Settings,
anti_aliasing,
tr("Anti-Aliasing Method:"),
tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a "
"lower performance impact and can produce a better and more stable picture under "
"very low resolutions."));
INSERT(Settings,
fullscreen_mode,
tr("Fullscreen Mode:"),
tr("The method used to render the window in fullscreen.\nBorderless offers the best "
"compatibility with the on-screen keyboard that some games request for "
"input.\nExclusive "
"fullscreen may offer better performance and better Freesync/Gsync support."));
INSERT(Settings,
aspect_ratio,
tr("Aspect Ratio:"),
tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support "
"16:9, so custom game mods are required to get other ratios.\nAlso controls the "
"aspect ratio of captured screenshots."));
INSERT(Settings,
use_disk_shader_cache,
tr("Use disk pipeline cache"),
tr("Allows saving shaders to storage for faster loading on following game "
"boots.\nDisabling "
"it is only intended for debugging."));
INSERT(Settings,
optimize_spirv_output,
tr("Optimize SPIRV output shader"),
tr("Runs an additional optimization pass over generated SPIRV shaders.\n"
"Will increase time required for shader compilation.\nMay slightly improve "
"performance.\nThis feature is experimental."));
INSERT(
Settings,
use_asynchronous_gpu_emulation,
tr("Use asynchronous GPU emulation"),
tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
INSERT(Settings,
nvdec_emulation,
tr("NVDEC emulation:"),
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
"decoding, or perform no decoding at all (black screen on videos).\n"
"In most cases, GPU decoding provides the best performance."));
INSERT(Settings,
accelerate_astc,
tr("ASTC Decoding Method:"),
tr("This option controls how ASTC textures should be decoded.\n"
"CPU: Use the CPU for decoding, slowest but safest method.\n"
"GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most "
"games and users.\n"
"CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely "
"eliminates ASTC decoding\nstuttering at the cost of rendering issues while the "
"texture is being decoded."));
INSERT(
Settings,
astc_recompression,
tr("ASTC Recompression Method:"),
tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing "
"the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
"This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
"negatively affecting image quality."));
INSERT(Settings,
vram_usage_mode,
tr("VRAM Usage Mode:"),
tr("Selects whether the emulator should prefer to conserve memory or make maximum usage "
"of available video memory for performance. Has no effect on integrated graphics. "
"Aggressive mode may severely impact the performance of other applications such as "
"recording software."));
INSERT(
Settings,
vsync_mode,
tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from "
"a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop "
"frames.\nImmediate (no synchronization) just presents whatever is available and can "
"exhibit tearing."));
INSERT(Settings, bg_red, QString(), QString());
INSERT(Settings, bg_green, QString(), QString());
INSERT(Settings, bg_blue, QString(), QString());
// Renderer (Advanced Graphics)
INSERT(Settings,
async_presentation,
tr("Enable asynchronous presentation (Vulkan only)"),
tr("Slightly improves performance by moving presentation to a separate CPU thread."));
INSERT(
Settings,
renderer_force_max_clock,
tr("Force maximum clocks (Vulkan only)"),
tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed."));
INSERT(Settings,
max_anisotropy,
tr("Anisotropic Filtering:"),
tr("Controls the quality of texture rendering at oblique angles.\nIts a light setting "
"and safe to set at 16x on most GPUs."));
INSERT(Settings,
gpu_accuracy,
tr("Accuracy Level:"),
tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still "
"required for some.\nParticles tend to only render correctly with High "
"accuracy.\nExtreme should only be used for debugging.\nThis option can "
"be changed while playing.\nSome games may require booting on high to render "
"properly."));
INSERT(Settings,
use_asynchronous_shaders,
tr("Use asynchronous shader building (Hack)"),
tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis "
"feature "
"is experimental."));
INSERT(Settings, use_fast_gpu_time, QString(), QString());
INSERT(Settings,
fast_gpu_time,
tr("Fast GPU Time (Hack)"),
tr("Overclocks the emulated GPU to increase dynamic resolution and render "
"distance.\nUse 128 for maximal performance and 512 for maximal graphics fidelity."));
INSERT(Settings,
use_vulkan_driver_pipeline_cache,
tr("Use Vulkan pipeline cache"),
tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally."));
INSERT(
Settings,
enable_compute_pipelines,
tr("Enable Compute Pipelines (Intel Vulkan Only)"),
tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
"proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
"on all other drivers."));
INSERT(
Settings,
use_reactive_flushing,
tr("Enable Reactive Flushing"),
tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
"syncing."));
INSERT(Settings,
use_video_framerate,
tr("Sync to framerate of video playback"),
tr("Run the game at normal speed during video playback, even when the framerate is "
"unlocked."));
INSERT(Settings,
barrier_feedback_loops,
tr("Barrier feedback loops"),
tr("Improves rendering of transparency effects in specific games."));
// Renderer (Extensions)
INSERT(Settings,
dyna_state,
tr("Extended Dynamic State"),
tr("Enables the VkExtendedDynamicState* extensions.\nHigher dynamic states will "
"generally improve "
"performance, but may cause issues on certain games or devices."));
INSERT(Settings,
provoking_vertex,
tr("Provoking Vertex"),
tr("Improves lighting and vertex handling in certain games.\n"
"Only Vulkan 1.0+ devices support this extension."));
INSERT(Settings,
descriptor_indexing,
tr("Descriptor Indexing"),
tr("Improves texture & buffer handling and the Maxwell translation layer.\n"
"Some Vulkan 1.1+ and all 1.2+ devices support this extension."));
// Renderer (Debug)
// System
INSERT(Settings,
rng_seed,
tr("RNG Seed"),
tr("Controls the seed of the random number generator.\nMainly used for speedrunning "
"purposes."));
INSERT(Settings, rng_seed_enabled, QString(), QString());
INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch."));
INSERT(Settings,
custom_rtc,
tr("Custom RTC Date:"),
tr("This option allows to change the emulated clock of the Switch.\n"
"Can be used to manipulate time in games."));
INSERT(Settings, custom_rtc_enabled, QString(), QString());
INSERT(Settings,
custom_rtc_offset,
QStringLiteral(" "),
QStringLiteral("The number of seconds from the current unix time"));
INSERT(Settings,
language_index,
tr("Language:"),
tr("Note: this can be overridden when region setting is auto-select"));
INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch."));
INSERT(Settings, time_zone_index, tr("Time Zone:"), tr("The time zone of the emulated Switch."));
INSERT(Settings, sound_index, tr("Sound Output Mode:"), QString());
INSERT(Settings,
use_docked_mode,
tr("Console Mode:"),
tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change "
"their resolution, details and supported controllers and depending on this setting.\n"
"Setting to Handheld can help improve performance for low end systems."));
INSERT(Settings, current_user, QString(), QString());
// Controls
// Data Storage
// Debugging
// Debugging Graphics
// Network
// Web Service
// Ui
// Ui General
INSERT(UISettings,
select_user_on_boot,
tr("Prompt for user on game boot"),
tr("Ask to select a user profile on each boot, useful if multiple people use eden on "
"the same PC."));
INSERT(UISettings,
pause_when_in_background,
tr("Pause emulation when in background"),
tr("This setting pauses eden when focusing other windows."));
INSERT(UISettings,
confirm_before_stopping,
tr("Confirm before stopping emulation"),
tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling "
"it bypasses such prompts and directly exits the emulation."));
INSERT(UISettings,
hide_mouse,
tr("Hide mouse on inactivity"),
tr("This setting hides the mouse after 2.5s of inactivity."));
INSERT(UISettings,
controller_applet_disabled,
tr("Disable controller applet"),
tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
"attempts to open the controller applet, it is immediately closed."));
INSERT(UISettings,
check_for_updates,
tr("Check for updates"),
tr("Whether or not to check for updates upon startup."));
// Linux
INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QString());
// Ui Debugging
// Ui Multiplayer
// Ui Games list
#undef INSERT
return translations;
}
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
{
std::unique_ptr<ComboboxTranslationMap> translations = std::make_unique<ComboboxTranslationMap>();
const auto& tr = [&](const char* text, const char* context = "") {
return parent->tr(text, context);
};
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
// Intentionally skipping VSyncMode to let the UI fill that one out
translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(),
{
PAIR(AppletMode, HLE, tr("Custom frontend")),
PAIR(AppletMode, LLE, tr("Real applet")),
}});
translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(),
{
PAIR(SpirvOptimizeMode, Never, tr("Never")),
PAIR(SpirvOptimizeMode, OnLoad, tr("On Load")),
PAIR(SpirvOptimizeMode, Always, tr("Always")),
}});
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{
PAIR(AstcDecodeMode, Cpu, tr("CPU")),
PAIR(AstcDecodeMode, Gpu, tr("GPU")),
PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
{
PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
}});
translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
{
PAIR(VramUsageMode, Conservative, tr("Conservative")),
PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
}});
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{
#ifdef HAS_OPENGL
PAIR(RendererBackend, OpenGL, tr("OpenGL")),
#endif
PAIR(RendererBackend, Vulkan, tr("Vulkan")),
PAIR(RendererBackend, Null, tr("Null")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
{
PAIR(ShaderBackend, Glsl, tr("GLSL")),
PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, AMD/Mesa Only)")),
}});
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{
PAIR(GpuAccuracy, Normal, tr("Normal")),
PAIR(GpuAccuracy, High, tr("High")),
PAIR(GpuAccuracy, Extreme, tr("Extreme")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
{
PAIR(CpuAccuracy, Auto, tr("Auto")),
PAIR(CpuAccuracy, Accurate, tr("Accurate")),
PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
}});
translations->insert({Settings::EnumMetadata<Settings::CpuBackend>::Index(),
{
PAIR(CpuBackend, Dynarmic, tr("Dynarmic")),
PAIR(CpuBackend, Nce, tr("NCE")),
}});
translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
{
PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")),
PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")),
}});
translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
{
PAIR(NvdecEmulation, Off, tr("No Video Output")),
PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")),
PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
{
PAIR(ResolutionSetup, Res1_4X, tr("0.25X (180p/270p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")),
PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")),
PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")),
PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")),
PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")),
PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")),
PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")),
PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")),
}});
translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
{
PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")),
PAIR(ScalingFilter, Bilinear, tr("Bilinear")),
PAIR(ScalingFilter, Bicubic, tr("Bicubic")),
PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™ Super Resolution")),
}});
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
{
PAIR(AntiAliasing, None, tr("None")),
PAIR(AntiAliasing, Fxaa, tr("FXAA")),
PAIR(AntiAliasing, Smaa, tr("SMAA")),
}});
translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
{
PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
PAIR(AspectRatio, R4_3, tr("Force 4:3")),
PAIR(AspectRatio, R21_9, tr("Force 21:9")),
PAIR(AspectRatio, R16_10, tr("Force 16:10")),
PAIR(AspectRatio, Stretch, tr("Stretch to Window")),
}});
translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
{
PAIR(AnisotropyMode, Automatic, tr("Automatic")),
PAIR(AnisotropyMode, Default, tr("Default")),
PAIR(AnisotropyMode, X2, tr("2x")),
PAIR(AnisotropyMode, X4, tr("4x")),
PAIR(AnisotropyMode, X8, tr("8x")),
PAIR(AnisotropyMode, X16, tr("16x")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::Language>::Index(),
{
PAIR(Language, Japanese, tr("Japanese (日本語)")),
PAIR(Language, EnglishAmerican, tr("American English")),
PAIR(Language, French, tr("French (français)")),
PAIR(Language, German, tr("German (Deutsch)")),
PAIR(Language, Italian, tr("Italian (italiano)")),
PAIR(Language, Spanish, tr("Spanish (español)")),
PAIR(Language, Chinese, tr("Chinese")),
PAIR(Language, Korean, tr("Korean (한국어)")),
PAIR(Language, Dutch, tr("Dutch (Nederlands)")),
PAIR(Language, Portuguese, tr("Portuguese (português)")),
PAIR(Language, Russian, tr("Russian (Русский)")),
PAIR(Language, Taiwanese, tr("Taiwanese")),
PAIR(Language, EnglishBritish, tr("British English")),
PAIR(Language, FrenchCanadian, tr("Canadian French")),
PAIR(Language, SpanishLatin, tr("Latin American Spanish")),
PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
}});
translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
{
PAIR(Region, Japan, tr("Japan")),
PAIR(Region, Usa, tr("USA")),
PAIR(Region, Europe, tr("Europe")),
PAIR(Region, Australia, tr("Australia")),
PAIR(Region, China, tr("China")),
PAIR(Region, Korea, tr("Korea")),
PAIR(Region, Taiwan, tr("Taiwan")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::TimeZone>::Index(),
{
{static_cast<u32>(Settings::TimeZone::Auto),
tr("Auto (%1)", "Auto select time zone")
.arg(QString::fromStdString(
Settings::GetTimeZoneString(Settings::TimeZone::Auto)))},
{static_cast<u32>(Settings::TimeZone::Default),
tr("Default (%1)", "Default time zone")
.arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
PAIR(TimeZone, Cet, tr("CET")),
PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
PAIR(TimeZone, Cuba, tr("Cuba")),
PAIR(TimeZone, Eet, tr("EET")),
PAIR(TimeZone, Egypt, tr("Egypt")),
PAIR(TimeZone, Eire, tr("Eire")),
PAIR(TimeZone, Est, tr("EST")),
PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
PAIR(TimeZone, Gb, tr("GB")),
PAIR(TimeZone, GbEire, tr("GB-Eire")),
PAIR(TimeZone, Gmt, tr("GMT")),
PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
PAIR(TimeZone, GmtZero, tr("GMT0")),
PAIR(TimeZone, Greenwich, tr("Greenwich")),
PAIR(TimeZone, Hongkong, tr("Hongkong")),
PAIR(TimeZone, Hst, tr("HST")),
PAIR(TimeZone, Iceland, tr("Iceland")),
PAIR(TimeZone, Iran, tr("Iran")),
PAIR(TimeZone, Israel, tr("Israel")),
PAIR(TimeZone, Jamaica, tr("Jamaica")),
PAIR(TimeZone, Japan, tr("Japan")),
PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
PAIR(TimeZone, Libya, tr("Libya")),
PAIR(TimeZone, Met, tr("MET")),
PAIR(TimeZone, Mst, tr("MST")),
PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
PAIR(TimeZone, Navajo, tr("Navajo")),
PAIR(TimeZone, Nz, tr("NZ")),
PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
PAIR(TimeZone, Poland, tr("Poland")),
PAIR(TimeZone, Portugal, tr("Portugal")),
PAIR(TimeZone, Prc, tr("PRC")),
PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
PAIR(TimeZone, Roc, tr("ROC")),
PAIR(TimeZone, Rok, tr("ROK")),
PAIR(TimeZone, Singapore, tr("Singapore")),
PAIR(TimeZone, Turkey, tr("Turkey")),
PAIR(TimeZone, Uct, tr("UCT")),
PAIR(TimeZone, Universal, tr("Universal")),
PAIR(TimeZone, Utc, tr("UTC")),
PAIR(TimeZone, WSu, tr("W-SU")),
PAIR(TimeZone, Wet, tr("WET")),
PAIR(TimeZone, Zulu, tr("Zulu")),
}});
translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
{
PAIR(AudioMode, Mono, tr("Mono")),
PAIR(AudioMode, Stereo, tr("Stereo")),
PAIR(AudioMode, Surround, tr("Surround")),
}});
translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
{
PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")),
PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")),
PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM")),
PAIR(MemoryLayout, Memory_10Gb, tr("10GB DRAM (Unsafe)")),
PAIR(MemoryLayout, Memory_12Gb, tr("12GB DRAM (Unsafe)")),
}});
translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
{
PAIR(ConsoleMode, Docked, tr("Docked")),
PAIR(ConsoleMode, Handheld, tr("Handheld")),
}});
translations->insert({Settings::EnumMetadata<Settings::CpuClock>::Index(),
{
PAIR(CpuClock, Boost, tr("Boost (1700MHz)")),
PAIR(CpuClock, Fast, tr("Fast (2000MHz)")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
{
PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")),
PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")),
PAIR(ConfirmStop, Ask_Never, tr("Never ask")),
}});
translations->insert({Settings::EnumMetadata<Settings::GpuOverclock>::Index(),
{
PAIR(GpuOverclock, Low, tr("Low (128)")),
PAIR(GpuOverclock, Medium, tr("Medium (256)")),
PAIR(GpuOverclock, High, tr("High (512)")),
}});
#undef PAIR
#undef CTX_PAIR
return translations;
}
} // namespace ConfigurationShared

View file

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include <QString>
#include "common/common_types.h"
#include "common/settings.h"
namespace ConfigurationShared {
using TranslationMap = std::map<u32, std::pair<QString, QString>>;
using ComboboxTranslations = std::vector<std::pair<u32, QString>>;
using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>;
std::unique_ptr<TranslationMap> InitializeTranslations(QObject *parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject *parent);
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
};
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
{Settings::ScalingFilter::NearestNeighbor,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
{Settings::ScalingFilter::Bilinear,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
{Settings::ScalingFilter::Gaussian,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
{Settings::ScalingFilter::ScaleForce,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
};
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
};
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
};
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
};
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
};
} // namespace ConfigurationShared

View file

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "uisettings.h"
#include <QSettings>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings {
template class Setting<bool>;
template class Setting<std::string>;
template class Setting<u16, true>;
template class Setting<u32>;
template class Setting<u8, true>;
template class Setting<u8>;
template class Setting<unsigned long long>;
} // namespace Settings
#endif
namespace FS = Common::FS;
namespace UISettings {
const Themes themes{{
{"Default", "default"},
{"Default Colorful", "colorful"},
{"Dark", "qdarkstyle"},
{"Dark Colorful", "colorful_dark"},
{"Midnight Blue", "qdarkstyle_midnight_blue"},
{"Midnight Blue Colorful", "colorful_midnight_blue"},
}};
bool IsDarkTheme() {
const auto& theme = UISettings::values.theme;
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
}
Values values = {};
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
switch (ratio) {
case Settings::AspectRatio::R4_3:
return height * 4 / 3;
case Settings::AspectRatio::R21_9:
return height * 21 / 9;
case Settings::AspectRatio::R16_10:
return height * 16 / 10;
case Settings::AspectRatio::R16_9:
case Settings::AspectRatio::Stretch:
// TODO: Move this function wherever appropriate to implement Stretched aspect
break;
}
return height * 16 / 9;
}
void SaveWindowState() {
const auto window_state_config_loc =
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "window_state.ini");
void(FS::CreateParentDir(window_state_config_loc));
QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
config.setValue(QStringLiteral("geometry"), values.geometry);
config.setValue(QStringLiteral("state"), values.state);
config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry);
config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state);
config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry);
config.sync();
}
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) {
const auto window_state_config_loc =
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "window_state.ini");
// Migrate window state from old location
if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) {
const auto config_loc =
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "qt-config.ini");
QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat);
config.beginGroup(QStringLiteral("UI"));
config.beginGroup(QStringLiteral("UILayout"));
values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
values.state = config.value(QStringLiteral("state")).toByteArray();
values.renderwindow_geometry =
config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
values.gamelist_header_state =
config.value(QStringLiteral("gameListHeaderState")).toByteArray();
values.microprofile_geometry =
config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
config.endGroup();
config.endGroup();
return;
}
void(FS::CreateParentDir(window_state_config_loc));
const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
values.state = config.value(QStringLiteral("state")).toByteArray();
values.renderwindow_geometry =
config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
values.gamelist_header_state =
config.value(QStringLiteral("gameListHeaderState")).toByteArray();
values.microprofile_geometry =
config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
}
} // namespace UISettings

View file

@ -0,0 +1,288 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QByteArray>
#include <QMetaType>
#include <QString>
#include <QStringList>
#include <QVector>
#include "common/common_types.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "qt_config.h"
#include <array>
#include <atomic>
#include <vector>
using Settings::Category;
using Settings::ConfirmStop;
using Settings::Setting;
using Settings::SwitchableSetting;
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings {
extern template class Setting<bool>;
extern template class Setting<std::string>;
extern template class Setting<u16, true>;
extern template class Setting<u32>;
extern template class Setting<u8, true>;
extern template class Setting<u8>;
extern template class Setting<unsigned long long>;
} // namespace Settings
#endif
namespace UISettings {
bool IsDarkTheme();
struct ContextualShortcut {
std::string keyseq;
std::string controller_keyseq;
int context;
bool repeat;
};
struct Shortcut {
std::string name;
std::string group;
ContextualShortcut shortcut;
};
enum class Theme {
Default,
DefaultColorful,
Dark,
DarkColorful,
MidnightBlue,
MidnightBlueColorful,
};
enum class GameView {
Grid,
List,
Carousel,
};
static constexpr Theme default_theme{
#ifdef _WIN32
Theme::DarkColorful
#else
Theme::DefaultColorful
#endif
};
using Themes = std::array<std::pair<const char*, const char*>, 6>;
extern const Themes themes;
struct GameDir {
std::string path;
bool deep_scan = false;
bool expanded = false;
bool operator==(const GameDir& rhs) const {
return path == rhs.path;
}
bool operator!=(const GameDir& rhs) const {
return !operator==(rhs);
}
};
struct Values {
Settings::Linkage linkage{1000};
QByteArray geometry;
QByteArray state;
QByteArray renderwindow_geometry;
QByteArray gamelist_header_state;
QByteArray microprofile_geometry;
Setting<bool> microprofile_visible{linkage, false, "microProfileDialogVisible",
Category::UiLayout};
Setting<bool> single_window_mode{linkage, true, "singleWindowMode", Category::Ui};
Setting<bool> fullscreen{linkage, false, "fullscreen", Category::Ui};
Setting<bool> display_titlebar{linkage, true, "displayTitleBars", Category::Ui};
Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};
SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
ConfirmStop::Ask_Always,
"confirmStop",
Category::UiGeneral,
Settings::Specialization::Default,
true,
true};
Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
Setting<bool> pause_when_in_background{linkage,
false,
"pauseWhenInBackground",
Category::UiGeneral,
Settings::Specialization::Default,
true,
true};
Setting<bool> mute_when_in_background{linkage,
false,
"muteWhenInBackground",
Category::UiAudio,
Settings::Specialization::Default,
true,
true};
Setting<bool> hide_mouse{
linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
true, true};
Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet",
Category::UiGeneral};
// Set when Vulkan is known to crash the application
bool has_broken_vulkan = false;
Setting<bool> select_user_on_boot{linkage,
false,
"select_user_on_boot",
Category::UiGeneral,
Settings::Specialization::Default,
true,
true};
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui};
// Discord RPC
Setting<bool> enable_discord_presence{linkage, false, "enable_discord_presence", Category::Ui};
// logging
Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};
// Screenshots
Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
Category::Screenshots};
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
std::string roms_path;
std::string game_dir_deprecated;
bool game_dir_deprecated_deepscan;
QVector<GameDir> game_dirs;
QStringList recent_files;
Setting<std::string> language{linkage, {}, "language", Category::Paths};
std::string theme;
// Shortcut name <Shortcut, context>
std::vector<Shortcut> shortcuts;
Setting<u32> callout_flags{linkage, 0, "calloutFlags", Category::Ui};
// multiplayer settings
Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer};
Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned",
Category::Multiplayer};
Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty",
Category::Multiplayer};
Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full",
Category::Multiplayer};
Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
UINT16_MAX, "port", Category::Multiplayer};
Setting<std::string> multiplayer_room_nickname{
linkage, {}, "room_nickname", Category::Multiplayer};
Setting<std::string> multiplayer_room_name{linkage, {}, "room_name", Category::Multiplayer};
Setting<u8, true> multiplayer_max_player{linkage, 8, 0, 8, "max_player", Category::Multiplayer};
Setting<u16, true> multiplayer_room_port{linkage, 24872, 0,
UINT16_MAX, "room_port", Category::Multiplayer};
Setting<u8, true> multiplayer_host_type{linkage, 0, 0, 1, "host_type", Category::Multiplayer};
Setting<unsigned long long> multiplayer_game_id{linkage, {}, "game_id", Category::Multiplayer};
Setting<std::string> multiplayer_room_description{
linkage, {}, "room_description", Category::Multiplayer};
std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list;
// Game List
// Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
// Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
// Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
// Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
// Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
// std::atomic_bool is_game_list_reload_pending{false};
// Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
// Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
// QVector<u64> favorited_ids;
Setting<u8, true> grid_columns{linkage, 4, 1, 8, "grid_columns", Category::UiGameList};
// Compatibility List
Setting<bool> show_compat{linkage, false, "show_compat", Category::UiGameList};
// Size & File Types Column
Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList};
Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList};
// Play time
Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};
bool configuration_applied;
bool reset_to_defaults;
bool shortcut_already_warned{false};
};
extern Values values;
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
void SaveWindowState();
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
// This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE.
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<Shortcut, 28> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit eden")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
}};
// clang-format on
} // namespace UISettings
Q_DECLARE_METATYPE(UISettings::GameDir*);
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
Q_DECLARE_METATYPE(Settings::CpuAccuracy);
Q_DECLARE_METATYPE(Settings::GpuAccuracy);
Q_DECLARE_METATYPE(Settings::FullscreenMode);
Q_DECLARE_METATYPE(Settings::NvdecEmulation);
Q_DECLARE_METATYPE(Settings::ResolutionSetup);
Q_DECLARE_METATYPE(Settings::ScalingFilter);
Q_DECLARE_METATYPE(Settings::AntiAliasing);
Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::ShaderBackend);
Q_DECLARE_METATYPE(Settings::AstcRecompression);
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
Q_DECLARE_METATYPE(Settings::SpirvOptimizeMode);

59
src/eden/main.cpp Normal file
View file

@ -0,0 +1,59 @@
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "core/core.h"
#include "interface/QMLConfig.h"
#include "models/GameListModel.h"
#include "interface/SettingsInterface.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"));
/// 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, "org.eden_emu.interface", 1, 0, "SettingsCategories", QString());
// Directory List
GameListModel *gameListModel = new GameListModel(&app);
ctx->setContextProperty(QStringLiteral("EdenGameList"), gameListModel);
/// LOAD
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.loadFromModule("org.eden_emu.main", "Main");
return app.exec();
}

View file

@ -0,0 +1,16 @@
set(CMAKE_AUTOMOC ON)
# TODO: This might not need to be exposed to QML?
qt_add_library(edenModels STATIC)
qt_add_qml_module(edenModels
URI org.eden_emu.models
VERSION 1.0
SOURCES
GameListModel.h GameListModel.cpp
SettingsModel.h SettingsModel.cpp
)
target_link_libraries(edenModels
PRIVATE
Qt6::Gui
)

View 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);
}
}
}
}

View 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

View 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;
}

View file

@ -0,0 +1,42 @@
#ifndef SETTINGSMODEL_H
#define SETTINGSMODEL_H
#include <QAbstractListModel>
#include "eden/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

View file

@ -0,0 +1,6 @@
add_subdirectory(config)
add_subdirectory(constants)
add_subdirectory(items)
add_subdirectory(util)
add_subdirectory(main)

View file

@ -0,0 +1,57 @@
set(CMAKE_AUTOMOC ON)
qt_add_library(edenConfig STATIC)
qt_add_qml_module(edenConfig
URI org.eden_emu.config
VERSION 1.0
QML_FILES GlobalConfigureDialog.qml
QML_FILES Setting.qml
QML_FILES SectionHeader.qml
QML_FILES pages/SettingsList.qml
QML_FILES pages/global/GlobalTab.qml
QML_FILES pages/global/GlobalTabSwipeView.qml
QML_FILES pages/global/GlobalGeneralPage.qml
QML_FILES pages/global/GlobalSystemPage.qml
QML_FILES pages/global/GlobalCpuPage.qml
QML_FILES pages/global/GlobalGraphicsPage.qml
QML_FILES pages/global/GlobalAudioPage.qml
QML_FILES pages/general/UiGeneralPage.qml
QML_FILES pages/general/UiGameListPage.qml
QML_FILES
QML_FILES pages/graphics/RendererPage.qml
QML_FILES pages/graphics/RendererAdvancedPage.qml
QML_FILES pages/graphics/RendererExtensionsPage.qml
QML_FILES pages/system/SystemGeneralPage.qml
QML_FILES
QML_FILES pages/system/FileSystemPage.qml
QML_FILES pages/system/AppletsPage.qml
QML_FILES pages/cpu/CpuGeneralPage.qml
QML_FILES pages/audio/AudioGeneralPage.qml
QML_FILES pages/global/GlobalDebugPage.qml
QML_FILES pages/debug/DebugGeneralPage.qml
QML_FILES pages/debug/DebugGraphicsPage.qml
QML_FILES pages/debug/DebugAdvancedPage.qml
QML_FILES pages/debug/DebugCpuPage.qml
QML_FILES fields/ConfigCheckbox.qml
QML_FILES fields/FieldLabel.qml
QML_FILES fields/ConfigComboBox.qml
QML_FILES TestSetting.qml
QML_FILES fields/ConfigIntLine.qml
QML_FILES fields/ConfigTimeEdit.qml
QML_FILES fields/ConfigIntSpin.qml
QML_FILES fields/ConfigHexEdit.qml
QML_FILES fields/ConfigStringEdit.qml
QML_FILES fields/FieldCheckbox.qml
QML_FILES fields/ConfigIntSlider.qml
QML_FILES pages/system/SystemCorePage.qml
QML_FILES fields/BaseField.qml
)

View file

@ -0,0 +1,80 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.constants
import org.eden_emu.items
import org.eden_emu.interface
import org.eden_emu.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 * Constants.scalar
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 * Constants.scalar
}
clip: true
GlobalGeneralPage {}
GlobalSystemPage {}
GlobalCpuPage {}
GlobalGraphicsPage {}
GlobalAudioPage {}
GlobalDebugPage {}
}
}

View file

@ -0,0 +1,8 @@
import QtQuick
import org.eden_emu.constants
Text {
color: Constants.text
font.pixelSize: 16
}

View file

@ -0,0 +1,92 @@
import QtQuick
import Qt.labs.qmlmodels
import org.eden_emu.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
}
}
}

View file

@ -0,0 +1,39 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.constants
Column {
topPadding: 5 * Constants.scalar
leftPadding: 10 * Constants.scalar
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
}
}

View file

@ -0,0 +1,170 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.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 * Constants.scalar
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 * Constants.scalar
right: parent.right
rightMargin: 20 * Constants.scalar
top: content.bottom
topMargin: -height
}
z: -1
text: setting.tooltip
color: Constants.subText
font.pixelSize: 12 * Constants.scalar
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
}
}
}

View file

@ -0,0 +1,27 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.constants
BaseField {
forceCheckbox: true
// // TODO: global/custom
// contentItem: CheckBox {
// id: control
// Layout.rightMargin: 10 * Constants.scalar
// Layout.fillWidth: true
// font.pixelSize: 15 * Constants.scalar
// indicator.implicitHeight: 25 * Constants.scalar
// indicator.implicitWidth: 25 * Constants.scalar
// text: setting.label
// checked: setting.value
// onClicked: value = checked
// checkable: true
// }
}

View file

@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import QtQuick.Controls.Material.impl
import org.eden_emu.constants
import org.eden_emu.config
BaseField {
contentItem: ComboBox {
id: control
enabled: enable
Layout.fillWidth: true
Layout.rightMargin: 10 * Constants.scalar
font.pixelSize: 14 * Constants.scalar
model: setting.combo
currentIndex: value
background: MaterialTextContainer {
implicitWidth: 120
implicitHeight: 40 * Constants.scalar
outlineColor: (enabled
&& control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
focusedOutlineColor: control.Material.accentColor
controlHasActiveFocus: control.activeFocus
controlHasText: true
horizontalPadding: control.Material.textFieldHorizontalPadding
}
}
}

View file

@ -0,0 +1,26 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.constants
BaseField {
contentItem: BetterTextField {
enabled: enable
Layout.fillWidth: true
Layout.rightMargin: 10 * Constants.scalar
validator: RegularExpressionValidator {
regularExpression: /[0-9a-fA-F]{0,8}/
}
font.pixelSize: 15 * Constants.scalar
text: Number(value).toString(16)
suffix: setting.suffix
onTextEdited: value = Number("0x" + text)
}
}

View file

@ -0,0 +1,28 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.constants
BaseField {
contentItem: BetterTextField {
enabled: enable
Layout.fillWidth: true
Layout.rightMargin: 10 * Constants.scalar
inputMethodHints: Qt.ImhDigitsOnly
validator: IntValidator {
bottom: setting.min
top: setting.max
}
font.pixelSize: 15 * Constants.scalar
text: value
suffix: setting.suffix
onTextEdited: value = parseInt(text)
}
}

View file

@ -0,0 +1,40 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.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 * Constants.scalar
snapMode: Slider.SnapAlways
}
Text {
font.pixelSize: 14 * Constants.scalar
color: Constants.text
text: field.value + setting.suffix
Layout.rightMargin: 10 * Constants.scalar
}
}
}

View file

@ -0,0 +1,26 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.constants
BaseField {
id: field
contentItem: BetterSpinBox {
enabled: enable
Layout.fillWidth: true
Layout.rightMargin: 10 * Constants.scalar
from: setting.min
to: setting.max
font.pixelSize: 15 * Constants.scalar
value: field.value
label: setting.suffix
onValueModified: field.value = value
}
}

View file

@ -0,0 +1,22 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.constants
BaseField {
contentItem: BetterTextField {
enabled: enable
Layout.fillWidth: true
Layout.rightMargin: 10 * Constants.scalar
font.pixelSize: 15 * Constants.scalar
text: value
suffix: setting.suffix
onTextEdited: value = text
}
}

View file

@ -0,0 +1,27 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.items
import org.eden_emu.config
import org.eden_emu.constants
BaseField {
// TODO: real impl
contentItem: BetterTextField {
enabled: enable
Layout.fillWidth: true
Layout.rightMargin: 10 * Constants.scalar
inputMethodHints: Qt.ImhDigitsOnly
validator: IntValidator {
bottom: setting.min
top: setting.max
}
font.pixelSize: 15 * Constants.scalar
text: value
suffix: setting.suffix
}
}

View file

@ -0,0 +1,20 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.eden_emu.constants
CheckBox {
property bool force: false
property var setting
property var other: setting.other === null ? setting : setting.other
indicator.implicitHeight: 25 * Constants.scalar
indicator.implicitWidth: 25 * Constants.scalar
checked: visible ? other.value : true
onClicked: if (visible)
other.value = checked
visible: setting.other !== null || force
}

View file

@ -0,0 +1,18 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.eden_emu.constants
Text {
property var setting
text: setting.label
color: Constants.text
font.pixelSize: 14 * Constants.scalar
height: 50 * Constants.scalar
ToolTip.text: setting.tooltip
Layout.fillWidth: true
}

View file

@ -0,0 +1 @@
<RCC/>

View file

@ -0,0 +1,47 @@
import QtQuick
import QtQuick.Layouts
import org.eden_emu.config
import org.eden_emu.constants
import org.eden_emu.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 * Constants.scalar
spacing: 8 * Constants.scalar
model: SettingsInterface.category(category, idInclude, idExclude)
Rectangle {
anchors.fill: parent
color: "transparent"
border {
color: inset ? Constants.text : "transparent"
width: 1
}
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.Audio
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.Cpu
}
}
}

View file

@ -0,0 +1,19 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
// TODO: filter
SettingsList {
category: SettingsCategories.Debugging
}
}
}

View file

@ -0,0 +1,18 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.CpuDebug
}
}
}

View file

@ -0,0 +1,28 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.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
}
}
}
}

View file

@ -0,0 +1,20 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
Layout.fillWidth: true
category: SettingsCategories.DebuggingGraphics
}
}
}

View file

@ -0,0 +1,18 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
// TODO: language, theme
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.UiGameList
}
}
}

View file

@ -0,0 +1,23 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.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"
}
}
}

View file

@ -0,0 +1,16 @@
import QtQuick
import org.eden_emu.config
GlobalTab {
property alias swipe: swipe
tabs: ["Audio"]
GlobalTabSwipeView {
id: swipe
currentIndex: tabBar.currentIndex
AudioGeneralPage {}
}
}

View file

@ -0,0 +1,15 @@
import QtQuick
import org.eden_emu.config
GlobalTab {
property alias swipe: swipe
tabs: ["CPU"]
GlobalTabSwipeView {
id: swipe
currentIndex: tabBar.currentIndex
CpuGeneralPage {}
}
}

View file

@ -0,0 +1,18 @@
import QtQuick
import org.eden_emu.config
GlobalTab {
property alias swipe: swipe
tabs: ["General", "Graphics", "Advanced", "CPU"]
GlobalTabSwipeView {
id: swipe
currentIndex: tabBar.currentIndex
DebugGeneralPage {}
DebugGraphicsPage {}
DebugAdvancedPage {}
DebugCpuPage {}
}
}

View file

@ -0,0 +1,18 @@
import QtQuick
import org.eden_emu.config
GlobalTab {
property alias swipe: swipe
tabs: ["General", "Hotkeys", "Game List"]
GlobalTabSwipeView {
id: swipe
currentIndex: tabBar.currentIndex
// TODO: platform-specific stuff
UiGeneralPage {}
Item {}
UiGameListPage {}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import org.eden_emu.config
GlobalTab {
property alias swipe: swipe
tabs: ["Graphics", "Advanced", "Extensions"]
GlobalTabSwipeView {
id: swipe
currentIndex: tabBar.currentIndex
RendererPage {}
RendererAdvancedPage {}
RendererExtensionsPage {}
}
}

View file

@ -0,0 +1,19 @@
import QtQuick
import org.eden_emu.config
GlobalTab {
property alias swipe: swipe
tabs: ["System", "Core", "Profiles", "Filesystem", "Applets"]
GlobalTabSwipeView {
id: swipe
currentIndex: tabBar.currentIndex
SystemGeneralPage {}
SystemCorePage {}
Item {}
FileSystemPage {}
AppletsPage {}
}
}

View file

@ -0,0 +1,34 @@
import QtQuick 2.15
import QtQuick.Controls.Material
import org.eden_emu.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 * Constants.scalar
text: modelData
}
}
background: Rectangle {
color: tabBar.Material.backgroundColor
radius: 8 * Constants.scalar
}
}
}

View file

@ -0,0 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls.Material
import org.eden_emu.constants
SwipeView {
anchors {
top: tabBar.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
leftMargin: 20 * Constants.scalar
topMargin: 10 * Constants.scalar
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.RendererAdvanced
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.RendererExtensions
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.Renderer
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.LibraryApplet
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.DataStorage
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.config
ScrollView {
id: scroll
ColumnLayout {
width: scroll.width - scroll.effectiveScrollBarWidth
SettingsList {
category: SettingsCategories.Core
}
}
}

View file

@ -0,0 +1,22 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.interface
import org.eden_emu.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"]
}
}
}

View file

@ -0,0 +1,23 @@
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Quick)
set_source_files_properties(Constants.qml
PROPERTIES
QT_QML_SINGLETON_TYPE true
)
qt_add_library(edenConstants STATIC)
qt_add_qml_module(edenConstants
URI org.eden_emu.constants
OUTPUT_DIRECTORY EdenConstants
VERSION 1.0
QML_FILES
Constants.qml
)
target_link_libraries(edenConstants
PRIVATE
Qt6::Quick
)

View 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
}

View file

@ -0,0 +1,79 @@
import QtQuick
import QtQuick.Controls.Material
import org.eden_emu.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 {}
}

View file

@ -0,0 +1,59 @@
import QtQuick
import QtQuick.Controls
import org.eden_emu.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
}
}
}
}

View file

@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Controls
import org.eden_emu.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
}
}
}

View file

@ -0,0 +1,22 @@
set(CMAKE_AUTOMOC ON)
qt_add_library(edenItems STATIC)
qt_add_qml_module(edenItems
URI org.eden_emu.items
VERSION 1.0
QML_FILES
StatusBarButton.qml
BetterMenu.qml
AnimatedDialog.qml
BetterMenuBar.qml
SettingsTabButton.qml
IconButton.qml
QML_FILES VerticalTabBar.qml
QML_FILES
QML_FILES fields/BetterSpinBox.qml
QML_FILES fields/BetterTextField.qml
QML_FILES fields/FieldFooter.qml
)

View file

@ -0,0 +1,23 @@
import QtQuick
import QtQuick.Controls.Material
import org.eden_emu.constants
Button {
required property string label
bottomInset: 0
topInset: 0
leftPadding: 5 * Constants.scalar
rightPadding: 5 * Constants.scalar
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 {}
}

View file

@ -0,0 +1,40 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.constants
TabButton {
required property string label
id: button
implicitHeight: 100 * Constants.scalar
width: 95 * Constants.scalar
contentItem: ColumnLayout {
IconButton {
label: button.label
Layout.maximumHeight: 60 * Constants.scalar
Layout.maximumWidth: 65 * Constants.scalar
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
onClicked: button.clicked()
}
Text {
font.pixelSize: 16 * Constants.scalar
text: label
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
color: Constants.text
}
}
// background: Rectangle {
// color: button.Material.backgroundColor
// }
}

View file

@ -0,0 +1,36 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.eden_emu.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 * Constants.scalar
leftPadding: 4
rightPadding: 4
color: button.textColor
text: button.text
}
}
}

View file

@ -0,0 +1,46 @@
import QtQuick
import QtQuick.Controls.Material
import org.eden_emu.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 * Constants.scalar
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
height: parent.height / 2
width: 5 * Constants.scalar
color: Constants.accent
}
}
}
background: Rectangle {
color: control.Material.backgroundColor
radius: 8 * Constants.scalar
}
}

View file

@ -0,0 +1,68 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Controls.impl
import org.eden_emu.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 * Constants.scalar
implicitHeight: 40 * Constants.scalar
height: parent.height
width: height / 2
}
down.indicator: IconLabel {
icon {
source: "qrc:/icons/back.svg"
}
x: control.mirrored ? control.width - width : 0
implicitWidth: 40 * Constants.scalar
implicitHeight: 40 * Constants.scalar
height: parent.height
width: height / 2
}
background: Item {}
FieldFooter {
anchors {
bottom: contentItem.bottom
}
}
}

View file

@ -0,0 +1,38 @@
import QtQuick
import QtQuick.Controls.Material
import org.eden_emu.constants
import org.eden_emu.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 * Constants.scalar
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 5 * Constants.scalar
}
color: "gray"
}
}

View file

@ -0,0 +1,20 @@
import QtQuick
import QtQuick.Controls.Material
import org.eden_emu.constants
Rectangle {
height: 2 * Constants.scalar
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
}
}
}

View file

@ -0,0 +1,18 @@
set(CMAKE_AUTOMOC ON)
qt_add_library(edenMain STATIC)
qt_add_qml_module(edenMain
URI org.eden_emu.main
VERSION 1.0
QML_FILES
Main.qml
StatusBar.qml
GameList.qml
GameGridCard.qml
MarqueeText.qml
GameGrid.qml
GameCarouselCard.qml
GameCarousel.qml
)

View file

@ -0,0 +1,94 @@
import QtQuick
import QtQuick.Controls
import Qt.labs.platform
import QtCore
import org.eden_emu.constants
import org.eden_emu.interface
ListView {
id: carousel
focus: true
focusPolicy: Qt.StrongFocus
model: EdenGameList
orientation: ListView.Horizontal
clip: false
flickDeceleration: 1000
snapMode: ListView.SnapToItem
onHeightChanged: console.log(width, height)
spacing: 20
Keys.enabled: true
Keys.onRightPressed: incrementCurrentIndex()
Keys.onLeftPressed: decrementCurrentIndex()
onCurrentIndexChanged: scrollToCenter()
highlight: Rectangle {
id: hg
clip: false
z: 3
color: "transparent"
border {
color: "deepskyblue"
width: 4 * Constants.scalar
}
radius: 8 * Constants.scalar
// TODO: marquee
Text {
function toTitleCase(str) {
return str.replace(/\w\S*/g, text => text.charAt(0).toUpperCase(
) + text.substring(1).toLowerCase())
}
property var item: carousel.currentItem
text: toTitleCase(item.title)
font.pixelSize: 22 * Constants.scalar
color: "lightblue"
anchors {
bottom: hg.top
bottomMargin: 10 * Constants.scalar
left: hg.left
right: hg.right
}
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
}
highlightFollowsCurrentItem: true
highlightMoveDuration: 300
highlightMoveVelocity: -1
delegate: GameCarouselCard {
id: game
width: 300
height: 300
}
function scrollToCenter() {
let targetX = currentIndex * 320 - (width - 320) / 2
let min = 0
let max = contentWidth
contentX = Math.max(min, Math.min(max, targetX))
}
Behavior on contentX {
NumberAnimation {
duration: 300
easing.type: Easing.OutQuad
}
}
}

View file

@ -0,0 +1,44 @@
import QtQuick
import QtQuick.Controls
import Qt.labs.platform
import QtCore
import org.eden_emu.constants
import org.eden_emu.interface
import org.eden_emu.gamepad
GridView {
property var setting
id: grid
property int cellSize: Math.floor(width / setting.value)
highlightFollowsCurrentItem: true
clip: true
cellWidth: cellSize
cellHeight: cellSize + 60 * Constants.scalar
model: EdenGameList
delegate: GameGridCard {
id: game
width: grid.cellSize - 20 * Constants.scalar
height: grid.cellHeight - 20 * Constants.scalar
}
highlight: Rectangle {
color: "transparent"
z: 5
radius: 16 * Constants.scalar
border {
color: Constants.text
width: 3
}
}
focus: true
focusPolicy: "StrongFocus"
}

View file

@ -0,0 +1,77 @@
import QtQuick
import QtQuick.Controls
import Qt.labs.platform
import QtCore
import org.eden_emu.constants
Rectangle {
id: wrapper
color: Constants.dialog
radius: 16 * Constants.scalar
Image {
id: image
fillMode: Image.PreserveAspectFit
source: "file://" + model.path
clip: true
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 4 * Constants.scalar
}
height: parent.height - 40 * Constants.scalar
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
}
}
}
}
Text {
id: nameText
anchors {
bottom: parent.bottom
bottomMargin: 5 * Constants.scalar
left: parent.left
right: parent.right
}
style: Text.Outline
styleColor: Constants.bg
text: model.name.replace(/-/g, " ")
wrapMode: Text.WordWrap
horizontalAlignment: Qt.AlignHCenter
font {
pixelSize: 15 * Constants.scalar
}
color: "white"
}
}

View file

@ -0,0 +1,131 @@
import QtQuick
import QtQuick.Controls
import Qt.labs.platform
import QtCore
import org.eden_emu.constants
import org.eden_emu.interface
import org.eden_emu.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.decrementCurrentIndex()
onRightPressed: carousel.incrementCurrentIndex()
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)
}
}
// GameGrid {
// setting: parent.setting
// id: grid
// anchors.bottom: button.top
// anchors.left: parent.left
// anchors.margins: 8
// anchors.right: parent.right
// anchors.top: parent.top
// }
Item {
id: view
anchors {
bottom: button.top
left: parent.left
right: parent.right
top: parent.top
margins: 8 * Constants.scalar
}
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/qml/main/Main.qml Normal file
View file

@ -0,0 +1,207 @@
import QtQuick
import QtQuick.Controls.Material
import org.eden_emu.config
import org.eden_emu.items
import org.eden_emu.constants
ApplicationWindow {
width: Constants.width
height: Constants.height
visible: true
title: qsTr("eden")
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
}
}
}

View file

@ -0,0 +1,43 @@
import QtQuick
Item {
required property string text
property int spacing: 30
property int startDelay: 2000
property int speed: 40
property alias font: t1.font
property alias color: t1.color
id: root
width: t1.width + spacing
height: t1.height
clip: true
Text {
id: t1
SequentialAnimation on x {
loops: Animation.Infinite
running: true
PauseAnimation {
duration: root.startDelay
}
NumberAnimation {
from: root.width
to: -t1.width
duration: (root.width + t1.width) * 1000 / root.speed
easing.type: Easing.Linear
}
}
Text {
x: root.width
text: t1.text
}
}
}

View file

@ -0,0 +1,160 @@
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import org.eden_emu.constants
import org.eden_emu.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
}
}
}

Some files were not shown because too many files have changed in this diff Show more