UI Submodule Refactor (#32)

* Added recompui::IconButton

* Remove zelda themed input mapping visualization

* update toggle

* grayscale overlay colors

* recompui::theme namespace + border constants

* added ui_binding_button element

* Initial support for multiple controllers. (#3)

* Support for multiple controllers.

* Fix default bindings.

* Backwards compatibility with controls from previous version.

* WIP code driven input mapping and player assignment

* button press anims & temp reassignment state when opening assignment modal

* set base opacity for disable state getting re-enabled

* generic PlayerCard for both main Controls and assignment modal

* wip ui_select recompui component

* Add Input Profiles. (#4)

* Add Input Profiles.

* Add capability of setting custom profiles.

* Remove leftover early return.

* button sizes and annotation update

* flex wrap and context root being assets

* controls page multi vs single views

* ability to reparent elements

* ability to rotate elements

* added functional caret to Select component

* smaller edit profile button

* select fixes and player profile assignment

* pointer events property support and apply ptr events None to select arrow svg

* init mp kb mappings to be unmapped

* wire up selected profile's bindings

* reimplement binding system in new menu

* clear and reset input bindings implemented

* remove deprecated controls panel

* changes up to swapping to recompfrontend library

* switch ui/input to be using recompfrontend library (unfinished)

* register main font with recompui

* Changes for recompui built in default scss

* launcher + custom background

* update modern runtime for recompui

* Update N64ModernRuntime for recompui config changes

* Added RecordSpinner icon

* Added PlusKeyboard icon

* remove sass and rml

* single player + theme update

* Delete Rmlui and lunasvg from being dependencies.

* Update recomp frontend commit.

* Remove lunasvg and rmlui submodules.

* Update recomp frontend.

* Move findfreetype.

* Remove freetype windows binaries.

* More updates.

* Update frontend.

* It builds.

* Update frontend.

* Update runtime and frontend.

* Update frontend and RT64.

* Get rid of SDL2 path from recomp frontend.

* Update runtime and frontend and fix link order to fix Linux building

* Update frontend and remove gyro and mouse sensitivity options

* Update frontend to fix open menu button on controllers

---------

Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
Co-authored-by: Mr-Wiseguy <mrwiseguyromhacking@gmail.com>
This commit is contained in:
Darío
2026-01-03 18:44:58 -03:00
committed by GitHub
parent 7cd1cc8175
commit d26d18b373
153 changed files with 399 additions and 24833 deletions
+3 -9
View File
@@ -1,15 +1,6 @@
[submodule "lib/N64ModernRuntime"]
path = lib/N64ModernRuntime
url = https://github.com/N64Recomp/N64ModernRuntime
[submodule "lib/RmlUi"]
path = lib/RmlUi
url = https://github.com/mikke89/RmlUi
[submodule "lib/freetype-windows-binaries"]
path = lib/freetype-windows-binaries
url = https://github.com/ubawurinna/freetype-windows-binaries/
[submodule "lib/lunasvg"]
path = lib/lunasvg
url = https://github.com/sammycage/lunasvg
[submodule "lib/rt64"]
path = lib/rt64
url = https://github.com/rt64/rt64
@@ -19,3 +10,6 @@
[submodule "BanjoRecompSyms"]
path = BanjoRecompSyms
url = https://github.com/BanjoRecomp/BanjoRecompSyms
[submodule "lib/RecompFrontend"]
path = lib/RecompFrontend
url = https://github.com/N64Recomp/RecompFrontend
+11 -58
View File
@@ -51,15 +51,7 @@ endif()
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/rt64 ${CMAKE_BINARY_DIR}/rt64)
# set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
set(BUILD_SHARED_LIBS OFF)
SET(LUNASVG_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/lunasvg)
# set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
SET(RMLUI_SVG_PLUGIN ON CACHE BOOL "" FORCE)
SET(RMLUI_TESTS_ENABLED OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RmlUi)
target_compile_definitions(rmlui_core PRIVATE LUNASVG_BUILD_STATIC)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime)
@@ -149,14 +141,10 @@ add_executable(BanjoRecompiled)
set (SOURCES
${CMAKE_SOURCE_DIR}/src/main/main.cpp
${CMAKE_SOURCE_DIR}/src/main/support.cpp
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp
${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp
${CMAKE_SOURCE_DIR}/src/main/theme.cpp
${CMAKE_SOURCE_DIR}/src/game/input.cpp
${CMAKE_SOURCE_DIR}/src/game/controls.cpp
${CMAKE_SOURCE_DIR}/src/game/config.cpp
${CMAKE_SOURCE_DIR}/src/game/debug.cpp
${CMAKE_SOURCE_DIR}/src/game/recomp_api.cpp
@@ -164,61 +152,22 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/game/recomp_data_api.cpp
${CMAKE_SOURCE_DIR}/src/game/rom_decompression.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_state.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_prompt.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_config_sub_menu.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_details_panel.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_installer.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_mod_menu.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api_events.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_api_images.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_utils.cpp
${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp
${CMAKE_SOURCE_DIR}/src/ui/core/ui_context.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_clickable.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_radio.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_span.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_text_input.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_theme.cpp
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
${CMAKE_SOURCE_DIR}/rsp/n_aspMain.cpp
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp
)
if (APPLE)
list(APPEND SOURCES ${CMAKE_SOURCE_DIR}/src/main/support_apple.mm)
endif()
target_include_directories(BanjoRecompiled PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
${CMAKE_SOURCE_DIR}/lib/GamepadMotionHelpers
${CMAKE_SOURCE_DIR}/lib/RmlUi/Include
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/hlslpp/include
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/dxc/inc
${CMAKE_SOURCE_DIR}/lib/rt64/src
${CMAKE_SOURCE_DIR}/lib/rt64/src/rhi
${CMAKE_SOURCE_DIR}/lib/rt64/src/render
${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompinput/include
${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompui/include
${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompui/src
${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/nativefiledialog-extended/src/include
${CMAKE_SOURCE_DIR}/lib/SlotMap
@@ -285,7 +234,7 @@ if (WIN32)
PROPERTIES
LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE"
LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"
LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"
LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE" # "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"
LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"
)
@@ -349,16 +298,20 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_link_libraries(BanjoRecompiled PRIVATE "-latomic -static-libstdc++" ${CMAKE_DL_LIBS} Threads::Threads)
endif()
set(RECOMP_FRONTEND_N64MODERNRUNTIME_PATH ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime)
set(RECOMP_FRONTEND_RT64_PATH ${CMAKE_SOURCE_DIR}/lib/rt64)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RecompFrontend)
target_link_libraries(BanjoRecompiled PRIVATE
PatchesLib
RecompiledFuncs
recompui
recompinput
librecomp
ultramodern
rt64
RmlUi::Core
RmlUi::Debugger
nfd
lunasvg
)
# TODO fix the rt64 CMake script so that this doesn't need to be duplicated here
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
<svg width="1920" height="1280" viewBox="0 0 1920 1280" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_540_3892)">
<path d="M640.284 1159.76C639.797 1155.45 450.469 1282.17 509.25 1013.98C558.275 790.302 931.74 830.287 935.414 1080.11C934.13 1082.73 817.852 1271.15 640.284 1159.76Z" fill="#F7FAFA"/>
<path d="M840.342 1153.98C848.276 1149.28 858.618 1146.39 863.738 1139.58C889.655 1105.09 876.419 1006.88 843.177 979.092C807.939 949.633 781.195 946.456 745.196 976.366C712.989 1003.12 687.729 1001.8 654.91 977.124C608.969 942.579 573.886 949.503 534.1 992.979C530.091 997.36 525.037 1000.78 515.791 1007.7C512.957 1009.82 509.73 1012.26 506.019 1015.17C523.951 896.876 489.547 810.372 389.215 748.43C440.937 733.701 492.005 709.847 544.513 706.085C665.308 697.43 786.953 700.842 904.341 736.144C944.472 748.212 981.097 771.589 1020.21 787.649C1029.63 791.52 1044.09 790.074 1053.65 785.575C1129.56 749.85 1163.71 762.009 1200.28 845.599C1203.26 890.116 1201.35 923.722 1159.61 946.421C1141.33 956.364 1131.25 983.91 1120.83 1005.12C1108.07 1031.07 1098.32 1058.55 1088.28 1085.75C1079.27 1110.16 1071.64 1135.09 1061.45 1161.61C1038.31 1177.6 1017.14 1191.77 993.45 1204.76C982.508 1198.92 974.59 1192.22 965.556 1189.93C923.594 1179.3 881.322 1169.89 839.497 1158.53C839.833 1157.02 840.342 1153.98 840.342 1153.98ZM663.696 811.922C614.42 828.006 559.256 829.022 510.459 870.041C535.005 881.377 554.123 888.103 570.95 898.525C590.764 910.798 603.621 907.893 621.516 892.344C646.88 870.307 676.37 852.559 705.603 835.571C731.945 820.262 760.316 808.444 787.791 795.087C787.396 792.979 787.001 790.871 786.606 788.763C747.539 795.752 708.472 802.74 663.696 811.922ZM1060.89 840.786C1071.07 865.99 1081.26 891.193 1092.28 918.453C1118.02 922.553 1146.64 917.172 1153.07 886.418C1157.78 863.91 1155.74 830.123 1141.84 815.877C1120.28 793.79 1090.16 809.888 1066.52 826.569C1063.74 828.534 1062.19 832.254 1060.89 840.786Z" fill="#62300F"/>
<path d="M995.96 1205.93C1017.14 1191.77 1038.31 1177.6 1064.05 1162.62C1099.11 1159.53 1129.99 1159.44 1160.02 1154.32C1186.05 1149.89 1211.11 1139.75 1239.34 1132.84C1279.43 1125.11 1316.77 1116.66 1356.71 1107.84C1367.44 1105.02 1375.56 1102.58 1386.68 1101.06C1417.57 1098.78 1445.46 1095.58 1476.26 1092.02C1489.79 1088.14 1500.41 1084.62 1512.05 1080.84C1513.07 1080.57 1515.17 1080.52 1515.17 1080.52C1607.17 1076.03 1699.17 1071.54 1793.59 1070.03C1795.74 1094.85 1795.44 1116.71 1793.29 1140.42C1786.58 1145.78 1782.2 1150.47 1776.81 1152.62C1717.36 1176.32 1657.39 1198.78 1598.41 1223.58C1581.3 1230.78 1566.9 1244.4 1548.45 1255.19C1456.27 1288.6 1366.76 1321.29 1259.62 1311.31C1271.98 1337.18 1280.5 1355.02 1288.93 1375.16C1290.9 1379.07 1292.97 1380.68 1294.77 1382.11C1294.5 1381.94 1293.99 1382.32 1293.4 1385.03C1361.57 1517.62 1439.36 1639.59 1575.52 1710.42C1520.53 1726.41 1479.19 1691.16 1436.71 1668.82C1390.82 1644.69 1350.75 1609.66 1307.32 1580.62C1250.71 1542.77 1193.24 1506.22 1136.26 1468.94C1113.13 1453.8 1090.32 1438.2 1063.15 1420.27C980.139 1371.54 972.652 1273.71 995.96 1205.93Z" fill="#FEE003"/>
<path d="M473.743 1137.47C476.779 1137.08 479.815 1136.68 488.691 1136.06C498.038 1136.38 501.546 1136.92 505.055 1137.47C514.058 1137.92 523.061 1138.36 536.733 1141.44C558.37 1149.77 575.005 1156.93 592.37 1160.91C641.457 1172.15 684.637 1194.46 716.199 1233.43C729.906 1250.36 745.604 1276.19 742.184 1294.6C729.101 1365.01 701.55 1435.73 646.084 1479.14C574.848 1534.91 503.547 1599.29 403.882 1604.24C392.443 1604.81 381.314 1611.63 370.041 1615.55C361.764 1614.25 353.487 1612.95 341.848 1608.22C271.197 1515.55 272.792 1408.28 262.912 1304.01C293.108 1296.61 320.801 1291.47 347.486 1283.11C454.72 1249.5 469.282 1232.16 473.743 1137.47Z" fill="#F1AF85"/>
<path d="M1236.59 1132.13C1211.11 1139.75 1186.05 1149.89 1160.02 1154.32C1129.99 1159.44 1099.11 1159.53 1066 1160.8C1071.64 1135.09 1079.27 1110.16 1088.28 1085.75C1098.32 1058.55 1108.07 1031.07 1120.83 1005.12C1131.25 983.91 1141.33 956.364 1159.61 946.421C1201.35 923.722 1203.26 890.116 1200.65 848.276C1229.54 780.721 1254.15 714.307 1287.66 652.732C1320.75 591.925 1361.11 534.774 1401.61 478.407C1426.62 443.594 1485.69 445.705 1519.17 474.27C1539.63 491.721 1565.11 503.276 1588.34 517.482C1599.16 524.101 1617.62 529.49 1618.94 537.652C1620.77 548.935 1611.81 563.927 1603.77 574.514C1564.05 626.791 1523.41 678.379 1482.51 729.751C1434.55 790.009 1453.6 850.044 1481.3 910.739C1489.86 929.474 1496.03 949.298 1501.48 973.875C1494.24 981.438 1488.81 983.75 1481.87 984.024C1466.62 959.458 1452.39 937.21 1439.27 914.33C1422.7 885.447 1404.33 891.008 1388.89 913.221C1372.36 936.982 1356.98 933.105 1332.79 924.059C1312.8 916.588 1276.64 914.068 1266.73 925.662C1243.63 952.713 1232.02 989.581 1215.66 1025.64C1222.58 1063.24 1229.59 1097.69 1236.59 1132.13Z" fill="#FF2904"/>
<path d="M993.45 1204.76C972.652 1273.71 980.139 1371.54 1060.66 1419.33C1052.6 1431.2 1041.31 1440.41 1033.31 1451.92C961.96 1554.66 883.013 1650.58 773.747 1714.87C717.841 1747.76 653.326 1755.11 589.315 1747.89C529.296 1741.13 492.999 1694.78 480.317 1611.52C485.92 1604.94 490.262 1603.37 495.067 1604.41C506.742 1626.81 513.468 1652.19 529.943 1665.44C598.66 1720.72 739.644 1672.44 767.512 1584.68C805.086 1528.67 835.282 1472.97 824.279 1401.41C831.53 1374.04 842.141 1349.96 844.002 1325.23C845.721 1302.37 845.917 1272.14 832.967 1256.87C802.4 1220.85 763.609 1191.8 728.144 1159.94C728.144 1159.94 728.634 1159.86 731.981 1160.38C743.988 1160.73 752.648 1160.56 764.513 1160.12C791.532 1159.91 815.347 1159.97 839.161 1160.04C881.322 1169.89 923.594 1179.3 965.556 1189.93C974.59 1192.22 982.508 1198.92 993.45 1204.76Z" fill="#F2B085"/>
<path d="M472.524 1135.06C469.282 1232.16 454.72 1249.5 347.486 1283.11C320.801 1291.47 293.108 1296.61 260.622 1302.34C246.885 1303.39 238.404 1305.33 227.06 1306.78C205.015 1298.55 185.833 1290.82 166.651 1283.08C114.696 1234.84 110.751 1171.2 115.315 1107.59C119.09 1054.98 194.335 999.957 264.464 991.678C374.636 978.67 444.355 1026.19 472.524 1135.06Z" fill="#030303"/>
<path d="M725.034 1159.56C763.609 1191.81 802.4 1220.85 832.967 1256.87C845.917 1272.14 845.721 1302.37 844.002 1325.23C842.141 1349.96 831.53 1374.04 821.921 1401.64C804.038 1416.78 790.026 1430.44 773.721 1440.31C687.531 1492.49 600.763 1543.7 512.022 1595.26C504.768 1597.45 499.685 1599.63 494.603 1601.81C490.262 1603.37 485.92 1604.94 480.405 1608.58C444.759 1612.81 410.287 1614.96 372.928 1616.33C381.314 1611.63 392.443 1604.81 403.882 1604.24C503.547 1599.29 574.848 1534.91 646.084 1479.15C701.55 1435.73 729.101 1365.01 742.184 1294.6C745.604 1276.19 729.906 1250.36 716.199 1233.43C684.637 1194.46 641.457 1172.15 592.37 1160.91C575.005 1156.93 558.37 1149.77 540.102 1141.73C542.031 1138.9 545.26 1138.39 552.654 1137.76C558.036 1137.47 559.252 1137.3 563.1 1138.21C590.92 1144.08 616.109 1148.87 643.195 1154C646.36 1154.13 647.628 1153.93 648.897 1153.73C673.239 1155.55 697.582 1157.36 725.034 1159.56Z" fill="#D69A76"/>
<path d="M1551.26 1255.07C1566.9 1244.4 1581.3 1230.78 1598.41 1223.58C1657.39 1198.78 1717.36 1176.32 1776.81 1152.62C1782.2 1150.47 1786.58 1145.78 1795.71 1141.55C1806.27 1150.46 1812.55 1160.1 1818.83 1169.74C1801.57 1184.19 1785.13 1199.77 1766.9 1212.88C1719.12 1247.22 1670.57 1280.49 1617.82 1315.33C1603.14 1319.41 1592.97 1322.34 1581.84 1322.63C1571.02 1298.35 1561.14 1276.71 1551.26 1255.07Z" fill="#684004"/>
<path d="M839.497 1158.53C815.347 1159.97 791.532 1159.91 762.191 1158.36C749.054 1155.45 741.443 1154.03 733.832 1152.61C725.362 1122.91 713.629 1093.66 709.433 1063.37C705.937 1038.13 722.181 1023.98 749.438 1022.26C827.461 1017.34 854.162 1047.41 839.312 1125.01C837.797 1132.93 837.988 1141.17 838.864 1151.62C840.342 1153.98 839.833 1157.02 839.497 1158.53ZM760.158 1111.74C763.782 1114.96 767.883 1121.33 770.947 1120.86C785.23 1118.69 809.287 1117.55 811.275 1110.67C815.713 1095.32 812.437 1075.2 805.443 1060.23C802.308 1053.52 777.394 1048.42 771.724 1053.36C761.776 1062.01 758.401 1078.73 753.708 1092.57C752.471 1096.21 756.494 1101.64 760.158 1111.74Z" fill="#30C4FB"/>
<path d="M641.299 1153.67C616.109 1148.87 590.92 1144.08 562.615 1136.48C558.536 1131.32 556.887 1129.63 554.55 1128.61C552.893 1125.38 550.868 1122.28 549.634 1118.89C536.146 1081.9 543.329 1045.31 568.005 1029.72C596.236 1011.87 621.649 1021.11 644.43 1040.7C666.907 1060.03 669.292 1103.25 648.149 1137.82C644.117 1145.7 642.708 1149.68 641.299 1153.67ZM638.523 1102.35C632.818 1085.45 629.577 1066.83 619.806 1052.75C617.114 1048.87 586.227 1055.35 583.587 1062.63C578.67 1076.19 579.38 1100.91 588.147 1107.85C597.74 1115.45 619.101 1108.19 638.523 1102.35Z" fill="#30C4FB"/>
<path d="M1820.95 1168.31C1812.55 1160.1 1806.27 1150.46 1797.57 1139.69C1795.44 1116.71 1795.74 1094.85 1797.22 1070.46C1803.11 1066.88 1808.44 1063.85 1812.41 1065.11C1833.98 1071.99 1855.22 1079.92 1876.57 1087.49C1863.83 1105.44 1851.09 1123.39 1838.74 1144.12C1839.12 1146.9 1839.28 1147.28 1839.28 1147.28C1833.88 1153.81 1828.47 1160.35 1820.95 1168.31Z" fill="#A94105"/>
<path d="M1483.38 986.062C1488.81 983.75 1494.24 981.438 1501.42 976.661C1508.08 1007.89 1512.99 1041.59 1516.54 1077.9C1515.17 1080.52 1513.07 1080.57 1510.37 1078.28C1499.58 1046.01 1491.48 1016.03 1483.38 986.062Z" fill="#FF4120"/>
<path d="M507.013 1136.52C502.665 1139.78 499.233 1139.06 494.555 1137.98C497.34 1094.73 497.591 1058.23 505.576 1013.98C509.529 1012.62 516.598 1006.72 516.598 1006.72C513.708 1048.66 510.819 1090.6 507.013 1136.52Z" fill="#62300F"/>
<path d="M732.275 1153.46C741.443 1154.03 749.054 1155.45 758.987 1158.63C752.648 1160.56 743.988 1160.73 732.096 1160.6C727.352 1157.64 727.969 1155.64 732.275 1153.46Z" fill="#D69A76"/>
<path d="M1838.96 1146.77C1838.8 1146.64 1839.97 1147.65 1839.97 1147.65C1839.97 1147.65 1839.45 1145.93 1839.37 1146.61C1839.28 1147.28 1839.12 1146.9 1838.96 1146.77Z" fill="#A94105"/>
<path d="M554.155 1130.62C556.887 1129.63 558.536 1131.32 559.984 1135.4C559.252 1137.3 558.036 1137.47 555.035 1137.17C553.421 1135.35 553.59 1133.99 554.155 1130.62Z" fill="#F7FAFA"/>
<path d="M666.551 810.825C708.472 802.74 747.539 795.752 786.606 788.763C787.001 790.871 787.396 792.979 787.791 795.087C760.316 808.444 731.945 820.262 705.603 835.571C676.37 852.559 646.88 870.307 621.516 892.344C603.621 907.893 590.764 910.798 570.95 898.525C554.123 888.103 535.005 881.377 510.459 870.041C559.256 829.022 614.42 828.006 666.551 810.825Z" fill="#351907"/>
<path d="M1060.48 837.973C1062.19 832.254 1063.74 828.534 1066.52 826.569C1090.16 809.888 1120.28 793.79 1141.84 815.877C1155.74 830.123 1157.78 863.91 1153.07 886.418C1146.64 917.172 1118.02 922.553 1092.28 918.453C1081.26 891.193 1071.07 865.99 1060.48 837.973Z" fill="#EEAE83"/>
<path d="M1581.84 1322.63C1582.05 1328.39 1581.3 1331.5 1580 1336.99C1573.43 1339.03 1565.84 1336.16 1561.61 1338.76C1506.88 1372.38 1445.66 1380.68 1383.57 1384.21C1356.03 1385.78 1328.25 1383.01 1297.28 1382.26C1293.99 1382.32 1294.5 1381.94 1294.26 1380.26C1292.35 1376.68 1290.69 1374.77 1289.03 1372.86C1280.5 1355.02 1271.98 1337.18 1259.61 1311.31C1366.76 1321.29 1456.27 1288.6 1548.45 1255.19C1561.14 1276.71 1571.02 1298.35 1581.84 1322.63Z" fill="#A22986"/>
<path d="M1239.34 1132.85C1229.59 1097.69 1222.58 1063.24 1218.09 1024.71C1227.89 1016.4 1235.14 1012.14 1242.44 1007.97C1296.64 977.059 1322.84 983.561 1357.79 1036.17C1359.41 1038.6 1363.69 1039.27 1367.07 1040.93C1371.26 988.879 1379.6 982.11 1434.52 985.249C1468.22 987.175 1488.77 1012.11 1485.84 1051.1C1484.79 1065.1 1477.69 1078.64 1473.36 1092.38C1445.46 1095.58 1417.57 1098.78 1386.23 1098.86C1373.23 1099.9 1363.67 1104.05 1354.11 1108.21C1316.77 1116.66 1279.43 1125.11 1239.34 1132.85ZM1296.93 1101.79C1317.5 1093 1343.26 1083.66 1323.77 1056.05C1314.69 1043.19 1292.53 1034.86 1275.87 1034.13C1268.13 1033.79 1253.81 1055.79 1252.26 1068.83C1249.2 1094.4 1270.71 1099.46 1296.93 1101.79ZM1400.93 1065.13C1416.53 1082.98 1434.49 1082.16 1447.13 1064.76C1453.01 1056.68 1452.77 1033.16 1447.64 1030.27C1435.53 1023.47 1417.91 1021.29 1404.46 1024.75C1399.4 1026.04 1399.68 1048.12 1400.93 1065.13ZM1369.82 1071.19C1370.64 1072.02 1371.35 1073.29 1372.32 1073.54C1372.96 1073.7 1374.68 1072.44 1374.71 1071.76C1374.92 1067.05 1374.82 1062.33 1374.82 1057.61C1373.55 1057.58 1372.29 1057.56 1371.02 1057.54C1370.4 1060.8 1369.78 1064.06 1369.82 1071.19Z" fill="#099805"/>
<path d="M1476.26 1092.02C1477.69 1078.64 1484.79 1065.1 1485.84 1051.1C1488.77 1012.11 1468.22 987.175 1434.52 985.249C1379.6 982.11 1371.26 988.879 1367.07 1040.93C1363.69 1039.27 1359.41 1038.6 1357.79 1036.17C1322.84 983.561 1296.64 977.059 1242.44 1007.97C1235.14 1012.14 1227.89 1016.4 1218.19 1021.54C1232.02 989.581 1243.63 952.713 1266.73 925.662C1276.64 914.068 1312.8 916.588 1332.79 924.058C1356.98 933.105 1372.36 936.982 1388.89 913.22C1404.33 891.008 1422.7 885.447 1439.27 914.33C1452.39 937.21 1466.62 959.457 1481.87 984.024C1491.48 1016.03 1499.58 1046.01 1509.36 1078.54C1500.41 1084.62 1489.79 1088.14 1476.26 1092.02Z" fill="#FBF8F4"/>
<path d="M1356.71 1107.84C1363.67 1104.05 1373.23 1099.9 1383.23 1097.94C1375.56 1102.58 1367.44 1105.02 1356.71 1107.84Z" fill="#0B9909"/>
<path d="M759.13 1108.99C756.494 1101.64 752.471 1096.21 753.708 1092.56C758.401 1078.73 761.776 1062.01 771.724 1053.36C777.394 1048.42 802.308 1053.52 805.443 1060.23C812.437 1075.2 815.713 1095.32 811.275 1110.67C809.287 1117.55 785.23 1118.69 770.947 1120.86C767.883 1121.33 763.782 1114.96 759.13 1108.99Z" fill="#02090C"/>
<path d="M636.928 1104.88C619.101 1108.19 597.74 1115.45 588.147 1107.85C579.38 1100.91 578.67 1076.19 583.587 1062.63C586.227 1055.35 617.114 1048.87 619.806 1052.75C629.577 1066.83 632.818 1085.44 636.928 1104.88Z" fill="#02090C"/>
<path d="M1294.06 1101.9C1270.71 1099.46 1249.2 1094.4 1252.26 1068.83C1253.81 1055.79 1268.13 1033.79 1275.87 1034.13C1292.53 1034.86 1314.69 1043.19 1323.77 1056.05C1343.26 1083.66 1317.5 1093 1294.06 1101.9Z" fill="#010901"/>
<path d="M1399.23 1062.93C1399.68 1048.12 1399.4 1026.04 1404.46 1024.75C1417.91 1021.29 1435.53 1023.47 1447.64 1030.27C1452.77 1033.16 1453.01 1056.68 1447.13 1064.76C1434.49 1082.16 1416.53 1082.98 1399.23 1062.93Z" fill="#010901"/>
<path d="M1369.5 1069.25C1369.78 1064.06 1370.4 1060.8 1371.02 1057.54C1372.29 1057.56 1373.55 1057.58 1374.82 1057.61C1374.82 1062.33 1374.93 1067.05 1374.71 1071.76C1374.68 1072.44 1372.96 1073.7 1372.32 1073.54C1371.35 1073.29 1370.64 1072.02 1369.5 1069.25Z" fill="#0B9909"/>
</g>
<defs>
<clipPath id="clip0_540_3892">
<rect width="1920" height="1280" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

-136
View File
@@ -1,136 +0,0 @@
<rml>
<head>
<link type="text/rcss" href="rml.rcss"/>
<link type="text/rcss" href="recomp.rcss"/>
<title>Inventory</title>
<style>
body
{
width: 100%;
height: 100%;
}
/* Hide the window icon. */
div#title_bar div#icon
{
display: none;
}
.flex-grid {
display: flex;
}
.col {
flex: 1;
text-align: center;
}
</style>
<link type="text/template" href="config_menu/general.rml" />
<link type="text/template" href="config_menu/controls.rml" />
<link type="text/template" href="config_menu/graphics.rml" />
<link type="text/template" href="config_menu/sound.rml" />
<link type="text/template" href="config_menu/mods.rml" />
<link type="text/template" href="config_menu/debug.rml" />
</head>
<body class="window">
<!-- <handle move_target="#document"> -->
<div id="window" class="rmlui-window" style="display:flex; flex-flow: column; background-color:rgba(0,0,0,0)" onkeydown="config_keydown">
<div class="centered-page" onclick="close_config_menu_backdrop">
<div class="centered-page__modal">
<tabset class="tabs" id="config_tabset">
<tab class="tab" autofocus id="tab_general">
<div>General</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config" data-model="general_model">
<template src="config-menu__general" />
</panel>
<tab class="tab" id="tab_controls">
<div>Controls</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config" data-model="controls_model">
<template src="config-menu__controls" />
</panel>
<tab class="tab" id="tab_graphics">
<div>Graphics</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config" data-model="graphics_model">
<template src="config-menu__graphics" />
</panel>
<tab class="tab" id="tab_sound">
<div>Sound</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config" data-model="sound_options_model">
<template src="config-menu__sound" />
</panel>
<tab class="tab" id="tab_mods">
<div>Mods</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config">
<template src="config-menu__mods" />
</panel>
<tab class="tab" data-model="debug_model" data-if="debug_enabled" id="tab_debug">
<div>Debug</div>
<div class="tab__indicator"></div>
</tab>
<panel class="config" data-model="debug_model">
<template src="config-menu__debug" />
</panel>
</tabset>
<div class="config__icon-buttons">
<button
class="icon-button"
onclick="open_quit_game_prompt"
id="config__quit-game-button"
>
<svg src="icons/Quit.svg" />
</button>
<button
class="icon-button"
onclick="close_config_menu"
id="config__close-menu-button"
>
<svg src="icons/X.svg" />
</button>
</div>
</div>
<div
class="centered-page__controls"
data-model="nav_help_model"
>
<label>
<span>Navigate</span>
<span class="prompt-font-sm">{{nav_help__navigate}}</span>
</label>
<label>
<span>Accept</span>
<span class="prompt-font-sm">{{nav_help__accept}}</span>
</label>
<label>
<span>Exit</span>
<span class="prompt-font-sm">{{nav_help__exit}}</span>
</label>
<!-- <label><span style="font-family:promptfont;">&#x21F3;</span> Navigate</label>
<label><span style="font-family:promptfont;">&#x21A7;</span> Accept</label> -->
</div>
</div>
<!-- <div
id="prompt-root"
data-model="prompt_model"
data-if="prompt__open"
data-alias-promptOpen="prompt__open"
data-alias-promptHeader="prompt__header"
data-alias-promptContent="prompt__content"
data-alias-promptConfirmLabel="prompt__confirmLabel"
data-alias-promptCancelLabel="prompt__cancelLabel"
data-event-click="prompt__on_click"
>
<template src="prompt"/>
</div> -->
</div>
<!-- </handle> -->
<!-- <handle size_target="#document" style="position: absolute; width: 16dp; height: 16dp; bottom: 0px; right: 0px; cursor: resize;"></handle> -->
</body>
</rml>
-360
View File
@@ -1,360 +0,0 @@
<template name="config-menu__controls">
<head>
</head>
<body>
<form class="config__form" data-attr-cur-input="cur_input_row" data-attr-cur-binding-slot="active_binding_slot">
<div class="config__header">
<div class="config__header-left">
<button
class="toggle"
id="cont_kb_toggle"
data-class-toggle--checked="input_device_is_keyboard"
onclick="toggle_input_device"
style="nav-down: #input_row_button_0_0; nav-up: #tab_controls"
>
<div class="toggle__border" />
<div class="toggle__floater" />
<div class="toggle__icons">
<div class="toggle__icon toggle__icon--left"><div>␼</div></div>
<div class="toggle__icon toggle__icon--right"><div>␽</div></div>
</div>
</button>
</div>
<div>
<button
class="button button--warning"
style="nav-down:#input_row_button_0_0"
data-event-click="reset_input_bindings_to_defaults"
>
<div class="button__label">Reset to defaults</div>
</button>
</div>
</div>
<div class="config__wrapper input-config">
<div class="input-config__horizontal-split">
<div class="input-config__mappings" data-event-mouseout="set_input_row_focus(-1)">
<div class="input-config__mappings-scroll">
<div class="input-config__mappings-wrapper">
<div
class="control-option"
data-attr-id="'input_row_' + i"
data-for="input_bindings, i : inputs.array"
data-event-mouseover="set_input_row_focus(i)"
data-class-control-option--active="get_input_enum_name(i)==cur_input_row"
data-if="!input_device_is_keyboard || (get_input_enum_name(i) != 'TOGGLE_MENU' && get_input_enum_name(i) != 'ACCEPT_MENU' && get_input_enum_name(i) != 'APPLY_MENU')"
>
<label
class="control-option__label"
>{{get_input_name(i)}}</label>
<div class="control-option__bindings">
<button
data-attr-id="'input_row_button_' + i + '_' + j"
data-event-blur="set_input_row_focus(-1)"
data-event-focus="set_input_row_focus(i)"
data-for="cur_binding, j : input_bindings"
data-event-click="set_input_binding(i,j)"
class="prompt-font control-option__binding"
data-attr-bind-slot="j"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
>
<div class="control-option__binding-recording">
<div class="control-option__binding-circle" />
<div class="control-option__binding-edge">
<svg class="control-option__binding-edge-svg" src="icons/RecordBorder.svg" />
</div>
</div>
<div class="control-option__binding-icon">{{cur_binding}}</div>
</button>
</div>
<button
data-if="get_input_enum_name(i) != 'TOGGLE_MENU' && get_input_enum_name(i) != 'ACCEPT_MENU'"
data-event-blur="set_input_row_focus(-1)"
data-event-focus="set_input_row_focus(i)"
data-event-click="clear_input_bindings(i)"
class="icon-button icon-button--danger"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
>
<svg src="icons/Trash.svg" />
</button>
<button
data-if="get_input_enum_name(i) == 'TOGGLE_MENU' || get_input_enum_name(i) == 'ACCEPT_MENU'"
data-event-blur="set_input_row_focus(-1)"
data-event-focus="set_input_row_focus(i)"
data-event-click="reset_single_input_binding_to_default(i)"
class="icon-button icon-button--danger"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
>
<svg src="icons/Reset.svg" />
</button>
</div>
</div>
</div>
</div>
<div class="input-config__visual-wrapper">
<div class="input-config__visual-aspect">
<div class="input-config__visual">
<!-- stick only -->
<div class="input-config__visual-stick-wrapper">
<div
class="input-viz input-config__visual-stick"
visual-input="X_AXIS_NEG X_AXIS_POS Y_AXIS_NEG Y_AXIS_POS"
>
<div class="input-viz__stick-split input-viz__stick-split--vertical">
<div class="input-viz input-viz__mappings" visual-input="Y_AXIS_POS">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--up" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.Y_AXIS_POS"
>
<div>{{cur_binding}}</div>
</div>
</div>
<div class="input-viz__dpad-divider" />
<div class="input-viz input-viz__mappings" visual-input="Y_AXIS_NEG">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--down" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.Y_AXIS_NEG"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div class="input-viz__stick-split input-viz__stick-split--horizontal">
<div class="input-viz input-viz__mappings" visual-input="X_AXIS_NEG">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--left" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.X_AXIS_NEG"
>
<div>{{cur_binding}}</div>
</div>
</div>
<div class="input-viz__dpad-divider" />
<div class="input-viz input-viz__mappings" visual-input="X_AXIS_POS">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--right" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.X_AXIS_POS"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- top half -->
<div class="input-config__visual-half">
<div class="input-config__visual-quarter-left">
<div
class="input-viz input-viz__dpad"
visual-input="DPAD_UP DPAD_DOWN DPAD_LEFT DPAD_RIGHT"
>
<svg src="icons/VizMap/DPad.svg" />
<div class="input-viz__dpad-split input-viz__dpad-split--vertical">
<div class="input-viz input-viz__mappings" visual-input="DPAD_UP">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--up" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.DPAD_UP"
>
<div>{{cur_binding}}</div>
</div>
</div>
<div class="input-viz__dpad-divider" />
<div class="input-viz input-viz__mappings" visual-input="DPAD_DOWN">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--down" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.DPAD_DOWN"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div class="input-viz__dpad-split input-viz__dpad-split--horizontal">
<div class="input-viz input-viz__mappings" visual-input="DPAD_LEFT">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--left" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.DPAD_LEFT"
>
<div>{{cur_binding}}</div>
</div>
</div>
<div class="input-viz__dpad-divider" />
<div class="input-viz input-viz__mappings" visual-input="DPAD_RIGHT">
<svg class="input-viz__dpad-arrow input-viz__dpad-arrow--right" src="icons/VizMap/DPadArrow.svg" />
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.DPAD_RIGHT"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
</div>
</div>
<div class="input-config__visual-quarter-right">
<div class="input-config__main-buttons">
<div
class="input-viz input-viz__button input-viz__button--sm input-viz__button--Start"
visual-input="START"
>
<svg src="icons/VizMap/ButtonSmall.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.START"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div
class="input-viz input-viz__button input-viz__button--lg input-viz__button--B"
visual-input="B"
>
<svg src="icons/VizMap/ButtonLarge.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.B"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div
class="input-viz input-viz__button input-viz__button--lg input-viz__button--A"
visual-input="A"
>
<svg src="icons/VizMap/ButtonLarge.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.A"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
</div>
<div class="input-config__c-buttons">
<div class="input-config__c-buttons-lr">
<div
class="input-viz input-viz__button input-viz__button--md input-viz__button--C"
visual-input="C_LEFT"
>
<svg src="icons/VizMap/ButtonMedium.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.C_LEFT"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div
class="input-viz input-viz__button input-viz__button--md input-viz__button--C"
visual-input="C_RIGHT"
>
<svg src="icons/VizMap/ButtonMedium.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.C_RIGHT"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
</div>
<div class="input-config__c-buttons-du">
<div
class="input-viz input-viz__button input-viz__button--md input-viz__button--C"
visual-input="C_DOWN"
>
<svg src="icons/VizMap/ButtonMedium.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.C_DOWN"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div
class="input-viz input-viz__button input-viz__button--sm input-viz__button--C"
visual-input="C_UP"
>
<svg src="icons/VizMap/ButtonMedium.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.C_UP"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- bottom half -->
<div class="input-config__visual-half input-config__visual-half--bottom">
<div
class="input-viz input-viz__Z"
visual-input="Z"
>
<svg src="icons/VizMap/Target.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.Z"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div
class="input-viz input-viz__R"
visual-input="R"
>
<svg src="icons/VizMap/Shield.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.R"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
<div
class="input-viz input-viz__L"
visual-input="L"
>
<svg src="icons/VizMap/Map.svg" />
<div class="input-viz__mappings">
<div
class="input-config__visual-mapping"
data-for="cur_binding, i : inputs.L"
>
<div>{{cur_binding}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</body>
</template>
-13
View File
@@ -1,13 +0,0 @@
<template name="config-menu__debug">
<head>
</head>
<body>
<form class="config__form">
<div class="config__wrapper">
<div class="config-debug">
</div>
</div>
</form>
</body>
</template>
-330
View File
@@ -1,330 +0,0 @@
<template name="config-menu__general">
<head>
</head>
<body>
<form class="config__form" id="conf-general__form">
<div class="config__hz-wrapper" id="conf-general__hz-wrapper">
<!-- Options -->
<div class="config__wrapper" data-event-mouseout="set_cur_config_index(-1)" id="conf-general__wrapper">
<!-- rumble strength -->
<div class="config-option" data-event-mouseover="set_cur_config_index(1)">
<label class="config-option__title">Rumble Strength</label>
<div class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{rumble_strength}}%</label>
<input
class="nav-vert"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(1)"
id="rumble_strength_input"
type="range"
min="0"
max="100"
style="flex: 1; margin: 0dp;"
data-value="rumble_strength"
/>
</div>
</div>
<!-- gyro sensitivity -->
<div class="config-option" data-event-mouseover="set_cur_config_index(2)">
<label class="config-option__title">Gyro Sensitivity</label>
<div class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{gyro_sensitivity}}%</label>
<input
class="nav-vert"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(2)"
id="gyro_sensitivity_input"
type="range"
min="0"
max="100"
style="flex: 1; margin: 0dp;"
data-value="gyro_sensitivity"
/>
</div>
</div>
<!-- mouse sensitivity -->
<div class="config-option" data-event-mouseover="set_cur_config_index(3)">
<label class="config-option__title">Mouse Sensitivity</label>
<div class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{mouse_sensitivity}}%</label>
<input
class="nav-vert"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(3)"
id="mouse_sensitivity_input"
type="range"
min="0"
max="100"
style="flex: 1; margin: 0dp;"
data-value="mouse_sensitivity"
/>
</div>
</div>
<!-- joystick deadzone -->
<div class="config-option" data-event-mouseover="set_cur_config_index(4)">
<label class="config-option__title">Joystick Deadzone</label>
<div class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{joystick_deadzone}}%</label>
<input
class="nav-vert"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(4)"
id="joystick_deadzone_input"
type="range"
min="0"
max="100"
style="flex: 1; margin: 0dp; nav-down: #bg_input_enabled"
data-value="joystick_deadzone"
/>
</div>
</div>
<!-- background input -->
<div class="config-option" data-event-mouseover="set_cur_config_index(5)" id="conf-general__Background-Input">
<label class="config-option__title">Background Input</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(5)"
name="background_input_mode"
data-checked="background_input_mode"
value="On"
id="bg_input_enabled"
style="nav-up: #joystick_deadzone_input; nav-down: #camera_inversion_none"
/>
<label class="config-option__tab-label" for="bg_input_enabled">On</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(5)"
name="background_input_mode"
data-checked="background_input_mode"
value="Off"
id="bg_input_disabled"
style="nav-up: #joystick_deadzone_input; nav-down: #camera_inversion_x"
/>
<label class="config-option__tab-label" for="bg_input_disabled">Off</label>
</div>
</div>
<!-- camera inversion -->
<div class="config-option" data-event-mouseover="set_cur_config_index(7)">
<label class="config-option__title">Aiming Camera Mode</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertNone"
id="camera_inversion_none"
style="nav-up: #bg_input_enabled; nav-down: #analog_cam_enabled"
/>
<label class="config-option__tab-label" for="camera_inversion_none">None</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertX"
id="camera_inversion_x"
style="nav-up: #bg_input_disabled; nav-down: #analog_cam_disabled"
/>
<label class="config-option__tab-label" for="camera_inversion_x">Invert X</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertY"
id="camera_inversion_y"
style="nav-up: #bg_input_disabled; nav-down: #analog_cam_disabled"
/>
<label class="config-option__tab-label" for="camera_inversion_y">Invert Y</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertBoth"
id="camera_inversion_both"
style="nav-up: #bg_input_disabled; nav-down: #analog_cam_disabled"
/>
<label class="config-option__tab-label" for="camera_inversion_both">Invert Both</label>
</div>
</div>
<!-- analog camera -->
<div class="config-option" data-event-mouseover="set_cur_config_index(8)">
<label class="config-option__title">Analog Camera</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(8)"
name="analog_cam_mode"
data-checked="analog_cam_mode"
value="On"
id="analog_cam_enabled"
style="nav-up: #camera_inversion_none; nav-down: #analog_camera_inversion_none"
/>
<label class="config-option__tab-label" for="analog_cam_enabled">On</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(8)"
name="analog_cam_mode"
data-checked="analog_cam_mode"
value="Off"
id="analog_cam_disabled"
style="nav-up: #camera_inversion_x; nav-down: #analog_camera_inversion_x"
/>
<label class="config-option__tab-label" for="analog_cam_disabled">Off</label>
</div>
</div>
<!-- analog camera inversion -->
<div class="config-option" data-event-mouseover="set_cur_config_index(9)">
<label class="config-option__title">Analog Camera Mode</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(9)"
name="analog_camera_invert_mode"
data-checked="analog_camera_invert_mode"
value="InvertNone"
id="analog_camera_inversion_none"
style="nav-up: #analog_cam_enabled; nav-down: #note_saving_enabled"
/>
<label class="config-option__tab-label" for="analog_camera_inversion_none">None</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(9)"
name="analog_camera_invert_mode"
data-checked="analog_camera_invert_mode"
value="InvertX"
id="analog_camera_inversion_x"
style="nav-up: #analog_cam_disabled; nav-down: #note_saving_disabled"
/>
<label class="config-option__tab-label" for="analog_camera_inversion_x">Invert X</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(9)"
name="analog_camera_invert_mode"
data-checked="analog_camera_invert_mode"
value="InvertY"
id="analog_camera_inversion_y"
style="nav-up: #analog_cam_disabled; nav-down: #note_saving_disabled"
/>
<label class="config-option__tab-label" for="analog_camera_inversion_y">Invert Y</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(9)"
name="analog_camera_invert_mode"
data-checked="analog_camera_invert_mode"
value="InvertBoth"
id="analog_camera_inversion_both"
style="nav-up: #analog_cam_disabled; nav-down: #note_saving_disabled"
/>
<label class="config-option__tab-label" for="analog_camera_inversion_both">Invert Both</label>
</div>
</div>
<!-- note saving -->
<div class="config-option" data-event-mouseover="set_cur_config_index(10)">
<label class="config-option__title">Note Saving</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(10)"
name="note_saving_mode"
data-checked="note_saving_mode"
value="On"
id="note_saving_enabled"
style="nav-up: #analog_camera_inversion_none"
/>
<label class="config-option__tab-label" for="note_saving_enabled">On</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(10)"
name="note_saving_mode"
data-checked="note_saving_mode"
value="Off"
id="note_saving_disabled"
style="nav-up: #analog_camera_inversion_x"
/>
<label class="config-option__tab-label" for="note_saving_disabled">Off</label>
</div>
</div>
</div>
<!-- Descriptions -->
<div class="config__wrapper">
<p data-if="cur_config_index == 1">
Controls the strength of rumble when using a controller that supports it. <b>Setting this to zero will disable rumble.</b>
</p>
<p data-if="cur_config_index == 2">
Controls the sensitivity of gyro aiming when using items in first person for controllers that support it. <b>Setting this to zero will disable gyro.</b>
<br />
<br />
<b>Note: To recalibrate controller gyro, set the controller down on a still, flat surface for 5 seconds.</b>
</p>
<p data-if="cur_config_index == 3">
Controls the sensitivity of mouse aiming when using items in first person. <b>Setting this to zero will disable mouse aiming.</b>
<br />
<br />
<b>Note: This option does not allow mouse buttons to activate items. Mouse aiming is intended to be used with inputs that are mapped to mouse movement, such as gyro on Steam Deck.</b>
</p>
<p data-if="cur_config_index == 4">
Applies a deadzone to joystick inputs.
</p>
<p data-if="cur_config_index == 5">
Allows the game to read controller input when out of focus.
<br/>
<b>This setting does not affect keyboard input.</b>
</p>
<p data-if="cur_config_index == 7">
Inverts the camera controls for first-person aiming. <b>Invert Y</b> is the default and matches the original game.
</p>
<p data-if="cur_config_index == 8">
Enables an analog "free" camera similar to later entries in the series that's mapped to the right analog stick on the controller.
<br/>
<br/>
When you move the right stick, the camera will enter free mode and stop centering behind Link. Press the <b>Target</b> button at any time to go back into the normal camera mode. The camera will also return to normal mode after a cutscene plays or when you move between areas.
<br/>
<br/>
This option also enables right stick control while looking and aiming.
</p>
<p data-if="cur_config_index == 9">
Inverts the camera controls for the analog camera if it's enabled. <b>None</b> is the default.
</p>
<p data-if="cur_config_index == 10">
Note saving DESCRIPTION TODO.
</p>
</div>
</div>
</form>
</body>
</template>
-325
View File
@@ -1,325 +0,0 @@
<template name="config-menu__graphics">
<head>
</head>
<body>
<form class="config__form">
<div class="config__hz-wrapper">
<div class="config__wrapper" data-event-mouseout="set_cur_config_index(-1)">
<div class="config-option" data-event-mouseover="set_cur_config_index(0)">
<label class="config-option__title">Resolution</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(0)"
name="resolution"
data-checked="res_option"
value="Original"
id="res_original"
style="nav-up:#tab_graphics; nav-down: #ds_windowed"
data-attr-style="res_option == 'Auto' ? 'nav-up:#tab_graphics; nav-down: #ar_original' : 'nav-up:#tab_graphics; nav-down: #ds_windowed'"
/>
<label class="config-option__tab-label" for="res_original">Original</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(0)"
name="resolution"
data-checked="res_option"
value="Original2x"
id="res_2x"
style="nav-up:#tab_graphics; nav-down: #ds_2x"
data-attr-style="res_option == 'Auto' ? 'nav-up:#tab_graphics; nav-down: #ar_expand' : 'nav-up:#tab_graphics; nav-down: #ds_2x'"
/>
<label class="config-option__tab-label" for="res_2x">Original 2x</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(0)"
name="resolution"
data-checked="res_option"
value="Auto"
id="res_auto"
style="nav-up:#tab_graphics; nav-down: #ds_4x"
data-attr-style="res_option == 'Auto' ? 'nav-up:#tab_graphics; nav-down: #ar_expand' : 'nav-up:#tab_graphics; nav-down: #ds_4x'"
/>
<label class="config-option__tab-label" for="res_auto">Auto</label>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(1)">
<label class="config-option__title">Downsampling Quality</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(1)"
name="downsampling"
data-attrif-disabled="res_option == 'Auto'"
data-checked="ds_option"
value="1"
id="ds_windowed"
style="nav-up: #res_original; nav-down: #ar_original"
/>
<label class="config-option__tab-label" for="ds_windowed">Off</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(1)"
name="downsampling"
data-attrif-disabled="res_option == 'Auto'"
data-checked="ds_option"
value="2"
id="ds_2x"
style="nav-up: #res_2x; nav-down: #ar_expand"
/>
<label class="config-option__tab-label" for="ds_2x">2x</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(1)"
name="downsampling"
data-attrif-disabled="res_option == 'Auto'"
data-checked="ds_option"
value="4"
id="ds_4x"
style="nav-up: #res_auto; nav-down: #ar_expand"
/>
<label class="config-option__tab-label" for="ds_4x">4x</label>
<div class="config-option__details">{{ds_info}}</div>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(2)">
<label class="config-option__title">Aspect Ratio</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(2)"
name="aspectratio"
data-checked="ar_option"
value="Original"
id="ar_original"
style="nav-up: #ds_windowed; nav-down: #wm_windowed"
data-attr-style="res_option == 'Auto' ? 'nav-up:#res_original; nav-down: #wm_windowed' : 'nav-up:#ds_windowed; nav-down: #wm_windowed'"
/>
<label class="config-option__tab-label" for="ar_original">Original</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(2)"
name="aspectratio"
data-checked="ar_option"
value="Expand"
id="ar_expand"
style="nav-up: #ds_2x; nav-down: #wm_fullscreen"
data-attr-style="res_option == 'Auto' ? 'nav-up:#res_2x; nav-down: #wm_fullscreen' : 'nav-up:#ds_2x; nav-down: #wm_fullscreen'"
/>
<label class="config-option__tab-label" for="ar_expand">Expand</label>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(3)">
<label class="config-option__title">Window Mode</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(3)"
name="windowmode"
data-checked="wm_option"
value="Windowed"
id="wm_windowed"
style="nav-up: #ar_original; nav-down: #rr_original"
/>
<label class="config-option__tab-label" for="wm_windowed">Windowed</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(3)"
name="windowmode"
data-checked="wm_option"
value="Fullscreen"
id="wm_fullscreen"
style="nav-up: #ar_expand; nav-down: #rr_display"
/>
<label class="config-option__tab-label" for="wm_fullscreen">Fullscreen</label>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(4)">
<label class="config-option__title">Framerate</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(4)"
name="refreshrate"
data-checked="rr_option"
value="Original"
id="rr_original"
data-attr-style="rr_option=='Manual' ? 'nav-up: #wm_windowed; nav-down: #rr_manual_input' : 'nav-up: #wm_windowed; nav-down: #msaa_none'"
/>
<label class="config-option__tab-label" for="rr_original">Original</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(4)"
name="refreshrate"
data-checked="rr_option"
value="Display"
id="rr_display"
style="nav-up: #wm_fullscreen"
data-style-nav-down="rr_option=='Manual' ? '#rr_manual_input' : (msaa2x_supported ? '#msaa_2x' : '#msaa_none')"
/>
<label class="config-option__tab-label" for="rr_display">Display</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(4)"
name="refreshrate"
data-checked="rr_option"
value="Manual"
id="rr_manual"
style="nav-up: #wm_fullscreen"
data-style-nav-down="rr_option=='Manual' ? '#rr_manual_input' : (msaa4x_supported ? '#msaa_4x' : (msaa2x_supported ? '#msaa_2x' : '#msaa_none'))"
/>
<label class="config-option__tab-label" for="rr_manual">Manual</label>
</div>
<div data-if="rr_option=='Manual'" class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{rr_manual_value}}</label>
<input
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(4)"
id="rr_manual_input"
type="range"
min="20"
max="360"
style="flex:1;margin: 0dp;nav-up:#rr_manual;nav-down:#msaa_none;"
data-value="rr_manual_value"
/>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(5)">
<label class="config-option__title">MS Anti-Aliasing</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(5)"
name="antialiasing"
data-checked="msaa_option"
value="None"
id="msaa_none"
data-attr-style="rr_option=='Manual' ? 'nav-up: #rr_manual_input; nav-down: #hr_original' : 'nav-up: #rr_original; nav-down: #hr_original'"
/>
<label class="config-option__tab-label" for="msaa_none">None</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(5)"
name="antialiasing"
data-attrif-disabled="!msaa2x_supported"
data-checked="msaa_option"
value="MSAA2X"
id="msaa_2x"
data-attr-style="rr_option=='Manual' ? 'nav-up: #rr_manual_input; nav-down: #hr_16_9' : 'nav-up: #rr_display; nav-down: #hr_16_9'"
data-style-nav-right="msaa4x_supported ? '#msaa_4x' : 'none'"
/>
<label class="config-option__tab-label" for="msaa_2x">2x</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(5)"
name="antialiasing"
data-attrif-disabled="!msaa4x_supported"
data-checked="msaa_option"
value="MSAA4X"
id="msaa_4x"
data-attr-style="rr_option=='Manual' ? 'nav-up: #rr_manual_input; nav-down: #hr_full' : 'nav-up: #rr_manual; nav-down: #hr_full'"
/>
<label class="config-option__tab-label" for="msaa_4x">4x</label>
<div class="config-option__details" data-if="!sample_positions_supported">Not available (missing sample positions support)</div>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(6)">
<label class="config-option__title">HUD Placement</label>
<div class="config-option__list">
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(6)"
name="hr-option"
data-checked="hr_option"
value="Original"
id="hr_original"
style="nav-up: #msaa_none; nav-down: #apply_button"
/>
<label class="config-option__tab-label" for="hr_original">Original</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(6)"
name="hr-option"
data-checked="hr_option"
value="Clamp16x9"
id="hr_16_9"
style="nav-up: #msaa_2x; nav-down: #apply_button"
data-style-nav-up="msaa2x_supported ? '#msaa_2x' : '#msaa_none'"
/>
<label class="config-option__tab-label" for="hr_16_9">16:9</label>
<input type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(6)"
name="hr-option"
data-checked="hr_option"
value="Full"
id="hr_full"
style="nav-up: #msaa_4x; nav-down: #apply_button"
data-style-nav-up="msaa4x_supported ? '#msaa_4x' : (msaa2x_supported ? '#msaa_2x' : '#msaa_none')"
/>
<label class="config-option__tab-label" for="hr_full">Expand</label>
</div>
</div>
</div>
<div class="config__wrapper">
<p data-if="cur_config_index == 0">
Sets the output resolution of the game. <b>Original</b> matches the game's original 240p resolution. <b>Original 2x</b> will render at 480p. <b>Auto</b> will scale based on the game window's resolution.
</p>
<p data-if="cur_config_index == 1">
Renders at a higher resolution and scales it down to the output resolution for increased quality. Only available in <b>Original</b> and <b>Original 2x</b> resolution.
<br />
<br />
Note: <b>4x</b> downsampling quality at <b>Original 2x</b> resolution may cause performance issues on low end devices, as it will cause the game to render <i>at almost 4k internal resolution</i>.
</p>
<p data-if="cur_config_index == 2">
Sets the horizontal aspect ratio. <b>Original</b> uses the game's original 4:3 aspect ratio. <b>Expand</b> will adjust to match the game window's aspect ratio.
</p>
<p data-if="cur_config_index == 3">
Sets whether the game should display <b>Windowed</b> or <b>Fullscreen</b>. You can also use <b>F11</b> or <b>Alt + Enter</b> to toggle this option.
</p>
<p data-if="cur_config_index == 4">
Sets the game's output framerate. This option does not affect gameplay.
<br />
<br />
Note: If you have issues with <b>Display</b> mode while using an external frame limiter, use <b>Manual</b> mode instead and configure it to that same frame limit.
<br />
<br />
<b>Detected display refresh rate: {{display_refresh_rate}}hz</b>
</p>
<p data-if="cur_config_index == 5">
Sets the multisample anti-aliasing (MSAA) quality level. This reduces jagged edges in the final image at the expense of rendering performance.
<br />
<br />
<b>Note: This option won't be available if your GPU does not support programmable MSAA sample positions, as it is currently required to avoid rendering glitches.</b>
</p>
<p data-if="cur_config_index == 6">
Adjusts the placement of HUD elements to fit the selected aspect ratio. <b>Expand</b> will use the aspect ratio of the game's output window.
</p>
</div>
</div>
<div class="config__footer">
<!-- this empty div makes sure Apply button gets right aligned -->
<div />
<div>
<button
class="button button--secondary"
nav-return="rr_manual"
data-attrif-disabled="!options_changed"
onclick="apply_options"
id="apply_button"
style="nav-up:#hr_original"
>
<div class="button__label">Apply<span class="prompt-font-sm">{{gfx_help__apply}}</span></div>
</button>
</div>
</div>
</form>
</body>
</template>
-9
View File
@@ -1,9 +0,0 @@
<template name="config-menu__mods">
<head>
</head>
<body>
<form class="config__form">
<recomp-mod-menu id="menu_mods" />
</form>
</body>
</template>
-57
View File
@@ -1,57 +0,0 @@
<template name="config-menu__sound">
<head>
</head>
<body>
<form class="config__form">
<div class="config__hz-wrapper">
<!-- Options -->
<div class="config__wrapper" data-event-mouseout="set_cur_config_index(-1)">
<div class="config-option" data-event-mouseover="set_cur_config_index(0)">
<label class="config-option__title">Main Volume</label>
<div class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{main_volume}}%</label>
<input
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(0)"
class="nav-vert"
id="main_volume_input"
type="range"
min="0"
max="100"
style="flex: 1; margin: 0dp; nav-up: #tab_sound; nav-down: #bgm_volume_input;"
data-value="main_volume"
/>
</div>
</div>
<div class="config-option" data-event-mouseover="set_cur_config_index(1)">
<label class="config-option__title">Background Music Volume</label>
<div class="config-option__range-wrapper config-option__list">
<label class="config-option__range-label">{{bgm_volume}}%</label>
<input
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(1)"
class="nav-vert"
id="bgm_volume_input"
type="range"
min="0"
max="100"
style="flex: 1; margin: 0dp; nav-up: #main_volume_input;"
data-value="bgm_volume"
/>
</div>
</div>
</div>
<!-- Descriptions -->
<div class="config__wrapper">
<p data-if="cur_config_index == 0">
Controls the main volume of the game.
</p>
<p data-if="cur_config_index == 1">
Controls the overall volume of background music.
</p>
</div>
</div>
</form>
</body>
</template>
-70
View File
@@ -1,70 +0,0 @@
<rml>
<head>
<link type="text/rcss" href="rml.rcss"/>
<link type="text/rcss" href="recomp.rcss"/>
<title>Inventory</title>
<style>
body
{
width: 100%;
height: 100%;
}
/* Hide the window icon. */
div#title_bar div#icon
{
display: none;
}
.flex-grid {
display: flex;
}
.col {
flex: 1;
text-align: center;
}
</style>
</head>
<body class="window">
<!-- <handle move_target="#document"> -->
<div id="window" class="rmlui-window" style="display:flex; flex-flow: column; background-color:rgba(0,0,0,0)" onkeydown="config_keydown">
<div class="centered-page" onclick="close_config_menu_backdrop">
<div class="centered-page__modal">
<div class="config__icon-buttons">
<button
class="icon-button"
onclick="open_quit_game_prompt"
id="config__quit-game-button"
>
<svg src="icons/Quit.svg" />
</button>
<button
class="icon-button"
onclick="close_config_menu"
id="config__close-menu-button"
>
<svg src="icons/X.svg" />
</button>
</div>
<recomp-config-sub-menu id="config_sub_menu" />
</div>
<div
class="centered-page__controls"
data-model="nav_help_model"
>
<label>
<span>Navigate</span>
<span class="prompt-font-sm">{{nav_help__navigate}}</span>
</label>
<label>
<span>Accept</span>
<span class="prompt-font-sm">{{nav_help__accept}}</span>
</label>
<label>
<span>Exit</span>
<span class="prompt-font-sm">{{nav_help__exit}}</span>
</label>
</div>
</div>
</div>
</body>
</rml>
+3
View File
@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 11L15.2929 22.2929C15.6834 22.6834 16.3166 22.6834 16.7071 22.2929L28 11" stroke="#FFFFFF" stroke-width="8" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 248 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.72625 23.3925C8.13475 23.0659 8.61021 22.3958 9.032 21.7129C9.78396 20.4953 11.0499 19.6669 12.4578 19.6669H19.5422C20.9501 19.6669 22.216 20.4953 22.968 21.7129C23.3898 22.3958 23.8653 23.0659 24.2738 23.3925C25.2857 24.2015 26.6008 24.2035 27.6585 23.3925C28.4268 22.8033 29 21.7232 29 20.2983C29 18.8735 28.6384 12.9609 27.6585 9.98045C26.6786 7 23.4285 7 16 7C8.57147 7 5.32145 7 4.34153 9.98045C3.36162 12.9609 3 18.8735 3 20.2983C3 21.7232 3.5732 22.8033 4.34153 23.3925C5.39916 24.2035 6.71431 24.2015 7.72625 23.3925Z" stroke="white" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 676 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27 7C29.2091 7 31 8.79086 31 11V21C31 23.2091 29.2091 25 27 25H5C2.79086 25 1 23.2091 1 21V11C1 8.79086 2.79086 7 5 7H27ZM5 19V21H7V19H5ZM9 19V21H23V19H9ZM25 19V21H27V19H25ZM5 15V17H9V15H5ZM11 15V17H13V15H11ZM15 15V17H17V15H15ZM19 15V17H21V15H19ZM23 15V17H27V15H23ZM5 11V13H7V11H5ZM9 11V13H11V11H9ZM13 11V13H15V11H13ZM17 11V13H19V11H17ZM21 11V13H23V11H21ZM25 11V13H27V11H25Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 504 B

+17
View File
@@ -0,0 +1,17 @@
<svg width="48" height="20" viewBox="0 0 48 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_543_3678)">
<g clip-path="url(#clip1_543_3678)">
<path d="M8 5V15" stroke="white" stroke-width="4" stroke-linecap="round"/>
<path d="M3 10L13 10" stroke="white" stroke-width="4" stroke-linecap="round"/>
</g>
<path d="M43 1C45.2091 1 47 2.79086 47 5V15C47 17.2091 45.2091 19 43 19H21C18.7909 19 17 17.2091 17 15V5C17 2.79086 18.7909 1 21 1H43ZM21 13V15H23V13H21ZM25 13V15H39V13H25ZM41 13V15H43V13H41ZM21 9V11H25V9H21ZM27 9V11H29V9H27ZM31 9V11H33V9H31ZM35 9V11H37V9H35ZM39 9V11H43V9H39ZM21 5V7H23V5H21ZM25 5V7H27V5H25ZM29 5V7H31V5H29ZM33 5V7H35V5H33ZM37 5V7H39V5H37ZM41 5V7H43V5H41Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_543_3678">
<rect width="48" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_543_3678">
<rect width="16" height="16" fill="white" transform="translate(0 2)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 946 B

+6
View File
@@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 3C24.091 3.00019 28.7417 10.0042 28.9902 16.082C29.0372 17.2318 28.8162 18.5326 28.2207 19.4746C27.7079 20.2859 26.8271 21 25 21H7C5.17292 21 4.29117 20.2859 3.77832 19.4746C3.18306 18.5326 2.96285 17.2316 3.00977 16.082C3.25811 10.0041 7.90882 3 16 3Z" stroke="white" stroke-width="4" stroke-linecap="round"/>
<circle cx="16" cy="14.5" r="2.5" fill="white"/>
<circle cx="22.5" cy="14.5" r="2.5" fill="white"/>
<circle cx="9.5" cy="14.5" r="2.5" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 577 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00008 11C9.00008 6.5 12.0002 4 16.0002 4C20.0002 4 23.0002 7 23.0002 11C23.0002 15 20.0002 18 16.0002 18" stroke="white" stroke-width="4" stroke-linecap="round"/>
<path d="M16 22V18" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="16" cy="28" r="2" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 424 B

+16
View File
@@ -0,0 +1,16 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_541_3853)">
<circle cx="16" cy="28" r="3" fill="white"/>
<mask id="mask0_541_3853" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="32">
<path d="M32 32H16C18.2091 32 20 30.2091 20 28C20 25.7909 18.2091 24 16 24C13.7909 24 12 25.7909 12 28C12 30.2091 13.7909 32 16 32H0V0H32V32Z" fill="white"/>
</mask>
<g mask="url(#mask0_541_3853)">
<circle cx="16" cy="16" r="12" stroke="white" stroke-width="4"/>
</g>
</g>
<defs>
<clipPath id="clip0_541_3853">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 673 B

-70
View File
@@ -1,70 +0,0 @@
<rml>
<head>
<title>Launcher</title>
<link type="text/rcss" href="rml.rcss"/>
<link type="text/rcss" href="recomp.rcss"/>
<style>
body
{
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="window" data-model="launcher_model">
<div class="launcher">
<!--<div class="launcher__vertical-split">
<div class="launcher__title-quadrant">
<button class="subtitle-title" disabled>
<div><h3>Banjo: Recompiled</h3></div>
<div><h1>Banjo-Tooie</h1></div>
<div class="subtitle-title__disclaimer">Coming Soon™</div>
</button>
</div>
<div class="launcher__content-quadrant"></div>
</div>-->
<div class="launcher__vertical-split launcher__vertical-split--right">
<div class="launcher__background-wrapper">
<!-- <svg src="[changeme].svg" class="launcher__background-bk" /> -->
</div>
<div class="launcher__title-quadrant launcher__title-quadrant--right">
<button class="subtitle-title subtitle-title--right" selected>
<div><h3>Banjo: Recompiled</h3></div>
<div><h1>Banjo-Kazooie</h1></div>
</button>
</div>
<div class="launcher__content-quadrant">
<button data-if="!bk_rom_valid" onclick="select_rom" class="menu-list-item menu-list-item--right" autofocus>
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Select ROM</div>
</button>
<button data-if="bk_rom_valid" onclick="start_game" class="menu-list-item menu-list-item--right" autofocus>
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Start game</div>
</button>
<button onclick="open_controls" class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Setup controls</div>
</button>
<button onclick="open_settings" class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Settings</div>
</button>
<button onclick="open_mods" class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Mods</div>
</button>
<button onclick="exit_game" class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Exit</div>
</button>
</div>
</div>
<div class="bottom-left">
<label>v{{version_number}}</label>
</div>
</div>
</div>
</body>
</rml>
-335
View File
@@ -1,335 +0,0 @@
@font-face{font-family:'promptfont'; src:url('promptfont.ttf');}
.pf{font-family:promptfont;}
.pf-exchange::after{content:'\u2194';}
.pf-reverse::after{content:'\u2195';}
.pf-left-trigger-lt::after{content:'\u2196';}
.pf-right-trigger-rt::after{content:'\u2197';}
.pf-left-shoulder-lb::after{content:'\u2198';}
.pf-right-shoulder-rb::after{content:'\u2199';}
.pf-left-trigger-zl::after{content:'\u219A';}
.pf-right-trigger-zr::after{content:'\u219B';}
.pf-left-shoulder-l::after{content:'\u219C';}
.pf-right-shoulder-r::after{content:'\u219D';}
.pf-dpad-left::after{content:'\u219E';}
.pf-dpad-up::after{content:'\u219F';}
.pf-dpad-right::after{content:'\u21A0';}
.pf-dpad-down::after{content:'\u21A1';}
.pf-dpad-left-right::after{content:'\u21A2';}
.pf-dpad-up-down::after{content:'\u21A3';}
.pf-button-left-x::after{content:'\u21A4';}
.pf-button-up-y::after{content:'\u21A5';}
.pf-button-right-b::after{content:'\u21A6';}
.pf-button-down-a::after{content:'\u21A7';}
.pf-left-analog-clockwise::after{content:'\u21A9';}
.pf-left-analog-counter::after{content:'\u21AA';}
.pf-right-analog-clockwise::after{content:'\u21AB';}
.pf-right-analog-counter::after{content:'\u21AC';}
.pf-both-analog-clockwise::after{content:'\u21AD';}
.pf-both-analog-counter::after{content:'\u21AE';}
.pf-left-shoulder-l1::after{content:'\u21B0';}
.pf-right-shoulder-r1::after{content:'\u21B1';}
.pf-left-trigger-l2::after{content:'\u21B2';}
.pf-right-trigger-r2::after{content:'\u21B3';}
.pf-dpad-left-down::after{content:'\u21B4';}
.pf-dpad-up-right::after{content:'\u21B5';}
.pf-analog-clockwise::after{content:'\u21B6';}
.pf-analog-counter::after{content:'\u21B7';}
.pf-both-analog-click::after{content:'\u21B9';}
.pf-left-analog-click::after{content:'\u21BA';}
.pf-right-analog-click::after{content:'\u21BB';}
.pf-left-analog-left::after{content:'\u21BC';}
.pf-right-analog-left::after{content:'\u21BD';}
.pf-left-analog-up::after{content:'\u21BE';}
.pf-right-analog-up::after{content:'\u21BF';}
.pf-left-analog-right::after{content:'\u21C0';}
.pf-right-analog-right::after{content:'\u21C1';}
.pf-left-analog-down::after{content:'\u21C2';}
.pf-right-analog-down::after{content:'\u21C3';}
.pf-left-analog-left-right::after{content:'\u21C4';}
.pf-left-analog-up-down::after{content:'\u21C5';}
.pf-right-analog-left-right::after{content:'\u21C6';}
.pf-analog-left::after{content:'\u21C7';}
.pf-analog-up::after{content:'\u21C8';}
.pf-analog-right::after{content:'\u21C9';}
.pf-analog-down::after{content:'\u21CA';}
.pf-left-analog::after{content:'\u21CB';}
.pf-right-analog::after{content:'\u21CC';}
.pf-dpad::after{content:'\u21CE';}
.pf-button-x::after{content:'\u21D0';}
.pf-button-y::after{content:'\u21D1';}
.pf-button-b::after{content:'\u21D2';}
.pf-button-a::after{content:'\u21D3';}
.pf-analog-left-right::after{content:'\u21D4';}
.pf-analog-up-down::after{content:'\u21D5';}
.pf-analog-up-left::after{content:'\u21D6';}
.pf-analog-up-right::after{content:'\u21D7';}
.pf-analog-down-right::after{content:'\u21D8';}
.pf-analog-down-left::after{content:'\u21D9';}
.pf-left-analog-touch::after{content:'\u21DA';}
.pf-right-analog-touch::after{content:'\u21DB';}
.pf-left-trigger-pull::after{content:'\u21DC';}
.pf-right-trigger-pull::after{content:'\u21DD';}
.pf-dpad-right-down::after{content:'\u21DE';}
.pf-dpad-left-up::after{content:'\u21DF';}
.pf-button-square::after{content:'\u21E0';}
.pf-button-triangle::after{content:'\u21E1';}
.pf-button-circle::after{content:'\u21E2';}
.pf-button-cross::after{content:'\u21E3';}
.pf-steam-menu::after{content:'\u21E4';}
.pf-options-menu::after{content:'\u21E5';}
.pf-dualshock-share::after{content:'\u21E6';}
.pf-dualshock-touchpad::after{content:'\u21E7';}
.pf-dualshock-options::after{content:'\u21E8';}
.pf-gamecube-z::after{content:'\u21E9';}
.pf-back-trigger-z::after{content:'\u21EA';}
.pf-button-c::after{content:'\u21EB';}
.pf-button-z::after{content:'\u21EC';}
.pf-button-alt-1::after{content:'\u21ED';}
.pf-button-alt-2::after{content:'\u21EE';}
.pf-left-analog-any::after{content:'\u21F1';}
.pf-right-analog-any::after{content:'\u21F2';}
.pf-analog-any::after{content:'\u21F3';}
.pf-right-analog-up-down::after{content:'\u21F5';}
.pf-select-share::after{content:'\u21F7';}
.pf-start::after{content:'\u21F8';}
.pf-home-menu::after{content:'\u21F9';}
.pf-share-capture::after{content:'\u21FA';}
.pf-burger-menu::after{content:'\u21FB';}
.pf-minus::after{content:'\u21FD';}
.pf-plus::after{content:'\u21FE';}
.pf-joycon-dpad-left::after{content:'\u21FF';}
.pf-joycon-dpad-up::after{content:'\u2200';}
.pf-joycon-dpad-right::after{content:'\u2201';}
.pf-joycon-dpad-down::after{content:'\u2202';}
.pf-joycon-sl::after{content:'\u2203';}
.pf-joycon-sr::after{content:'\u2204';}
.pf-lenovo-legion-quick-settings::after{content:'\u2205';}
.pf-dualsense-share::after{content:'\u2206';}
.pf-dualsense-touchpad::after{content:'\u2207';}
.pf-dualsense-options::after{content:'\u2208';}
.pf-ayaneo-lc::after{content:'\u2209';}
.pf-ayaneo-rc::after{content:'\u220A';}
.pf-ayaneo-wave::after{content:'\u220B';}
.pf-ayn-home::after{content:'\u220C';}
.pf-ayn-lcc::after{content:'\u220D';}
.pf-gpd-c1::after{content:'\u220E';}
.pf-gpd-c2::after{content:'\u220F';}
.pf-onexplayer-keyboard::after{content:'\u2210';}
.pf-onexplayer-turbo::after{content:'\u2211';}
.pf-m1::after{content:'\u2212';}
.pf-m2::after{content:'\u2213';}
.pf-m3::after{content:'\u2214';}
.pf-y1::after{content:'\u2215';}
.pf-y2::after{content:'\u2216';}
.pf-y3::after{content:'\u2217';}
.pf-onexplayer-function::after{content:'\u2218';}
.pf-onexplayer-home::after{content:'\u2219';}
.pf-left-trackpad-any::after{content:'\u2264';}
.pf-right-trackpad-any::after{content:'\u2265';}
.pf-left-trackpad-click::after{content:'\u2266';}
.pf-right-trackpad-click::after{content:'\u2267';}
.pf-left-trackpad-touch::after{content:'\u2268';}
.pf-right-trackpad-touch::after{content:'\u2269';}
.pf-left-trackpad-left::after{content:'\u226E';}
.pf-right-trackpad-left::after{content:'\u226F';}
.pf-left-trackpad-up::after{content:'\u2270';}
.pf-right-trackpad-up::after{content:'\u2271';}
.pf-left-trackpad-right::after{content:'\u2272';}
.pf-right-trackpad-right::after{content:'\u2273';}
.pf-left-trackpad-down::after{content:'\u2274';}
.pf-right-trackpad-down::after{content:'\u2275';}
.pf-steamdeck-l4::after{content:'\u2276';}
.pf-steamdeck-r4::after{content:'\u2277';}
.pf-steamdeck-l5::after{content:'\u2278';}
.pf-steamdeck-r5::after{content:'\u2279';}
.pf-xbox-dpad-left::after{content:'\u227A';}
.pf-xbox-dpad-up::after{content:'\u227B';}
.pf-xbox-dpad-right::after{content:'\u227C';}
.pf-xbox-dpad-down::after{content:'\u227D';}
.pf-xbox-dpad-left-right::after{content:'\u227E';}
.pf-xbox-dpad-up-down::after{content:'\u227F';}
.pf-xbox-dpad-left-up::after{content:'\u2280';}
.pf-xbox-dpad-right-up::after{content:'\u2281';}
.pf-xbox-dpad-left-down::after{content:'\u2282';}
.pf-xbox-dpad-right-down::after{content:'\u2283';}
.pf-xbox-dpad::after{content:'\u2284';}
.pf-pin::after{content:'\u2316';}
.pf-tabs::after{content:'\u23CD';}
.pf-back::after{content:'\u23CE';}
.pf-home-screen::after{content:'\u23CF';}
.pf-horizontal-dots::after{content:'\u23D0';}
.pf-vertical-dots::after{content:'\u23D1';}
.pf-hamburger-menu::after{content:'\u23D2';}
.pf-arrow-left::after{content:'\u23F4';}
.pf-arrow-up::after{content:'\u23F5';}
.pf-arrow-right::after{content:'\u23F6';}
.pf-arrow-down::after{content:'\u23F7';}
.pf-wasd::after{content:'\u2423';}
.pf-arrow-keys::after{content:'\u2424';}
.pf-ijkl::after{content:'\u2425';}
.pf-fn::after{content:'\u2426';}
.pf-ctrl::after{content:'\u2427';}
.pf-alt::after{content:'\u2428';}
.pf-shift::after{content:'\u2429';}
.pf-super::after{content:'\u242A';}
.pf-tab::after{content:'\u242B';}
.pf-caps::after{content:'\u242C';}
.pf-backspace::after{content:'\u242D';}
.pf-enter::after{content:'\u242E';}
.pf-esc::after{content:'\u242F';}
.pf-prtsc::after{content:'\u2430';}
.pf-scrlk::after{content:'\u2431';}
.pf-pause::after{content:'\u2432';}
.pf-numlock::after{content:'\u2433';}
.pf-insert::after{content:'\u2434';}
.pf-home::after{content:'\u2435';}
.pf-page-up::after{content:'\u2436';}
.pf-delete::after{content:'\u2437';}
.pf-end::after{content:'\u2438';}
.pf-page-down::after{content:'\u2439';}
.pf-space::after{content:'\u243A';}
.pf-gamepad::after{content:'\u243C';}
.pf-keyboard::after{content:'\u243D';}
.pf-mouse::after{content:'\u243E';}
.pf-mouse-and-keyboard::after{content:'\u243F';}
.pf-f1::after{content:'\u2460';}
.pf-f2::after{content:'\u2461';}
.pf-f3::after{content:'\u2462';}
.pf-f4::after{content:'\u2463';}
.pf-f5::after{content:'\u2464';}
.pf-f6::after{content:'\u2465';}
.pf-f7::after{content:'\u2466';}
.pf-f8::after{content:'\u2467';}
.pf-f9::after{content:'\u2468';}
.pf-f10::after{content:'\u2469';}
.pf-f11::after{content:'\u246A';}
.pf-f12::after{content:'\u246B';}
.pf-empty-keycap::after{content:'\u248F';}
.pf-1::after{content:'\u24F5';}
.pf-2::after{content:'\u24F6';}
.pf-3::after{content:'\u24F7';}
.pf-4::after{content:'\u24F8';}
.pf-5::after{content:'\u24F9';}
.pf-6::after{content:'\u24FA';}
.pf-7::after{content:'\u24FB';}
.pf-8::after{content:'\u24FC';}
.pf-9::after{content:'\u24FD';}
.pf-0::after{content:'\u24FF';}
.pf-star::after{content:'\u2605';}
.pf-skull::after{content:'\u2620';}
.pf-frown::after{content:'\u2639';}
.pf-smile::after{content:'\u263A';}
.pf-empty-heart::after{content:'\u2661';}
.pf-heart::after{content:'\u2665';}
.pf-d4::after{content:'\u2673';}
.pf-d6::after{content:'\u2674';}
.pf-d8::after{content:'\u2675';}
.pf-d10::after{content:'\u2676';}
.pf-d12::after{content:'\u2677';}
.pf-d20::after{content:'\u2678';}
.pf-d6-1::after{content:'\u2680';}
.pf-d6-2::after{content:'\u2681';}
.pf-d6-3::after{content:'\u2682';}
.pf-d6-4::after{content:'\u2683';}
.pf-d6-5::after{content:'\u2684';}
.pf-d6-6::after{content:'\u2685';}
.pf-flag::after{content:'\u2691';}
.pf-gears-options-settings::after{content:'\u2699';}
.pf-cross::after{content:'\u2717';}
.pf-question::after{content:'\u2753';}
.pf-exclamation::after{content:'\u2757';}
.pf-mouse-button-1::after{content:'\u278A';}
.pf-mouse-button-2::after{content:'\u278B';}
.pf-mouse-button-3::after{content:'\u278C';}
.pf-mouse-button-4::after{content:'\u278D';}
.pf-mouse-button-5::after{content:'\u278E';}
.pf-mouse-button-6::after{content:'\u278F';}
.pf-mouse-button-7::after{content:'\u2790';}
.pf-mouse-button-8::after{content:'\u2791';}
.pf-scroll-up::after{content:'\u27F0';}
.pf-scroll-down::after{content:'\u27F1';}
.pf-left-click::after{content:'\u27F5';}
.pf-right-click::after{content:'\u27F6';}
.pf-middle-click::after{content:'\u27F7';}
.pf-mouse-left-right::after{content:'\u27FA';}
.pf-mouse-up-down::after{content:'\u27FB';}
.pf-mouse-any::after{content:'\u27FC';}
.pf-box-crate::after{content:'\u2B1B';}
.pf-playstation::after{content:'\uE000';}
.pf-xbox::after{content:'\uE001';}
.pf-nintendo-switch::after{content:'\uE002';}
.pf-ayaneo::after{content:'\uE003';}
.pf-lenovo-legion::after{content:'\uE004';}
.pf-rog-ally-armoury::after{content:'\uE005';}
.pf-rog-alloy-command::after{content:'\uE006';}
.pf-apple-mac::after{content:'\uE007';}
.pf-windows::after{content:'\uE008';}
.pf-linux::after{content:'\uE009';}
.pf-bsd::after{content:'\uE00A';}
.pf-key-0::after{content:'\uFF10';}
.pf-key-1::after{content:'\uFF11';}
.pf-key-2::after{content:'\uFF12';}
.pf-key-3::after{content:'\uFF13';}
.pf-key-4::after{content:'\uFF14';}
.pf-key-5::after{content:'\uFF15';}
.pf-key-6::after{content:'\uFF16';}
.pf-key-7::after{content:'\uFF17';}
.pf-key-8::after{content:'\uFF18';}
.pf-key-9::after{content:'\uFF19';}
.pf-key-a::after{content:'\uFF21';}
.pf-key-b::after{content:'\uFF22';}
.pf-key-c::after{content:'\uFF23';}
.pf-key-d::after{content:'\uFF24';}
.pf-key-e::after{content:'\uFF25';}
.pf-key-f::after{content:'\uFF26';}
.pf-key-g::after{content:'\uFF27';}
.pf-key-h::after{content:'\uFF28';}
.pf-key-i::after{content:'\uFF29';}
.pf-key-j::after{content:'\uFF2A';}
.pf-key-k::after{content:'\uFF2B';}
.pf-key-l::after{content:'\uFF2C';}
.pf-key-m::after{content:'\uFF2D';}
.pf-key-n::after{content:'\uFF2E';}
.pf-key-o::after{content:'\uFF2F';}
.pf-key-p::after{content:'\uFF30';}
.pf-key-q::after{content:'\uFF31';}
.pf-key-r::after{content:'\uFF32';}
.pf-key-s::after{content:'\uFF33';}
.pf-key-t::after{content:'\uFF34';}
.pf-key-u::after{content:'\uFF35';}
.pf-key-v::after{content:'\uFF36';}
.pf-key-w::after{content:'\uFF37';}
.pf-key-x::after{content:'\uFF38';}
.pf-key-y::after{content:'\uFF39';}
.pf-key-z::after{content:'\uFF3A';}
.pf-headphones::after{content:'\u1F3A7';}
.pf-music::after{content:'\u1F3B6';}
.pf-fish::after{content:'\u1F41F';}
.pf-dance-pad::after{content:'\u1F483';}
.pf-laptop::after{content:'\u1F4BB';}
.pf-diskette::after{content:'\u1F4BE';}
.pf-write::after{content:'\u1F4DD';}
.pf-phone::after{content:'\u1F4F1';}
.pf-camera::after{content:'\u1F4F7';}
.pf-speaker::after{content:'\u1F508';}
.pf-light-gun::after{content:'\u1F52B';}
.pf-sfx-sound-effect-noise::after{content:'\u1F56C';}
.pf-steering-wheel::after{content:'\u1F578';}
.pf-fight-stick-joystick::after{content:'\u1F579';}
.pf-vr-headset::after{content:'\u1F57B';}
.pf-vr-controller::after{content:'\u1F57C';}
.pf-flight-stick::after{content:'\u1F57D';}
.pf-cpu-processor::after{content:'\u1F5A5';}
.pf-web-internet-link::after{content:'\u1F5A7';}
.pf-gpu-graphics-card::after{content:'\u1F5A8';}
.pf-ram-memory::after{content:'\u1F5AA';}
.pf-usb-stick::after{content:'\u1F5AB';}
.pf-database::after{content:'\u1F5AC';}
.pf-hard-disk-drive::after{content:'\u1F5B4';}
.pf-screen-video::after{content:'\u1F5B5';}
.pf-text-entry-edit::after{content:'\u1F5B9';}
.pf-speaking-voice::after{content:'\u1F5E3';}
.pf-language::after{content:'\u1F5E9';}
.pf-exit-quit-leave::after{content:'\u1F6AA';}
.pf-information::after{content:'\u1F6C8';}
.pf-shopping-cart::after{content:'\u1F6D2';}
+3 -2508
View File
File diff suppressed because it is too large Load Diff
-61
View File
@@ -1,61 +0,0 @@
/*
* Default styles for all the basic elements.
*/
div
{
display: block;
}
p
{
display: block;
}
h1
{
display: block;
}
em
{
font-style: italic;
}
strong
{
font-weight: bold;
}
select
{
text-align: left;
}
tabset tabs
{
display: block;
}
table {
box-sizing: border-box;
display: table;
}
tr {
box-sizing: border-box;
display: table-row;
}
td {
box-sizing: border-box;
display: table-cell;
}
col {
box-sizing: border-box;
display: table-column;
}
colgroup {
display: table-column-group;
}
thead, tbody, tfoot {
display: table-row-group;
}
-1
View File
@@ -1 +0,0 @@
iron
-99
View File
@@ -1,99 +0,0 @@
{
"extends": [
"stylelint-config-recommended",
"stylelint-config-standard",
"stylelint-config-sass-guidelines",
"stylelint-config-property-sort-order-smacss",
"stylelint-config-standard-scss"
],
"plugins": ["stylelint-scss"],
"overrides": [{
"files": ["**/*.scss"],
"customSyntax": "postcss-scss"
}],
"rules": {
"no-descending-specificity": null,
"declaration-empty-line-before": null,
"declaration-colon-newline-after": null,
"declaration-block-no-duplicate-properties": true,
"declaration-block-trailing-semicolon": null,
"declaration-no-important": true,
"declaration-property-value-disallowed-list": {
"/^transition/": ["/all/"]
},
"block-closing-brace-newline-after": null,
"max-empty-lines": null,
"selector-list-comma-newline-after": null,
"at-rule-empty-line-before": null,
"at-rule-semicolon-newline-after": null,
"selector-pseudo-element-colon-notation": "single",
"color-no-hex": true,
"function-url-quotes": "always",
"max-nesting-depth": [
4,
{
"ignoreAtRules": ["each", "media", "supports", "include"],
"severity": "warning"
}
],
"number-leading-zero": null,
"order/order": [
[
"custom-properties",
"dollar-variables",
{
"type": "at-rule",
"name": "extend"
},
{
"type": "at-rule",
"name": "include"
},
"declarations",
"rules"
]
],
"order/properties-alphabetical-order": null,
"property-no-vendor-prefix": [
true,
{
"severity": "warning"
}
],
"value-no-vendor-prefix": [
true,
{
"severity": "warning"
}
],
"scss/selector-no-redundant-nesting-selector": null,
"selector-class-pattern": null,
"selector-max-compound-selectors": [
4,
{
"severity": "warning"
}
],
"selector-no-qualifying-type": null,
"string-quotes": null,
"max-line-length": null,
"keyframes-name-pattern": null,
"scss/double-slash-comment-empty-line-before": null,
"scss/double-slash-comment-whitespace-inside": null,
"scss/dollar-variable-empty-line-before": null,
"scss/dollar-variable-pattern": "[a-z-]",
"scss/at-import-partial-extension": null,
"declaration-block-no-redundant-longhand-properties": null,
"color-function-notation": null,
"alpha-value-notation": null,
"at-rule-no-unknown": null,
"property-no-unknown": null,
"scss/at-rule-no-unknown": true,
"selector-pseudo-class-no-unknown": [true, { "ignorePseudoClasses": ["selected"] }],
"font-family-no-missing-generic-family-keyword": null,
"scss/no-global-function-names": null,
"unit-no-unknown": [true, { "ignoreUnits": ["dp"] }],
"selector-type-no-unknown": [true, { "ignore": ["custom-elements", "default-namespace"] }],
"value-keyword-case": null
}
}
-2
View File
@@ -1,2 +0,0 @@
@import "styles/base";
@import "styles/global";
-2261
View File
File diff suppressed because it is too large Load Diff
-29
View File
@@ -1,29 +0,0 @@
{
"name": "mmrecomp-ui-scss",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npm run watch",
"watch": "sass --no-source-map --no-charset --style=compressed main.scss ..\\recomp.rcss --watch",
"watch:debug": "sass --no-source-map --no-charset main.scss ..\\recomp.rcss --watch",
"build": "sass --no-source-map --no-charset --style=compressed main.scss ..\\recomp.rcss",
"build:debug": "sass --no-source-map --no-charset main.scss ..\\recomp.rcss",
"lint": "stylelint '.\\**\\*.scss'"
},
"author": "",
"license": "ISC",
"devDependencies": {
"postcss-scss": "^4.0.9",
"stylelint": "^15.11.0",
"stylelint-config-property-sort-order-smacss": "^9.1.0",
"stylelint-config-sass-guidelines": "^10.0.0",
"stylelint-config-scss": "^1.0.0-security",
"stylelint-config-standard": "^34.0.0",
"stylelint-config-standard-scss": "^11.1.0",
"stylelint-scss": "^5.3.1"
},
"dependencies": {
"sass": "^1.75.0"
}
}
-11
View File
@@ -1,11 +0,0 @@
@use 'sass:math';
@import "./functions/spacing";
@import "./vars/spacing";
@import "./vars/colors";
@import "./vars/borders";
@import "./vars/gradients";
@import "./vars/transitions";
@import "./vars/animations";
@import "./mixins/typography";
@import "./mixins/transitions";
@import "./mixins/helpers";
@@ -1,14 +0,0 @@
@use 'sass:math';
.bottom-left {
display: flex;
position: absolute;
bottom: space(4);
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
max-width: space($base-modal-max-width);
height: auto;
margin: 0 space(4);
}
-81
View File
@@ -1,81 +0,0 @@
@use 'sass:color';
@mixin create-button-variation($base-col, $base-col-05, $base-col-20, $base-col-30, $base-col-80) {
border-color: $base-col-80;
background-color: $base-col-05;
color: $color-text-dim;
&:focus, &:hover {
border-color: $base-col;
background-color: $base-col-30;
color: $color-text;
}
&:disabled,&[disabled] {
color: $color-text-inactive;
}
&:active {
background-color: $base-col-20;
color: $color-text-active;
}
}
.button {
@extend %label-md;
@extend %nav-all;
@include create-button-variation($color-primary, $color-primary-a5, $color-primary-a20, $color-primary-a30, $color-primary-a80);
@include trans-colors;
display: block;
width: auto;
height: auto;
// leave 1dp room for border expansion
padding: space(24 - 1);
border-width: $border-width-thickness;
border-radius: $border-radius-md;
// Setting it by default for convenience
&--primary {
@include create-button-variation($color-primary, $color-primary-a5, $color-primary-a20, $color-primary-a30, $color-primary-a80);
}
&--large {
@extend %label-lg;
}
&--secondary {
@include create-button-variation($color-secondary, $color-secondary-a5, $color-secondary-a20, $color-secondary-a30, $color-secondary-a80);
}
&--tertiary {
@include create-button-variation($color-text, $color-text-a5, $color-text-a20, $color-text-a30, $color-text-a80);
}
&--success {
@include create-button-variation($color-success, $color-success-a5, $color-success-a20, $color-success-a30, $color-success-a80);
}
&--danger {
@include create-button-variation($color-danger, $color-danger-a5, $color-danger-a20, $color-danger-a30, $color-danger-a80);
}
&--warning {
@include create-button-variation($color-warning, $color-warning-a5, $color-warning-a20, $color-warning-a30, $color-warning-a80);
}
&:not([disabled]) {
@extend %nav-all;
cursor: pointer;
}
&:disabled,&[disabled] {
opacity: 0.5;
}
&__label {
width: auto;
height: auto;
}
}
@@ -1,73 +0,0 @@
@use 'sass:math';
.centered-page {
display: flex;
// visibility: hidden;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: space($page-margin);
background-color: $color-border-soft;
}
.centered-page__modal {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: column;
width: 100%;
max-width: space($base-modal-max-width);
height: 100%;
margin: auto;
border-width: $border-width-thickness;
border-radius: $border-radius-modal;
border-color: $color-border;
background: $color-modal-overlay;
> .tabs {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: column;
width: 100%;
max-width: space($base-modal-max-width);
height: 100%;
margin: auto;
}
panels {
flex: 1 1 100%;
}
}
.centered-page__controls {
display: flex;
position: absolute;
bottom: space(24);
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
max-width: space($base-modal-max-width);
height: auto;
margin: 0 auto;
> label {
@extend %label-sm;
display: inline-block;
align-items: center;
justify-content: space-between;
width: auto;
height: space(24);
&:not(:last-child) {
margin-right: space(40);
}
> span:first-child {
margin-right: space(4);
}
}
}
-101
View File
@@ -1,101 +0,0 @@
.config__icon-buttons {
display: flex;
position: absolute;
top: space(8);
right: space(0);
flex-direction: row;
align-items: center;
justify-content: flex-end;
width: auto;
.icon-button {
margin: 0 space(8);
}
}
.config__form {
@include border-top($color-border-soft);
display: flex;
flex: 1 1 100%;
flex-direction: column;
justify-content: space-between;
width: 100%;
height: 100%;
border-bottom-right-radius: $border-radius-modal;
border-bottom-left-radius: $border-radius-modal;
}
.config__wrapper {
flex: 1 1 100%;
width: auto;
height: auto;
padding: space(16);
border-radius: 0dp;
border-bottom-right-radius: $border-radius-modal;
border-bottom-left-radius: $border-radius-modal;
background-color: $color-bg-shadow;
text-align: left;
p {
@extend %body;
padding: space(16);
line-height: space(28);
white-space: pre-line;
b {
color: $color-primary;
}
i {
color: $color-warning;
font-style: normal;
}
}
}
.config__hz-wrapper {
display: flex;
flex: 1 1 100%;
flex-direction: row;
width: 100%;
height: 100%;
border-radius: 0dp;
text-align: left;
}
.config__header, .config__footer {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
background-color: $color-bg-shadow;
}
.config__header {
@include border-bottom($color-border-soft);
padding: space(12) space(20);
}
.config__footer {
@include border-top($color-border-soft);
padding: space(20) space(20);
border-bottom-right-radius: $border-radius-modal;
border-bottom-left-radius: $border-radius-modal;
}
.config__header-left {
display: flex;
flex: 1 1 auto;
flex-direction: row;
> :not(:first-child) {
margin-left: space(8);
}
}
.config__row {
display: flex;
flex-direction: row;
}
@@ -1,27 +0,0 @@
.config-description {
flex: 1 1 100%;
width: auto;
height: auto;
padding: space(16);
border-radius: 0dp;
border-bottom-right-radius: $border-radius-modal;
border-bottom-left-radius: $border-radius-modal;
background-color: $color-bg-shadow;
text-align: left;
&__contents {
@extend %body;
padding: space(16);
line-height: space(28);
white-space: pre-line;
b {
color: $color-primary;
}
i {
color: $color-warning;
font-style: normal;
}
}
}
@@ -1,29 +0,0 @@
.config-group {
position: relative;
&--scrollable {
flex: 1 1 100%;
width: auto;
height: auto;
padding: 0 0 0 space(16);
.config-group__wrapper {
max-height: 100%;
overflow-y: auto;
}
}
&__title {
@extend %label-md;
color: $color-primary;
&--hidden {
display: none;
}
}
&__wrapper {
padding: space(16) 0;
}
}
@@ -1,413 +0,0 @@
.config-option {
display: flex;
flex: 1;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
margin: space(16) space(0) space(24);
&--hz {
flex-direction: row-reverse;
align-items: center;
margin-top: space(4);
margin-bottom: space(4);
.config-option__title {
@extend %label-md;
flex: 1 1 100%;
}
.config-option__list {
flex: 1 1 auto;
width: auto;
}
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
.config-option__title {
@extend %label-md;
padding: 0 space(12);
}
.config-option__radio-tabs,
.config-option__list {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
height: auto;
padding: 0;
input:first-of-type {
nav-left: none;
}
input:last-of-type {
nav-right: none;
}
.config-option__tab-label {
@extend %label-sm;
@include trans-colors-opa;
display: block;
position: relative;
height: auto;
margin: space(4) space(12) 0;
padding: space(8) 0;
color: $color-text-inactive;
tab-index: none;
&:hover {
color: $color-text;
cursor: pointer;
}
}
.config-option__checkbox-wrapper {
@include trans-colors-opa;
width: space(32);
height: space(32);
margin: space(4) space(12) 0;
border-radius: $border-radius-sm;
opacity: 0.5;
background-color: $color-bg-overlay;
cursor: pointer;
&:hover {
opacity: 1;
}
&[checked] {
background-color: $color-a;
}
}
.config-option__checkbox {
@extend %nav-all;
@include trans-colors-opa;
visibility: visible;
width: 0;
height: 0;
}
// TODO: Remove & Replace old stylings
input.radio {
@extend %nav-all;
@include trans-colors-opa;
visibility: visible;
width: 0;
height: 0;
&:not(:disabled) {
&:checked + .config-option__tab-label {
border-bottom: 1dp;
border-color: $color-text;
color: $color-text;
&:hover {
cursor: default;
}
}
.rmlui-window:not([mouse-active]) &:focus + .config-option__tab-label {
transition: none;
animation: $focus-anim-border;
border-color: $color-secondary;
color: $color-secondary;
}
&:focus + .config-option__tab-label,
&:hover + .config-option__tab-label {
color: $color-text;
}
}
&:disabled + .config-option__tab-label {
opacity: 0.5;
&:hover {
cursor: default;
}
}
}
input.range slidertrack {
@include trans-colors;
height: 2dp;
margin-top: space(8);
background-color: $color-border;
}
input.range sliderbar {
@include trans-colors;
width: space(16);
height: space(16);
margin-top: space(1);
margin-right: space(-8);
margin-left: space(-8);
transition: background-color $transition-quick;
border-radius: 8dp;
background-color: $color-text-dim;
.rmlui-window:not([mouse-active]) &:focus {
@include border($color-a);
animation: $focus-anim-bg;
}
&:hover {
background-color: $color-text;
cursor: pointer;
}
}
input.range sliderbar:active,
input.range slidertrack:active + sliderbar {
background-color: $color-secondary;
}
input.range sliderarrowdec,
input.range sliderarrowinc {
display: none;
}
}
.config-option__details {
@extend %label-xs;
height: space(18);
margin: space(14) space(12) 0;
color: $color-primary;
}
.config-option-color {
width: 100%;
max-width: space(360);
height: auto;
margin-top: space(4);
margin-left: space(12);
padding: 0;
&__preview-wrapper {
display: flex;
flex-direction: row;
width: 100%;
height: space(8 * 9);
}
&__preview-block {
display: block;
width: space(8 * 11);
height: 100%;
border-width: $border-width-thickness;
border-radius: $border-radius-lg;
border-color: $color-border;
}
&__hsv-wrapper {
display: flex;
flex: 1 1 100%;
flex-direction: column;
width: auto;
height: auto;
padding-left: space(8);
.config-option-range {
flex: 1 1 auto;
label {
min-width: space(72);
}
input {
flex: 1 1 auto;
}
}
}
}
.config-option-range {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
max-width: space(360);
height: auto;
margin-top: space(4);
padding: 0;
&__label {
@extend %label-sm;
display: block;
width: space(56);
margin: 0 12dp;
margin-right: space(16);
padding: 0;
color: $color-text;
tab-index: none;
}
&__range-input {
flex: 1;
slidertrack {
@include trans-colors;
height: 2dp;
margin-top: space(8);
background-color: $color-border;
}
sliderbar {
@include trans-colors;
width: space(16);
height: space(16);
margin-top: space(1);
margin-right: space(-8);
margin-left: space(-8);
transition: background-color $transition-quick;
border-radius: 8dp;
background-color: $color-text-dim;
.rmlui-window:not([mouse-active]) &:focus {
@include border($color-a);
animation: $focus-anim-bg;
}
&:hover {
background-color: $color-text;
cursor: pointer;
}
}
sliderbar:active,
slidertrack:active + sliderbar {
background-color: $color-secondary;
}
sliderarrowdec,
sliderarrowinc {
display: none;
}
}
}
.config-option__range-wrapper {
max-width: space(360);
margin-top: space(4);
}
.config-option__range-label {
@extend %label-sm;
display: block;
// flex: 0 0 space(32);
width: space(56);
margin: 0 12dp;
margin-right: space(16);
padding: 0;
color: $color-text;
tab-index: none;
}
.config-option-dropdown, .config-option-textfield {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: row;
align-items: center;
justify-content: flex-start;
width: auto;
height: auto;
padding: space(8) space(24) space(8) space(12);
&__select {
display: block;
height: space(48);
padding: space(14);
cursor: pointer;
}
&__wrapper {
// Cursed guess & check so that this appears to be the same height as the select
$extra-space: 2;
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
height: auto;
padding: space(0 + $extra-space) 0 space(10 + $extra-space);
cursor: text;
input {
width: 100%;
height: auto;
vertical-align: middle;
}
}
&__select, &__wrapper {
@extend %body;
@extend %nav-all;
@include trans-colors-border;
@include border($color-white-a50);
position: relative;
box-sizing: border-box;
flex: 1 1 100%;
width: auto;
border-radius: $border-radius-md;
background-color: $color-white-a5;
&:hover, &:focus {
@include border($color-white-a80);
background-color: $color-white-a20;
}
selectvalue {
display: inline;
height: auto;
margin: auto 0;
}
selectbox {
@include border($color-border);
margin-top: space(2);
padding: space(4) 0;
border-radius: $border-radius-md;
background-color: $color-background-3;
option {
@extend %nav-all;
@include trans-colors;
padding: space(8) space(12);
background-color: $color-transparent;
color: $color-text-dim;
font-weight: 400;
&:hover, &:focus {
background-color: $color-white-a20;
}
&:hover:not(:checked) {
cursor: pointer;
}
&:checked {
background-color: $color-white-a5;
color: $color-white;
}
}
}
}
}
@@ -1,170 +0,0 @@
@use 'sass:color';
@use 'sass:math';
.control-option {
@include set-color($color-text-dim);
@include trans-colors-svg;
display: flex;
position: relative;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
height: auto;
padding: space(4) space(16) space(4) space(20);
border-radius: $border-radius-sm;
background-color: rgba(0, 0, 0, 0);
&:focus-visible:not(:disabled, [disabled]),
&:hover:not(:disabled, [disabled]) {
@include set-color($color-text);
background-color: $color-bg-overlay;
}
&:disabled, &[disabled] {
opacity: 0.5;
}
&--active {
// while actively looking for inputs, set styles to the correct slots
$valid-binding-slots: 0, 1;
@each $slot in $valid-binding-slots {
// global attr -> this active row -> binding slot
[cur-binding-slot="#{$slot}"] & .control-option__binding[bind-slot="#{$slot}"] {
border-color: $color-danger;
.control-option__binding-icon {
opacity: 0;
}
.control-option__binding-recording {
opacity: 1;
}
}
}
}
.icon-button {
flex: 1 1 auto;
}
}
.control-option__label {
@extend %label-md;
flex: 2 1 space(300);
height: auto;
white-space: nowrap;
}
.control-option__bindings {
display: flex;
position: relative;
flex: 2 1 space(400);
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
height: space(56);
padding: 0 space(12) 0 space(4);
}
.control-option__binding {
@include set-color($color-text-dim);
@include trans-colors-border;
display: flex;
position: relative;
flex: 1 1 100%;
align-items: center;
justify-content: center;
width: 100%;
height: space(56);
margin: 0 space(4);
padding: space(8);
border-width: $border-width-thickness;
border-radius: $border-radius-sm;
border-color: $color-bg-overlay;
background-color: $color-bg-overlay;
&:focus, &:hover {
@include set-color($color-text);
border-color: $color-text;
background-color: $color-border-soft;
}
&:active {
@include set-color($color-text-active);
}
&:disabled, &[disabled] {
@include set-color($color-text-dim);
opacity: 0.5;
}
&:not([disabled]) {
@extend %nav-all;
cursor: pointer;
}
}
.control-option__binding-icon {
@include trans-colors-opa;
opacity: 1;
}
@keyframes control-option__binding-recording-scale {
0% {
transform: scale(1);
}
50% {
transform: scale(0.85);
}
100% {
transform: scale(1);
}
}
.control-option__binding-recording {
@include trans-colors-opa;
display: flex;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
align-items: center;
justify-content: center;
opacity: 0;
.control-option__binding-circle {
$rec-size: 24;
width: space($rec-size);
height: space($rec-size);
animation: 1.5s sine-in-out infinite control-option__binding-recording-scale;
border-radius: space($rec-size);
background-color: $color-danger;
}
.control-option__binding-edge {
$edge-size: 36;
$h-edge-size: math.div($edge-size, 2);
position: absolute;
top: 50%;
left: 50%;
width: space($edge-size);
height: space($edge-size);
transform: translate(-50%, -50%);
> svg.control-option__binding-edge-svg {
width: space($edge-size);
height: space($edge-size);
image-color: $color-danger;
}
}
}
@@ -1,92 +0,0 @@
@use 'sass:color';
/*
<button
class="icon-button icon-button--danger"
>
<svg src="icons/Trash.svg" />
</button>
*/
@mixin create-icon-button-variation($base-col, $base-col-05, $base-col-20, $base-col-30, $base-col-80) {
border-color: $base-col-80;
background-color: $base-col-05;
&:focus, &:hover {
border-color: $base-col;
background-color: $base-col-30;
}
&:active {
background-color: $base-col-20;
}
}
$icon-button-size: 56 - ($border-width-thickness-num * 2);
.icon-button {
@include set-color($color-text-dim);
@include trans-colors-border;
display: flex;
align-items: center;
justify-content: center;
width: space($icon-button-size);
min-width: space($icon-button-size);
max-width: space($icon-button-size);
height: space($icon-button-size);
min-height: space($icon-button-size);
max-height: space($icon-button-size);
border-width: $border-width-thickness;
border-radius: space($icon-button-size * 0.5);
border-color: $color-transparent;
background-color: $color-transparent;
&:focus, &:hover {
@include set-color($color-text);
background-color: $color-border;
}
&:active {
@include set-color($color-text-active);
background-color: $color-border-soft;
}
&:disabled,&[disabled] {
@include set-color($color-text-dim);
opacity: 0.5;
}
&:not([disabled]) {
@extend %nav-all;
cursor: pointer;
}
svg {
width: space(32);
height: space(32);
}
&--primary {
@include create-icon-button-variation($color-primary, $color-primary-a5, $color-primary-a20, $color-primary-a30, $color-primary-a80);
}
&--secondary {
@include create-icon-button-variation($color-secondary, $color-secondary-a5, $color-secondary-a20, $color-secondary-a30, $color-secondary-a80);
}
&--tertiary {
@include create-icon-button-variation($color-text, $color-text-a5, $color-text-a20, $color-text-a30, $color-text-a80);
}
&--success {
@include create-icon-button-variation($color-success, $color-success-a5, $color-success-a20, $color-success-a30, $color-success-a80);
}
&--danger {
@include create-icon-button-variation($color-danger, $color-danger-a5, $color-danger-a20, $color-danger-a30, $color-danger-a80);
}
&--warning {
@include create-icon-button-variation($color-warning, $color-warning-a5, $color-warning-a20, $color-warning-a30, $color-warning-a80);
}
}
@@ -1,347 +0,0 @@
@use 'sass:math';
// Probably will need to adjust for other langs...
$mapping-min-width: 80 * 8;
$visual-max-width: $base-modal-max-width - $mapping-min-width - $scrollbar-width;
.input-config {
padding: 0;
}
.input-config__horizontal-split {
display: flex;
position: relative;
flex-direction: row;
height: 100%;
}
.input-config__mappings {
display: block;
flex: 1 1 auto;
min-width: space($mapping-min-width);
height: 100%;
}
.input-config__mappings-scroll {
display: block;
width: 100%;
max-height: 100%;
overflow-y: auto;
}
.input-config__mappings-wrapper {
padding: space(8);
}
.input-config__visual-wrapper {
display: block;
flex: 1 1 100%;
width: auto;
max-width: space($visual-max-width);
height: auto;
max-height: space(math.div($visual-max-width, 4) * 3);
margin: auto 0;
}
.input-config__visual-aspect {
position: relative;
width: 100%;
margin: auto 0;
padding-bottom: 75%;
background-color: $color-bg-shadow;
}
.input-config__visual {
display: flex;
position: absolute;
top: space(16);
right: space(16);
bottom: space(16);
left: space(16);
flex-direction: column;
border-radius: space(108);
background-color: $color-white-a5;
}
.input-config__visual-half {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: row;
padding: 6%;
&--bottom {
align-items: flex-end;
justify-content: space-between;
}
}
.input-config__visual-quarter-left {
display: flex;
flex: 1 1 50%;
align-items: flex-start;
justify-content: flex-start;
width: auto;
}
.input-config__visual-quarter-right {
display: flex;
flex: 1 1 100%;
align-items: flex-start;
justify-content: flex-end;
}
.input-config__visual-stick-wrapper {
display: flex;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
align-items: center;
justify-content: center;
}
.input-viz {
@include trans-colors-opa;
display: flex;
position: relative;
align-items: center;
justify-content: center;
> svg:not(.input-viz__dpad-arrow) {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
&__mappings div {
@extend %prompt-font-sm;
}
}
$all-inputs: A,
B,
Z,
START,
DPAD_UP,
DPAD_DOWN,
DPAD_LEFT,
DPAD_RIGHT,
L,
R,
C_UP,
C_DOWN,
C_LEFT,
C_RIGHT,
X_AXIS_NEG,
X_AXIS_POS,
Y_AXIS_NEG,
Y_AXIS_POS;
// Show default state while no inputs are active
[cur-input="NONE"] .input-viz[visual-input] {
opacity: 1;
}
@each $inp in $all-inputs {
.input-viz[visual-input~="#{$inp}"] {
opacity: 0.25;
[cur-input="#{$inp}"] & {
opacity: 1.0;
}
}
}
@mixin set-sizes($sz) {
width: space($sz);
height: space($sz);
> svg {
width: space($sz);
height: space($sz);
}
}
.input-viz__button {
@include set-color($color-text);
&--sm {
@include set-sizes(64);
}
&--md {
@include set-sizes(76);
}
&--lg {
@include set-sizes(84);
}
&--C {
@include set-svgs-color($color-warning);
}
&--A {
@include set-svgs-color($color-a);
margin-top: auto;
}
&--B {
@include set-svgs-color($color-success);
}
&--Start {
@include set-svgs-color($color-danger);
}
}
.input-viz__Z {
@include set-svgs-color($color-warning);
@include set-sizes(136);
}
$dpad-size: 192;
.input-viz.input-viz__dpad {
@include set-svgs-color($color-text);
@include set-sizes($dpad-size);
position: relative;
}
$stick-size: 200;
.input-config__visual-stick {
display: flex;
position: relative;
align-items: center;
justify-content: center;
width: space($stick-size);
height: space($stick-size);
border-radius: space(math.div($stick-size, 2));
background-color: $color-white-a5;
}
.input-viz__dpad-split,
.input-viz__stick-split {
@include inset-block(0);
display: flex;
width: 100%;
height: 100%;
&--vertical {
flex-direction: column;
align-items: center;
justify-content: space-between;
}
&--horizontal {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
> div {
display: flex;
flex: 1 1 100%;
flex-direction: row;
align-items: center;
justify-content: center;
}
}
.input-viz__dpad-split > div {
width: space(math.div($dpad-size, 3));
height: space(math.div($dpad-size, 3));
}
.input-viz__stick-split > div {
width: space(math.div($stick-size, 3));
height: space(math.div($stick-size, 3));
}
.input-viz__dpad-arrow {
$edge-dist: space(4);
position: absolute;
width: space(60);
height: space(60);
&--up {
top: $edge-dist;
margin: 0 auto;
}
&--down {
bottom: $edge-dist;
margin: 0 auto;
transform: rotate(180deg);
}
&--left {
left: $edge-dist;
margin: auto 0;
transform: rotate(-90deg);
}
&--right {
right: $edge-dist;
margin: auto 0;
transform: rotate(90deg);
}
}
.input-viz__R {
@include set-svgs-color($color-white);
@include set-sizes(96);
}
.input-viz__L {
@include set-svgs-color($color-secondary);
@include set-sizes(136);
}
.input-config__c-buttons {
position: relative;
width: space(76 + 76 + 56);
height: space(76 + 56);
&-lr, &-du {
display: flex;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
&-lr {
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
}
&-du {
flex-direction: column-reverse;
align-items: center;
justify-content: space-between;
}
.input-viz {
&[visual-input="C_UP"] {
margin-top: space(-32);
}
}
}
.input-config__main-buttons {
display: flex;
position: relative;
flex-direction: row;
justify-content: space-between;
width: space(268);
height: space(128);
margin-right: space(10);
}
-108
View File
@@ -1,108 +0,0 @@
// TODO: Affect all elements with launcher fade-in
// @keyframes fade-launcher-in {
// 0% {
// opacity: 0;
// }
// to {
// opacity: 1;
// }
// }
.launcher {
display: block;
position: relative;
flex-direction: row;
justify-content: space-between;
width: 100%;
height: 100%;
background-color: $color-background-1;
}
.launcher__vertical-split {
display: flex;
position: absolute;
top: 0;
right: 50%;
bottom: 0;
left: 0;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
&--right {
right: 0;
left: 50%;
align-items: flex-end;
}
}
@keyframes slide-mm-bg-over {
0% {
transform: translateX(space(100));
}
100% {
transform: translateX(space(0));
}
}
.launcher__background-wrapper {
display: flex;
position: absolute;
top: -55vw;
right: -100%;
bottom: -50vw;
left: -70vw;
align-items: center;
justify-content: flex-start;
transform: translateX(space(0));
animation: 25s cubic-out 1 slide-mm-bg-over;
}
@keyframes fade-mm-in {
0% {
opacity: 0;
}
100% {
opacity: 0.1;
}
}
.launcher__background-bk {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: auto;
height: 100%;
animation: 2.5s cubic-in-out 1 fade-mm-in;
opacity: 0.1;
}
.launcher__title-quadrant {
flex: 1 1 auto;
width: auto;
height: auto;
padding-top: space(96);
padding-left: space(96);
&--right {
padding-right: space(96);
padding-left: 0;
}
}
.launcher__content-quadrant {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: column;
align-items: flex-start;
justify-content: flex-end;
width: 100%;
height: auto;
padding: space(32);
}
@@ -1,73 +0,0 @@
/*
Example layout:
<button class="menu-list-item menu-list-item--right">
<div class="menu-list-item__bullet">•</div>
<div class="menu-list-item__label">Setup controls</div>
</button>
- Variants:
.menu-list-item--right (align to right side)
- Optional:
- <div class="subtitle-title__disclaimer">Coming Soon™</div>
*/
.menu-list-item {
@include set-color($color-text-dim);
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
height: auto;
padding: space(16);
border-radius: $border-radius-sm;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
&--right {
flex-direction: row-reverse;
align-content: flex-end;
.menu-list-item__bullet {
margin-left: space(12);
opacity: 1;
}
&.menu-list-item:focus:not(:disabled, [disabled]),
&.menu-list-item:hover:not(:disabled, [disabled]) {
// decorator: $primary-rl-fade;
background-color: $color-white-a5;
}
}
&:focus:not(:disabled, [disabled]),
&:hover:not(:disabled, [disabled]) {
@include set-color($color-primary);
// decorator: $primary-lr-fade;
background-color: $color-white-a5;
.menu-list-item__bullet {
opacity: 1;
}
}
&:not(:disabled, [disabled]) {
@extend %nav-all;
}
&:disabled, &[disabled] {
opacity: 0.5;
tab-index: none;
cursor: default;
}
}
.menu-list-item__label {
@extend %label-lg;
}
.menu-list-item__bullet {
margin-right: space(12);
opacity: 0;
}
-70
View File
@@ -1,70 +0,0 @@
@use 'sass:math';
$prompt-space: 24;
.prompt {
&__overlay {
background-color: $color-bg-overlay;
pointer-events: auto;
}
&__overlay,
&__content-wrapper {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
&__content-wrapper {
display: flex;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
align-items: center;
justify-content: center;
}
&__content {
display: flex;
position: relative;
flex: 1 1 100%;
flex-direction: column;
width: 100%;
max-width: space(700);
height: auto;
margin: auto;
border-width: $border-width-thickness;
border-radius: $border-radius-modal;
border-color: $color-border;
background: $color-modal-overlay;
h3, p {
margin: space($prompt-space);
}
p {
margin-top: 0;
}
}
&__controls {
display: flex;
flex-direction: row;
justify-content: center;
padding: space($prompt-space) space(math.div($prompt-space, 2));
border-top-width: $border-width-thickness;
border-top-color: $color-border-soft;
.button {
min-width: space(math.div(700, 3));
margin: 0 space(math.div($prompt-space, 2));
text-align: center;
nav-up: none;
nav-down: none;
}
}
}
@@ -1,71 +0,0 @@
/*
Example layout:
<button class="subtitle-title" disabled>
<h3>Zelda 64: Recompiled</h3>
<h1>Ocarina of Time</h1>
<div class="subtitle-title__disclaimer">Coming Soon™</div>
</button>
- Variants:
.subtitle-title--right (align to right side)
- Optional:
- <div class="subtitle-title__disclaimer">Coming Soon™</div>
*/
.subtitle-title {
display: block;
position: relative;
flex-direction: column;
align-content: flex-start;
align-items: flex-start;
width: auto;
height: auto;
padding: 0;
background-color: rgba(0, 0, 0, 0);
color: $color-text-dim;
text-align: left;
cursor: pointer;
&--right {
align-content: flex-end;
}
&--right, &--right > * {
text-align: right;
}
&[selected] {
color: $color-text;
cursor: default;
}
&:focus:not(:disabled, [disabled]),
&:hover:not(:disabled, [disabled], [selected]) {
color: $color-primary;
}
&:not(:disabled, [disabled]) {
@extend %nav-all;
}
&:disabled, &[disabled] {
opacity: 0.5;
cursor: default;
tab-index: none;
}
h3 {
margin-bottom: space(6);
}
h1 {
margin-top: space(6);
}
&__disclaimer {
@extend %label-sm;
margin-top: space(16);
}
}
-63
View File
@@ -1,63 +0,0 @@
/*
Example:
<tab class="tab">
<div>Graphics</div>
<div class="tab__indicator"></div>
</tab>
*/
.tabs tabs {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.tab {
@extend %nav-all;
@extend %header-3;
display: block;
position: relative;
margin: 0;
padding: space(20) space(24);
transition: color $transition-quick;
opacity: 0.9;
background-color: rgba(0,0,0,0);
color: $color-text-inactive;
&:selected {
color: $color-text;
.tab__indicator {
background-color: $color-border-solid;
}
&:hover {
cursor: default;
}
}
.rmlui-window:not([mouse-active]) &:focus {
transition: none;
animation: $focus-anim-border;
&:selected .tab__indicator {
animation: $focus-anim-bg;
}
}
&:focus, &:hover {
opacity: 1;
color: $color-text;
cursor: pointer;
}
}
.tab__indicator {
position: absolute;
right: 0;
bottom: 2dp;
left: 0;
height: 2dp;
background-color: rgba(0, 0, 0, 0);
}
-97
View File
@@ -1,97 +0,0 @@
@use "sass:math";
$toggle-width: 162;
$toggle-height: 72;
$toggle-floater-width: 80;
$toggle-floater-height: 64;
$toggle-floater-margin: 4;
$toggle-checked-left-offset: $toggle-width - $toggle-floater-margin - $toggle-floater-width;
.toggle {
@extend %nav-all;
@include trans-colors-opa;
display: flex;
position: relative;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: space($toggle-width);
height: space($toggle-height);
border-radius: space(math.div($toggle-height, 2));
opacity: 0.9;
background: $color-transparent;
cursor: pointer;
&:hover, &:focus-visible, &:focus {
opacity: 1;
background-color: $color-secondary-a30;
}
&:active {
opacity: 1;
background-color: $color-secondary-a5;
}
.toggle__border {
@include inner-border-block($color-secondary-l);
border-radius: space(math.div($toggle-height, 2));
}
.toggle__floater {
position: absolute;
top: 50%;
left: space($toggle-floater-margin);
width: space($toggle-floater-width);
height: space($toggle-floater-height);
transform: translateY(-50%);
border-radius: space(math.div($toggle-floater-height, 2));
background: $color-secondary-d;
}
&--checked {
.toggle__floater {
left: space($toggle-checked-left-offset);
}
.toggle__icon {
&.toggle__icon--left {
opacity: 0.9;
color: $color-secondary-l;
}
&.toggle__icon--right {
opacity: 1.0;
color: $color-text;
}
}
}
}
.toggle__icons {
display: flex;
position: absolute;
top: 50%;
right: space(16);
left: space(16);
align-items: center;
justify-content: space-between;
height: space(56);
transform: translateY(-50%);
}
.toggle__icon {
@extend %prompt-font-lg;
@include trans-colors;
display: flex;
align-items: center;
justify-content: center;
width: space(56);
height: space(56);
color: $color-text;
&--right {
opacity: 1;
color: $color-secondary-l;
}
}
@@ -1,16 +0,0 @@
@import "./CenteredPage";
@import "./ControlOption";
@import "./Tabs";
@import "./Config";
@import "./ConfigGroup";
@import "./ConfigOption";
@import "./ConfigDescription";
@import "./InputConfig";
@import "./Button";
@import "./IconButton";
@import "./Launcher";
@import "./MenuListItem";
@import "./SubtitleTitle";
@import "./Toggle";
@import "./BottomLeft";
@import "./Prompt";
@@ -1,4 +0,0 @@
@function space($amt) {
@return #{$amt}dp;
}
-134
View File
@@ -1,134 +0,0 @@
@import "./base";
body
{
@extend %body;
box-sizing: border-box;
color: $color-text;
font-family: $font-stack;
}
@import "./globals/old";
@import "./globals/scrollbars";
@import "./components/components";
@import "./pages/pages";
$font-size: 20dp;
.rmlui-window {
opacity: 1;
&--hidden {
opacity: 0;
}
&:not([mouse-active]) {
pointer-events: none;
}
}
.nav-vert {
@extend %nav-vert;
}
.nav-horiz {
@extend %nav-horiz;
}
.nav-dir {
@extend %nav-dir;
}
.nav-foc {
@extend %nav-foc;
}
.nav-all {
@extend %nav-all;
}
*, *:before, *:after {
box-sizing: border-box;
}
h1 {
@extend %header-1;
}
h2 {
@extend %header-2;
}
h3 {
@extend %header-3;
}
.label-lg {
@extend %label-lg;
}
.label-md {
@extend %label-md;
}
.label-sm {
@extend %label-sm;
}
.prompt-font {
@extend %prompt-font;
}
.prompt-font-sm {
@extend %prompt-font-sm;
}
button {
background-color: $color-primary-d;
}
@keyframes blue-pulse {
0% {
color: $color-secondary;
}
50% {
color: $color-secondary-l;
}
100% {
color: $color-secondary;
}
}
@keyframes blue-pulse-with-border {
0% {
border-color: $color-secondary;
color: $color-secondary;
}
50% {
border-color: $color-secondary-l;
color: $color-secondary-l;
}
100% {
border-color: $color-secondary;
color: $color-secondary;
}
}
@keyframes blue-pulse-background {
0% {
background-color: $color-secondary;
}
50% {
background-color: $color-secondary-l;
}
100% {
background-color: $color-secondary;
}
}
-211
View File
@@ -1,211 +0,0 @@
/* stylelint-disable color-no-hex */
/* stylelint-disable selector-max-id */
* {
box-sizing:border-box;
}
hr {
display:block;
padding:1.5dp;
background: $color-background-1;
}
body {
color: #fff;
font-size: 20dp;
font-style: normal;
font-weight: normal
}
/* div {
focus:none;
tab-index:none;
} */
div#window {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
border-color: $color-border;
background-color: $color-background-2;
}
div#content {
z-index: 2;
width: auto;
height: 100%;
overflow: hidden auto;
text-align: center
}
p {
text-align: left;
}
input.submit {
margin-left: 0
}
input.text,
input.password {
box-sizing: border-box;
height: 31dp;
padding: 11dp 10dp 0;
text-align: left;
cursor: text
}
textarea {
padding: 14dp 12dp 10dp;
text-align: left;
cursor: text
}
input.text,
input.password,
select,
textarea {
/* color: #333; */
/* font-size: 13dp */
height: auto;
}
table input.text {
box-sizing: border-box;
width: 100%;
height: auto;
/* height: 18dp; */
margin: 0;
border-width: $border-width-thickness;
border-color: #000;
background-color: #fff;
font-size: 15dp;
/* padding: 0 5dp; */
line-height: 1;
decorator: none;
/* vertical-align: center; */
}
select {
// display: inline-block;
// /* width: 175dp; */
// /* height: 37dp; */
// /* height: auto; */
// text-align: left;
// box-sizing:border-box;
// /* padding: 4dp; */
// vertical-align: center;
// padding: 4dp;
// border-radius: 5dp;
// background-color: rgb(120, 120, 120);
// width: 100%;
}
// select {
// @extend %body;
// display: flex;
// align-items: center;
// justify-content: flex-start;
// box-sizing: border-box;
// padding: space(16);
// flex: 1 1 100%;
// // width: auto;
// height: space(1000);
// border-radius: $border-radius-lg;
// background-color: $color-white-a20;
// }
// select selectvalue {
// height: auto;
// /* padding: 4dp; */
// /* margin-right: 30dp; */
// /* height: 25dp; */
// /* padding: 4dp; */
// /* decorator: image(selectvalue) */
// }
// select:hover selectvalue {
// /* margin-right: 30dp; */
// /* height: 25dp; */
// /* padding: 4dp; */
// background-color: rgb(150, 150, 150);
// /* decorator: image(selectvalue) */
// }
// select selectarrow {
// /* width: 30dp; */
// /* height: 37dp; */
// /* decorator: image(selectarrow) */
// /* background-color: black; */
// /* appearance: none; */
// }
// select:hover selectarrow {
// /* decorator: image(selectarrow-hover) */
// }
// select:active selectarrow,
// select selectarrow:checked {
// /* decorator: image(selectarrow-active) */
// }
// select selectbox {
// /* margin-left: 1dp; */
// /* margin-top: -7dp; */
// /* margin-bottom: -10dp; */
// /* width: 162dp; */
// /* padding: 1dp 4dp 4dp 4dp */
// }
// select selectbox,
// tbody {
// background-color: rgb(120,120,120);
// /* decorator: tiled-box(selectbox-tl, selectbox-t, selectbox-tr, selectbox-l, selectbox-c, auto, selectbox-bl, selectbox-b, selectbox-br) */
// }
// select selectbox option {
// width: auto;
// background-color: rgb(120, 120, 120)
// }
// select selectbox option:nth-child(even),
// tr:nth-child(even) {
// background-color: rgb(100, 100, 100)
// }
// select selectbox option:checked {
// font-weight:bold;
// color:rgb(255,255,255);
// }
// select selectbox option:hover {
// background: rgb(150,150,150)
// }
input.radio {
flex: 0;
width:0dp;
nav-up:auto;
nav-right:auto;
nav-down:auto;
nav-left:auto;
tab-index:auto;
focus:auto;
}
input.checkbox {
width: space(20);
height: space(20);
nav-up:auto;
nav-right:auto;
nav-down:auto;
nav-left:auto;
tab-index:auto;
focus:auto;
}
@@ -1,52 +0,0 @@
@use 'sass:math';
$scrollbar-width: 12;
@mixin _set-scroll-size($size-key) {
#{$size-key}: space($scrollbar-width);
slidertrack {
#{$size-key}: space($scrollbar-width);
}
sliderbar {
#{$size-key}: space($scrollbar-width);
}
}
scrollbarvertical,scrollbarhorizontal {
margin: 0;
border: 0;
slidertrack {
background: $color-primary-l;
opacity: 0.05;
}
sliderbar {
border-radius: space(math.div($scrollbar-width, 2) - 1);
background: $color-primary-l;
opacity: 0.1;
&:hover:not(:active) {
opacity: 0.2;
}
&:active {
opacity: 0.3;
}
}
sliderarrowdec, sliderarrowinc {
width: 0;
height: 0;
}
}
scrollbarvertical {
@include _set-scroll-size(width);
}
scrollbarhorizontal {
@include _set-scroll-size(height);
}
-39
View File
@@ -1,39 +0,0 @@
%nav-vert {
nav-up: auto;
nav-down: auto;
}
%nav-horiz {
nav-right: auto;
nav-left: auto;
}
%nav-dir {
@extend %nav-vert;
@extend %nav-horiz;
}
%nav-foc {
focus: auto;
tab-index: auto;
}
%nav-all {
@extend %nav-dir;
@extend %nav-foc;
}
@mixin set-svgs-color($col) {
svg {
image-color: $col;
}
}
/*
@include set-color(COLOR);
*/
@mixin set-color($col) {
@include set-svgs-color($col);
color: $col;
}
@@ -1,36 +0,0 @@
/*
@include trans-colors;
*/
@mixin trans-colors {
transition: color $transition-quick, background-color $transition-quick;
}
/*
@include trans-colors-opa;
*/
@mixin trans-colors-opa {
transition: color $transition-quick, background-color $transition-quick, opacity $transition-quick;
}
/*
@include trans-colors-svg;
*/
@mixin trans-colors-svg {
transition: color $transition-quick, background-color $transition-quick, opacity $transition-quick;
svg {
transition: image-color $transition-quick, background-color $transition-quick;
}
}
/*
@include trans-colors-border;
*/
@mixin trans-colors-border {
transition: color $transition-quick, background-color $transition-quick, opacity $transition-quick, border-color $transition-quick;
svg {
transition: image-color $transition-quick, background-color $transition-quick;
}
}
@@ -1,83 +0,0 @@
$font-stack: 'Suplexmentary Comic NC';
@mixin set-font-sizing($sz, $spacing) {
// font-family: $font-stack;
$sz-add: $sz + 4;
font-size: space($sz-add);
letter-spacing: space($sz-add * $spacing);
line-height: space($sz-add);
}
%header-1 {
@include set-font-sizing(64, 0.07);
font-style: normal;
font-weight: 700;
}
%header-2 {
@include set-font-sizing(48, 0.07);
font-style: normal;
font-weight: 700;
}
%header-3 {
@include set-font-sizing(32, 0.07);
font-style: normal;
font-weight: 700;
}
%label-lg {
@include set-font-sizing(32, 0.11);
font-style: normal;
font-weight: 700;
}
%label-md {
@include set-font-sizing(24, 0.11);
font-style: normal;
font-weight: 700;
}
%label-sm {
@include set-font-sizing(16, 0.14);
font-style: normal;
font-weight: 700;
text-transform: uppercase;
}
%label-xs {
@include set-font-sizing(14, 0.14);
font-style: normal;
font-weight: 400;
}
%body {
@include set-font-sizing(16, 0.0);
font-style: normal;
font-weight: 400;
}
%prompt-font-lg {
font-family: promptfont;
font-size: space(56);
font-style: normal;
font-weight: 400;
line-height: space(56);
}
%prompt-font {
font-family: promptfont;
font-size: space(40);
font-style: normal;
font-weight: 400;
line-height: space(40);
}
%prompt-font-sm {
font-family: promptfont;
font-size: space(32);
font-style: normal;
font-weight: 400;
line-height: space(32);
}
-1
View File
@@ -1 +0,0 @@
@import "./config/config";
@@ -1 +0,0 @@
@import "./debug";
-178
View File
@@ -1,178 +0,0 @@
.config-debug {
display: block;
position: relative;
width: 100%;
max-height: 100%;
padding: space(8);
}
.config-debug__scroll {
display: block;
position: relative;
width: 100%;
max-height: 100%;
overflow-y: auto;
}
.config-debug-option {
@include set-color($color-text-dim);
@include trans-colors-svg;
@include border-bottom($color-border-soft);
display: block;
position: relative;
flex-direction: column;
width: 100%;
height: auto;
padding: space(12) space(4);
background-color: rgba(0, 0, 0, 0);
&:focus:not(:disabled, [disabled]),
&:focus-visible:not(:disabled, [disabled]),
&:hover:not(:disabled, [disabled]) {
@include set-color($color-text);
background-color: $color-bg-overlay;
}
&:disabled, &[disabled] {
opacity: 0.5;
}
.icon-button {
margin-left: space(8);
}
}
.config-debug__option-split {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.config-debug-option__label {
@extend %label-md;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding: space(4) space(16) space(12);
width: auto;
height: auto;
white-space: nowrap;
}
.config-debug__option-controls {
display: block;
position: relative;
flex: 1 1 auto;
height: auto;
width: auto;
max-width: space(800);
padding: 0 space(12);
}
.config-debug__option-trigger {
flex: 1 1 auto;
}
.config-debug__select-wrapper {
display: flex;
position: relative;
flex-direction: row;
align-items: center;
justify-content: flex-start;
flex: 1 1 100%;
width: auto;
max-width: space(800);
height: auto;
padding: space(4);
.config-debug__select-label {
@extend %label-sm;
padding-right: space(16);
flex: auto;
width: space(196);
> div {
display: inline;
width: auto;
height: auto;
}
}
input {
@extend %body;
@extend %nav-all;
display: block;
position: relative;
box-sizing: border-box;
padding: 0;
flex: 1 1 100%;
width: auto;
height: space(20);
margin: auto 0;
}
select {
@extend %body;
@extend %nav-all;
@include trans-colors-border;
@include border($color-white-a50);
display: block;
position: relative;
box-sizing: border-box;
padding: 0;
flex: 1 1 100%;
width: auto;
height: space(48);
border-radius: $border-radius-md;
background-color: $color-white-a5;
cursor: pointer;
align-items: center;
justify-content: flex-start;
padding: space(14);
&:hover, &:focus {
@include border($color-white-a80);
background-color: $color-white-a20;
}
selectvalue {
display: inline;
margin: auto 0;
height: auto;
}
selectbox {
@include border($color-white-a80);
background-color: $color-background-3;
padding: space(4) 0;
margin-top: space(2);
border-radius: $border-radius-md;
option {
@extend %nav-all;
@include trans-colors;
padding: space(8) space(12);
background-color: $color-transparent;
color: $color-text-dim;
font-weight: 400;
&:hover, &:focus {
background-color: $color-white-a20;
}
&:hover:not(:checked) {
cursor: pointer;
}
&:checked {
color: $color-white;
background-color: $color-white-a5;
}
}
}
}
}
-4
View File
@@ -1,4 +0,0 @@
$focus-anim: blue-pulse-with-border 0.75s infinite;
$focus-anim-border: blue-pulse 0.75s infinite;
$focus-anim-bg: blue-pulse-background 0.75s infinite;
-42
View File
@@ -1,42 +0,0 @@
$border-radius-sm: 8dp;
$border-radius-md: 12dp;
// modals/pages
$border-radius-lg: 16dp;
$border-radius-modal: $border-radius-lg;
$border-width-thickness-num: 1.1;
// $border-width-thickness-num: 1.5;
$border-width-thickness: space($border-width-thickness-num);
@mixin border($col: $color-border) {
border-width: $border-width-thickness;
border-color: $col;
}
@mixin border-top($col: $color-border) {
border-top-width: $border-width-thickness;
border-top-color: $col;
}
@mixin border-bottom($col: $color-border) {
border-bottom-width: $border-width-thickness;
border-bottom-color: $col;
}
@mixin inset-block($inset-amt) {
position: absolute;
top: $inset-amt;
right: $inset-amt;
bottom: $inset-amt;
left: $inset-amt;
}
// add this to a child of the container that needs a border.
// parent must have `position: relative`
@mixin inner-border-block($col: $color-border) {
@include inset-block($border-width-thickness);
@include border($col);
display: block;
}
-83
View File
@@ -1,83 +0,0 @@
/* stylelint-disable color-no-hex, color-hex-length */
$color-background-1: Background1;
$color-background-2: Background2;
$color-background-3: Background3;
$color-bg-overlay: BGOverlay;
$color-modal-overlay: ModalOverlay;
$color-bg-shadow: BGShadow;
$color-bg-shadow-2: BGShadow2;
$color-text: Text;
$color-text-active: TextActive;
$color-text-dim: TextDim;
$color-text-inactive: TextInactive;
$color-text-a5: TextA5;
$color-text-a20: TextA20;
$color-text-a30: TextA30;
$color-text-a50: TextA50;
$color-text-a80: TextA80;
$color-primary: Primary;
$color-primary-l: PrimaryL;
$color-primary-d: PrimaryD;
$color-primary-a5: PrimaryA5;
$color-primary-a20: PrimaryA20;
$color-primary-a30: PrimaryA30;
$color-primary-a50: PrimaryA50;
$color-primary-a80: PrimaryA80;
$color-secondary: Secondary;
$color-secondary-l: SecondaryL;
$color-secondary-d: SecondaryD;
$color-secondary-a5: SecondaryA5;
$color-secondary-a20: SecondaryA20;
$color-secondary-a30: SecondaryA30;
$color-secondary-a50: SecondaryA50;
$color-secondary-a80: SecondaryA80;
$color-warning: Warning;
$color-warning-l: WarningL;
$color-warning-d: WarningD;
$color-warning-a5: WarningA5;
$color-warning-a20: WarningA20;
$color-warning-a30: WarningA30;
$color-warning-a50: WarningA50;
$color-warning-a80: WarningA80;
$color-danger: Danger;
$color-danger-l: DangerL;
$color-danger-d: DangerD;
$color-danger-a5: DangerA5;
$color-danger-a20: DangerA20;
$color-danger-a30: DangerA30;
$color-danger-a50: DangerA50;
$color-danger-a80: DangerA80;
$color-success: Success;
$color-success-l: SuccessL;
$color-success-d: SuccessD;
$color-success-a5: SuccessA5;
$color-success-a20: SuccessA20;
$color-success-a30: SuccessA30;
$color-success-a50: SuccessA50;
$color-success-a80: SuccessA80;
$color-border: Border;
$color-border-soft: BorderSoft;
$color-border-hard: BorderHard;
$color-border-solid: BorderSolid;
$color-transparent: Transparent;
$color-a: A;
$color-a-l: AL;
$color-a-d: AD;
$color-a-a5: AA5;
$color-a-a20: AA20;
$color-a-a30: AA30;
$color-a-a50: AA50;
$color-a-a80: AA80;
$color-white: White;
$color-white-a5: WhiteA5;
$color-white-a20: WhiteA20;
$color-white-a30: WhiteA30;
$color-white-a50: WhiteA50;
$color-white-a80: WhiteA80;
$color-bw-05: BW05;
$color-bw-10: BW10;
$color-bw-25: BW25;
$color-bw-50: BW50;
$color-bw-75: BW75;
$color-bw-90: BW90;
-9
View File
@@ -1,9 +0,0 @@
// Not supported yet, need to use decorator: gradient atm
// $primary-lr-fade: linear-gradient(90deg, rgba($color-primary-l, 0.08) 0%, rgba($color-primary-l, 0.00) 100%);
// $primary-rl-fade: linear-gradient(90deg, rgba($color-primary-l, 0.00) 0%, rgba($color-primary-l, 0.08) 100%);
// $primary-lr-fade: horizontal-gradient(#{$color-primary-d}14 #{$color-primary-l}00);
// $primary-rl-fade: horizontal-gradient(#{$color-primary-d}00 #{$color-primary-l}14);
$primary-lr-fade: horizontal-gradient(#ff000014 #ff000000);
$primary-rl-fade: horizontal-gradient(#ff000000 #ff000014);
-7
View File
@@ -1,7 +0,0 @@
@use 'sass:math';
// $page-margin: 32;
$page-margin: 64;
$base-height: 1080;
$base-modal-height: $base-height - ($page-margin * 2);
$base-modal-max-width: math.div($base-modal-height * 16, 9);
@@ -1,3 +0,0 @@
// see: lib/RmlUi/Source/Core/PropertyParserAnimation.cpp
$transition-quick: 0.05s linear-in-out;
// $transition-quick: 0.033s linear-in-out;
+21 -30
View File
@@ -2,53 +2,46 @@
#define __BANJO_CONFIG_H__
#include <filesystem>
#include <string>
#include <string_view>
#include "ultramodern/config.hpp"
#include "recomp_input.h"
#include "json/json.hpp"
namespace banjo {
constexpr std::u8string_view program_id = u8"BanjoRecompiled";
constexpr std::string_view program_name = "Banjo: Recompiled";
inline const std::u8string program_id = u8"BanjoRecompiled";
inline const std::string program_name = "Banjo: Recompiled";
namespace configkeys {
namespace general {
inline const std::string note_saving_mode = "note_saving_mode";
inline const std::string camera_invert_mode = "camera_invert_mode";
inline const std::string analog_cam_mode = "analog_cam_mode";
inline const std::string analog_camera_invert_mode = "analog_camera_invert_mode";
}
namespace sound {
inline const std::string bgm_volume = "bgm_volume";
}
}
// TODO: Move loading configs to the runtime once we have a way to allow per-project customization.
void load_config();
void save_config();
void reset_input_bindings();
void reset_cont_input_bindings();
void reset_kb_input_bindings();
void reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input);
void init_config();
std::filesystem::path get_app_folder_path();
bool get_debug_mode_enabled();
void set_debug_mode_enabled(bool enabled);
enum class CameraInvertMode {
InvertNone,
InvertX,
InvertY,
InvertBoth,
OptionCount
InvertBoth
};
NLOHMANN_JSON_SERIALIZE_ENUM(banjo::CameraInvertMode, {
{banjo::CameraInvertMode::InvertNone, "InvertNone"},
{banjo::CameraInvertMode::InvertX, "InvertX"},
{banjo::CameraInvertMode::InvertY, "InvertY"},
{banjo::CameraInvertMode::InvertBoth, "InvertBoth"}
});
CameraInvertMode get_camera_invert_mode();
void set_camera_invert_mode(CameraInvertMode mode);
CameraInvertMode get_analog_camera_invert_mode();
void set_analog_camera_invert_mode(CameraInvertMode mode);
enum class AnalogCamMode {
On,
Off,
OptionCount
OptionCount
};
NLOHMANN_JSON_SERIALIZE_ENUM(banjo::AnalogCamMode, {
@@ -57,7 +50,6 @@ namespace banjo {
});
AnalogCamMode get_analog_cam_mode();
void set_analog_cam_mode(AnalogCamMode mode);
enum class NoteSavingMode {
On,
@@ -71,7 +63,6 @@ namespace banjo {
});
NoteSavingMode get_note_saving_mode();
void set_note_saving_mode(NoteSavingMode mode);
void open_quit_game_prompt();
};
+2 -1
View File
@@ -6,6 +6,7 @@
#include "common/rt64_user_configuration.h"
#include "ultramodern/renderer_context.hpp"
#include "librecomp/config.hpp"
#include "librecomp/mods.hpp"
namespace RT64 {
@@ -54,7 +55,7 @@ namespace banjo {
// Texture pack enable option. Must be an enum with two options.
// The first option is treated as disabled and the second option is treated as enabled.
bool is_texture_pack_enable_config_option(const recomp::mods::ConfigOption& option, bool show_errors);
bool is_texture_pack_enable_config_option(const recomp::config::ConfigOption& option, bool show_errors);
}
}
-4
View File
@@ -2,10 +2,6 @@
#define __BANJO_SOUND_H__
namespace banjo {
void reset_sound_settings();
void set_main_volume(int volume);
int get_main_volume();
void set_bgm_volume(int volume);
int get_bgm_volume();
}
-202
View File
@@ -1,202 +0,0 @@
#ifndef __RECOMP_INPUT_H__
#define __RECOMP_INPUT_H__
#include <cstdint>
#include <variant>
#include <vector>
#include <type_traits>
#include <span>
#include <string>
#include <string_view>
#include "ultramodern/input.hpp"
#include "json/json.hpp"
namespace recomp {
// x-macros to build input enums and arrays.
// First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name.
// TODO refactor this to allow projects to rename these, or get rid of the readable name and leave that up to individual projects to map.
#define DEFINE_N64_BUTTON_INPUTS() \
DEFINE_INPUT(A, 0x8000, "A") \
DEFINE_INPUT(B, 0x4000, "B") \
DEFINE_INPUT(Z, 0x2000, "Z") \
DEFINE_INPUT(START, 0x1000, "Start") \
DEFINE_INPUT(L, 0x0020, "L") \
DEFINE_INPUT(R, 0x0010, "R") \
DEFINE_INPUT(C_UP, 0x0008, "C Up") \
DEFINE_INPUT(C_LEFT, 0x0002, "C Left") \
DEFINE_INPUT(C_DOWN, 0x0004, "C Down") \
DEFINE_INPUT(C_RIGHT, 0x0001, "C Right") \
DEFINE_INPUT(DPAD_UP, 0x0800, "D Pad Down") \
DEFINE_INPUT(DPAD_RIGHT, 0x0100, "D-Pad Down") \
DEFINE_INPUT(DPAD_DOWN, 0x0400, "D-Pad Down") \
DEFINE_INPUT(DPAD_LEFT, 0x0200, "D-Pad Down")
#define DEFINE_N64_AXIS_INPUTS() \
DEFINE_INPUT(Y_AXIS_POS, 0, "Up") \
DEFINE_INPUT(Y_AXIS_NEG, 0, "Down") \
DEFINE_INPUT(X_AXIS_NEG, 0, "Left") \
DEFINE_INPUT(X_AXIS_POS, 0, "Right") \
#define DEFINE_RECOMP_UI_INPUTS() \
DEFINE_INPUT(TOGGLE_MENU, 0, "Toggle Menu") \
DEFINE_INPUT(ACCEPT_MENU, 0, "Accept (Menu)") \
DEFINE_INPUT(APPLY_MENU, 0, "Apply (Menu)")
#define DEFINE_ALL_INPUTS() \
DEFINE_N64_BUTTON_INPUTS() \
DEFINE_N64_AXIS_INPUTS() \
DEFINE_RECOMP_UI_INPUTS()
// Enum containing every recomp input.
#define DEFINE_INPUT(name, value, readable) name,
enum class GameInput {
DEFINE_ALL_INPUTS()
COUNT,
N64_BUTTON_START = A,
N64_BUTTON_COUNT = C_RIGHT - N64_BUTTON_START + 1,
N64_AXIS_START = X_AXIS_NEG,
N64_AXIS_COUNT = Y_AXIS_POS - N64_AXIS_START + 1,
};
#undef DEFINE_INPUT
struct InputField {
uint32_t input_type;
int32_t input_id;
std::string to_string() const;
auto operator<=>(const InputField& rhs) const = default;
};
void poll_inputs();
float get_input_analog(const InputField& field);
float get_input_analog(const std::span<const recomp::InputField> fields);
bool get_input_digital(const InputField& field);
bool get_input_digital(const std::span<const recomp::InputField> fields);
void get_gyro_deltas(float* x, float* y);
void get_mouse_deltas(float* x, float* y);
void get_right_analog(float* x, float* y);
enum class InputDevice {
Controller,
Keyboard,
COUNT
};
void start_scanning_input(InputDevice device);
void stop_scanning_input();
void finish_scanning_input(InputField scanned_field);
void cancel_scanning_input();
void config_menu_set_cont_or_kb(bool cont_interacted);
InputField get_scanned_input();
int get_scanned_input_index();
struct DefaultN64Mappings {
std::vector<InputField> a;
std::vector<InputField> b;
std::vector<InputField> l;
std::vector<InputField> r;
std::vector<InputField> z;
std::vector<InputField> start;
std::vector<InputField> c_left;
std::vector<InputField> c_right;
std::vector<InputField> c_up;
std::vector<InputField> c_down;
std::vector<InputField> dpad_left;
std::vector<InputField> dpad_right;
std::vector<InputField> dpad_up;
std::vector<InputField> dpad_down;
std::vector<InputField> analog_left;
std::vector<InputField> analog_right;
std::vector<InputField> analog_up;
std::vector<InputField> analog_down;
std::vector<InputField> toggle_menu;
std::vector<InputField> accept_menu;
std::vector<InputField> apply_menu;
};
inline const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
static const std::vector<InputField> empty_input_field{};
switch (input) {
case GameInput::A: return defaults.a;
case GameInput::B: return defaults.b;
case GameInput::L: return defaults.l;
case GameInput::R: return defaults.r;
case GameInput::Z: return defaults.z;
case GameInput::START: return defaults.start;
case GameInput::C_LEFT: return defaults.c_left;
case GameInput::C_RIGHT: return defaults.c_right;
case GameInput::C_UP: return defaults.c_up;
case GameInput::C_DOWN: return defaults.c_down;
case GameInput::DPAD_LEFT: return defaults.dpad_left;
case GameInput::DPAD_RIGHT: return defaults.dpad_right;
case GameInput::DPAD_UP: return defaults.dpad_up;
case GameInput::DPAD_DOWN: return defaults.dpad_down;
case GameInput::X_AXIS_NEG: return defaults.analog_left;
case GameInput::X_AXIS_POS: return defaults.analog_right;
case GameInput::Y_AXIS_POS: return defaults.analog_up;
case GameInput::Y_AXIS_NEG: return defaults.analog_down;
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
case GameInput::APPLY_MENU: return defaults.apply_menu;
default: return empty_input_field;
}
}
extern const DefaultN64Mappings default_n64_keyboard_mappings;
extern const DefaultN64Mappings default_n64_controller_mappings;
constexpr size_t bindings_per_input = 2;
size_t get_num_inputs();
const std::string& get_input_name(GameInput input);
const std::string& get_input_enum_name(GameInput input);
GameInput get_input_from_enum_name(const std::string_view name);
InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device);
void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value);
bool get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out);
void set_rumble(int controller_num, bool);
void update_rumble();
void handle_events();
ultramodern::input::connected_device_info_t get_connected_device_info(int controller_num);
// Rumble strength ranges from 0 to 100.
int get_rumble_strength();
void set_rumble_strength(int strength);
// Gyro and mouse sensitivities range from 0 to 100.
int get_gyro_sensitivity();
int get_mouse_sensitivity();
int get_joystick_deadzone();
void set_gyro_sensitivity(int strength);
void set_mouse_sensitivity(int strength);
void set_joystick_deadzone(int strength);
void apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out);
void set_right_analog_suppressed(bool suppressed);
enum class BackgroundInputMode {
On,
Off,
OptionCount
};
NLOHMANN_JSON_SERIALIZE_ENUM(recomp::BackgroundInputMode, {
{recomp::BackgroundInputMode::On, "On"},
{recomp::BackgroundInputMode::Off, "Off"}
});
BackgroundInputMode get_background_input_mode();
void set_background_input_mode(BackgroundInputMode mode);
bool game_input_disabled();
bool all_input_disabled();
}
#endif
-138
View File
@@ -1,138 +0,0 @@
#ifndef __RECOMP_UI__
#define __RECOMP_UI__
#include <memory>
#include <string>
#include <string_view>
#include <list>
// TODO move this file into src/ui
#include "SDL.h"
#include "RmlUi/Core.h"
#include "../src/ui/util/hsv.h"
#include "../src/ui/util/bem.h"
#include "../src/ui/elements/ui_button.h"
#include "../src/ui/elements/ui_theme.h"
#include "../src/ui/core/ui_context.h"
namespace Rml {
class ElementDocument;
class EventListenerInstancer;
class Context;
class Event;
}
namespace recompui {
class UiEventListenerInstancer;
// TODO remove this once the UI has been ported over to the new system.
class MenuController {
public:
virtual ~MenuController() {}
virtual void load_document() = 0;
virtual void register_events(UiEventListenerInstancer& listener) = 0;
virtual void make_bindings(Rml::Context* context) = 0;
};
std::unique_ptr<MenuController> create_launcher_menu();
std::unique_ptr<MenuController> create_config_menu();
using event_handler_t = void(const std::string& param, Rml::Event&);
void queue_event(const SDL_Event& event);
bool try_deque_event(SDL_Event& out);
std::unique_ptr<UiEventListenerInstancer> make_event_listener_instancer();
void register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler);
void show_context(ContextId context, std::string_view param);
void hide_context(ContextId context);
void hide_all_contexts();
bool is_context_shown(ContextId context);
bool is_context_capturing_input();
bool is_context_capturing_mouse();
bool is_any_context_shown();
ContextId try_close_current_context();
ContextId get_launcher_context_id();
ContextId get_config_context_id();
ContextId get_config_sub_menu_context_id();
enum class ConfigTab {
General,
Controls,
Graphics,
Sound,
Mods,
Debug,
};
void set_config_tab(ConfigTab tab);
int config_tab_to_index(ConfigTab tab);
Rml::ElementTabSet* get_config_tabset();
Rml::Element* get_mod_tab();
void set_config_tabset_mod_nav();
void focus_mod_configure_button();
void init_styling(const std::filesystem::path& rcss_file);
void init_prompt_context();
void open_choice_prompt(
const std::string& header_text,
const std::string& content_text,
const std::string& confirm_label_text,
const std::string& cancel_label_text,
std::function<void()> confirm_action,
std::function<void()> cancel_action,
ButtonStyle confirm_variant = ButtonStyle::Success,
ButtonStyle cancel_variant = ButtonStyle::Danger,
bool focus_on_cancel = true,
const std::string& return_element_id = ""
);
void open_info_prompt(
const std::string& header_text,
const std::string& content_text,
const std::string& okay_label_text,
std::function<void()> okay_action,
ButtonStyle okay_variant = ButtonStyle::Danger,
const std::string& return_element_id = ""
);
void open_notification(
const std::string& header_text,
const std::string& content_text,
const std::string& return_element_id = ""
);
void close_prompt();
bool is_prompt_open();
void update_mod_list(bool scan_mods = true);
void process_game_started();
void apply_color_hack();
void get_window_size(int& width, int& height);
void set_cursor_visible(bool visible);
void update_supported_options();
void toggle_fullscreen();
bool get_cont_active(void);
void set_cont_active(bool active);
void activate_mouse();
void message_box(const char* msg);
void set_render_hooks();
Rml::ElementPtr create_custom_element(Rml::Element* parent, std::string tag);
Rml::ElementDocument* load_document(const std::filesystem::path& path);
Rml::ElementDocument* create_empty_document();
Rml::Element* get_child_by_tag(Rml::Element* parent, const std::string& tag);
void queue_image_from_bytes_rgba32(const std::string &src, const std::vector<char> &bytes, uint32_t width, uint32_t height);
void queue_image_from_bytes_file(const std::string &src, const std::vector<char> &bytes);
void release_image(const std::string &src);
void drop_files(const std::list<std::filesystem::path> &file_list);
}
#endif
-9
View File
@@ -1,9 +0,0 @@
set(FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include)
set(FREETYPE_LIBRARIES "${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/release static/vs2015-2022/win64/freetype.lib")
add_library(Freetype::Freetype STATIC IMPORTED)
set_target_properties(Freetype::Freetype PROPERTIES
IMPORTED_LOCATION ${FREETYPE_LIBRARIES}
)
target_include_directories(Freetype::Freetype INTERFACE
${FREETYPE_INCLUDE_DIRS}
)
-32
View File
@@ -1,32 +0,0 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
-11
View File
@@ -1,11 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(GamepadMotionHelpers LANGUAGES CXX)
add_library(${PROJECT_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_include_directories(${PROJECT_NAME}
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
File diff suppressed because it is too large Load Diff
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020-2023 Julian "Jibb" Smart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-76
View File
@@ -1,76 +0,0 @@
# GamepadMotionHelpers
GamepadMotionHelpers is a lightweight header-only library for sensor fusion, gyro calibration, etc. BYO input library (eg [SDL2](https://github.com/libsdl-org/SDL)).
## Units
Convert your gyro units into **degrees per second** and accelerometer units to **g-force** (1 g = 9.8 m/s^2). You don't have to use these units in your application, but convert to these units when writing to GamepadMotionHelpers and convert back when reading from it. Your input reader might prefer radians per second and metres per second squared, but the datasheets for every IMU I've seen talk about degrees per second and g-force.
## Coordinate Space
This library uses a Y-up coordinate system. While Z-up is (only slightly) preferable for many games, PlayStation controllers use Y-up, and have set the standard for input libraries like [SDL2](https://github.com/libsdl-org/SDL) and [JSL](https://github.com/JibbSmart/JoyShockLibrary). These libraries convert inputs from other controller types to the same space used by PlayStation's DualShock 4 and DualSense, so that's what's used here.
## Basic Use
Include the GamepadMotion.hpp file in your C++ project. That's it! Everything you need is in that file, and its only dependency is ```<math.h>```.
For each controller with gyro (and optionally accelerometer), create a ```GamepadMotion``` object. At regular intervals, whether when a new report comes in from the controller or when polling the controller's state, you should call ```ProcessMotion(...)```. This is when you tell your GamepadMotion object the latest gyro (in degrees per second) and accelerometer (in g-force) inputs. You'll also give it the time since the last update for this controller (in seconds).
ProcessMotion takes these inputs, updates some internal values, and then you can use any of the following to read its current state:
- ```GetCalibratedGyro(float& x, float& y, float& z)``` - Get the controller's angular velocity in degrees per second. This is just the raw gyro you gave it minus the gyro's bias as determined by your calibration settings (more on that below).
- ```GetGravity(float& x, float& y, float& z)``` - Get the gravity direction in the controller's local space. When the controller is still on a flat surface it'll be approximately (0, -1, 0). The controller can't detect the gravity direction when it's in freefall or being shaken around, but it can make a pretty good guess if its gyro is correctly calibrated and then make further corrections when the controller is still again.
- ```GetProcessedAcceleration(float& x, float& y, float& z)``` - Get the controller's current acceleration in g-force with gravity removed. Raw accelerometer input includes gravity -- it is only (0, 0, 0) when the controller is in freefall. However, using the gravity direction as calculated for GetGravity, it can remove that component and detect how you're shaking the controller about. This function gives you that acceleration vector with the gravity removed.
- ```GetOrientation(float& w, float& x, float& y, float& z)``` - Get the controller's orientation. Gyro and accelerometer input are combined to give a good estimate of the controller's orientation.
Additional helper functions are available for taking gravity into account and returning a "world space" or "player space" rotation in two axes. Bear in mind that the **X** and **Y** set by these functions is still around the controller's axes. This means **Y** is the *horizontal* part of the rotation, and **X** is the vertical part. To convert to a mouse-like input, you'll treat the **Y** as the horizontal or yaw input and **X** as the vertical or pitch input. This might be unintuitive, but since it's also true of the "local space" angular velocities obtained from GetCalibratedGyro, this makes it simple to let the user choose between *local space*, *world space*, and *player space* in your game or application by just swapping GetCalibratedGyro for these functions depending on that selection:
- ```GetWorldSpaceGyro(float& x, float& y, const float sideReductionThreshold = 0.125f)``` - Get the controller's angular velocity in *world space* as described on GyroWiki in the [player space article here](http://gyrowiki.jibbsmart.com/blog:player-space-gyro-and-alternatives-explained#toc6). Yaw input will be derived from motion around the gravity axis, and pitch input from an appropriate pitch axis calculated from the controller's orientation with respect to the gravity axis. Any errors in the calculated gravity axis (though likely very small) will be taken on by the calculated world space gyro rotation, making it slightly less robust than using calibrated gyro directly ("local space" gyro) or using *player space* gyro below. More info in the linked article.
- ```GetPlayerSpaceGyro(float& x, float& y, const float yawRelaxFactor = 1.41f)``` - Get the controller's angular velocity in *player space* as described on GyroWiki in the [player space article here](http://gyrowiki.jibbsmart.com/blog:player-space-gyro-and-alternatives-explained#toc7). Yaw input will be derived from motion approximately around the gravity axis, without any impact from errors in the gravity calculation. Pitch is just local pitch. It is robust, accommodates players who are used to both local space and world space gyro, while taking on most of the advantages of each. It is proven in popular games and is an ideal default for players using a standalone controller. For handheld (where the screen is part of the controller, such as mobile, Switch, or Steam Deck) local space (using the calibrated gyro input directly) may be preferable. More info in the linked article.
If you want to plug in the gyro and gravity values yourself (perhaps you're using an externally calculated gravity), you can use ```CalculateWorldSpaceGyro``` and ```CalculatePlayerSpaceGyro``` instead. Make sure you use this GamepadMotionHelpers' coordinate space, units, and gravity is normalized, since those are all assumed for these functions.
## Sensor Fusion
Combining multiple types of sensor like this to get a better picture of the controller's state is called "sensor fusion". Moment-to-moment changes in orientation are detected using the gyro, but that only gives local angular velocity and needs to be correctly calibrated. Errors can accumulate over time. The gravity vector as detected by the accelerometer is used to make corrections to the relevant components of the controller's orientation.
But this cannot be used to correct the controller's orientation around the gravity vector (the **yaw** axis). If you're using the controller's absolute orientation for some reason, this "yaw drift" may need to be accounted for somehow. Some devices also have a magnetometer (compass) to counter yaw drift, but since popular game controllers don't have a magnetometer, I haven't tried it myself. In future, if I get such a device, I'd like to add the option for GamepadMotionHelpers to accept magnetometer input and account for it when calculating values for the above functions.
## Gyro Calibration
Modern gyroscopes often need calibration. This is like how a [weighing scale](https://en.wikipedia.org/wiki/Weighing_scale) can need calibration to tell it what 'zero' is. Like a weighing scale, a correctly calibrated gyroscope will give an accurate reading. If you're using the gyro input as a mouse, which is the simplest application of a controller's gyro, you can find essential reading on [GyroWiki here](http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-1:the-gyro-is-a-mouse).
Calibration just means having the controller sit still and remembering the average reported angular velocity in each axis. This is the gyro's "bias". In GamepadMotionHelpers, I call our best guess at the controller's bias the "calibration offset". GamepadMotionHelpers has some options to help with calibrating:
At any time, you can begin manually calibrating a controller by calling ```StartContinuousCalibration()```. This will start recording the average angular velocity and apply it immediately to any subsequent **GetGalibratedGyro(...)** call. At any time you can ```PauseContinuousCalibration()``` to no longer add current values to the average angular velocity being recorded. You can ```ResetContinousCalibration()``` to remove the recorded average before starting over with **StartContinuousCalibration** again.
You can read the stored calibration values using ```GetCalibrationOffset(float& xOffset, float& yOffset, float& zOffset)```. You can manually set the calibration offset yourself with ```SetCalibrationOffset(float xOffset, float yOffset, float zOffset, int weight)```. This will override all stored values. The **weight** argument at the end determines how strongly these values should be considered over time if Continuous Calibration is still active (new values are still being added to the average). Each new sample has a weight of 1, so if you **SetCalibrationOffset** with a weight of 10, it'll have the weight of 10 samples when calculating the average. If you're not continuing to add samples (Continuous Calibration is not active), the weight will be meaningless. Setting this manually is unusual, so don't worry about it too much if that sounds complicated.
Most games don't ask the user to calibrate the gyro themselves. They have built-in automatic calibration, which I like to call "auto-calibration". There's no such thing as a "good enough" auto-calibration solution -- at least not with only gyro and accelerometer. Every game that has an auto-calibration solution would be made better for more serious players with the option to manually calibrate their gyro, so I urge you to provide players the option to do the same in your game. Having said that, auto-calibration is a useful option for casual players, and you may choose to have it enabled in your game by default.
So GamepadMotionHelpers provides some auto-calibration options. You can call ```SetCalibrationMode(CalibrationMode)``` on each GamepadMotion instance with the following options:
- ```CalibrationMode::Manual``` - No auto-calibration. This is the default.
- ```CalibrationMode::Stillness``` - Automatically try to detect when the controller is being held still and update the calibration offset accordingly.
- ```CalibrationMode::SensorFusion``` - Calculate an angular velocity from changes in the gravity direction as detected by the accelerometer. If these are steady enough, use them to make corrections to the calibration offset. This will only apply to relevant axes.
Many players are already aware of the shortcomings of trying to automatically detect stillness to automatically calibrate the gyro. Whether on Switch, PlayStation, or using PlayStation controllers on PC, players have tried to track a slow or distant target only to have the aimer suddenly stop moving! The game or the platform has **misinterpreted their slow and steady input as the controller being held still**, and they've incorrectly recalibrated accordingly. Players *hate it* when this happens.
**This is why it's important to let players manually calibrate their gyro** if they want to.
Auto-calibration is used so widely in console games that it's speculated that game developers may not have the option to disable it on these platforms. If this is the case, GamepadMotionHelpers offers a big advantage over those platforms: you can disable it and enable it at any time.
You, the game developer, can have your game tell if the player is tracking a distant or slow-moving target. You can tell if the player's aimer is moving towards a visible target or roughly following the movement of one. When it is, maybe disabling the auto-calibration (```SetCalibrationMode(CalibrationMode::Manual)```) could be the difference between good and bad auto-calibration. I don't know if the GamepadMotionHelpers auto-calibration functions are better or worse than their Switch and PlayStation counterparts generally, but by letting you take the game's context into account, you may be able to offer players a way better experience without them having to manually calibrate.
But still give them the option to calibrate manually, please :)
The **SensorFusion** calibration mode has shortcomings of its own. It's much harder to accidentally trick the game into incorrectly calibrating, but the angular velocity calculated from the accelerometer moment-to-moment is generally much less precise. Leaving the controller still, you'll notice the calibrated gyro moving slightly up and down over time. So while the **Stillness** mode is characterised by good behaviour occasionally punctuated by frustrating errors, the **SensorFusion** mode will tend to be more consistently not-quite-right without being terrible.
Secondly, this library currently only combines accelerometer and gyro, so the **SensorFusion** auto-calibration cannot correct the gyro in all axes at the same time. The **SensorFusion**-only mode will be more useful in future when magnetometer input is supported, which can account for the axes that the accelerometer can't.
Both auto-calibration modes can be combined by passing ```CalibrationMode::Stillness | CalibrationMode::SensorFusion``` to **SetCalibrationMode**. In this case, it'll use **Stillness** auto-calibration, but it'll adjust the calibration offset based on any angular velocity implied by changes in the accelerometer input. This tends to give better results than just using **Stillness** or **SensorFusion** on their own.
If you aren't sure what to choose, I'd suggest using the combined ```CalibrationMode::Stillness | CalibrationMode::SensorFusion``` when auto calibration is enabled, but also allowing the player to manually calibrate.
**TODO** This is a clunky way to let the user set up what is obviously the best solution. Maybe I should just call it "hybrid" or something and be done with it?
Auto-calibration can also be used to communicate manual calibration to the player. ```GetAutoCalibrationIsSteady()``` will tell you whether GamepadMotionHelpers thinks the controller is currently being held steady (if auto-calibration is enabled). ```GetAutoCalibrationConfidence()``` will tell you how confident GamepadMotionHelpers is that it has a good calibration value from auto-calibration, from 0-1. Higher confidence means that new calibration changes will be applied more gradually. You can use these functions to detect when a controller needs to be calibrated, prompt the player to put their controller down, detect when they have put their controller down, and show progress for calibration (default 1 second once it starts). You can also override the confidence yourself (```SetAutoCalibrationConfidence()```), and resetting calibration will reset confidence to 0. How quickly confidence grows as well as other calibration settings can be customised in **GamepadMotionSettings**.
## In the Wild
GamepadMotionHelpers is currently used in:
- [JoyShockMapper](https://github.com/Electronicks/JoyShockMapper)
- [JoyShockLibrary](https://github.com/JibbSmart/JoyShockLibrary)
- JoyShockOverlay
If you know of any other games or applications using GamepadMotionHelpers, please let me know!
Submodule lib/RecompFrontend added at 8a58012d20
Submodule lib/RmlUi deleted from 7a06f27db0
Submodule lib/lunasvg deleted from 4166d0cccf
+18 -1
View File
@@ -1,7 +1,7 @@
#ifndef __UI_FUNCS_H__
#define __UI_FUNCS_H__
// These two enums must be kept in sync with src/ui/elements/ui_types.h!
// These three enums must be kept in sync with src/ui/elements/ui_types.h!
typedef enum {
UI_EVENT_NONE,
UI_EVENT_CLICK,
@@ -11,6 +11,9 @@ typedef enum {
UI_EVENT_DRAG,
UI_EVENT_RESERVED1, // Would be UI_EVENT_TEXT but text events aren't usable in mods currently
UI_EVENT_UPDATE,
UI_EVENT_NAVIGATE,
UI_EVENT_MOUSE_BUTTON,
UI_EVENT_MENU_ACTION,
UI_EVENT_COUNT
} RecompuiEventType;
@@ -21,6 +24,16 @@ typedef enum {
UI_DRAG_END
} RecompuiDragPhase;
typedef enum {
UI_MENU_ACTION_NONE,
UI_MENU_ACTION_ACCEPT,
UI_MENU_ACTION_APPLY,
UI_MENU_ACTION_BACK,
UI_MENU_ACTION_TOGGLE,
UI_MENU_ACTION_TAB_LEFT,
UI_MENU_ACTION_TAB_RIGHT
} RecompuiMenuAction;
typedef struct {
RecompuiEventType type;
union {
@@ -46,6 +59,10 @@ typedef struct {
float y;
RecompuiDragPhase phase;
} drag;
struct {
RecompuiMenuAction action;
} menu_action;
} data;
} RecompuiEventData;
+97 -479
View File
@@ -1,10 +1,14 @@
#include "banjo_config.h"
#include "recomp_input.h"
#include "recompui/recompui.h"
#include "recompui/config.h"
#include "recompinput/recompinput.h"
#include "banjo_sound.h"
#include "banjo_render.h"
#include "banjo_support.h"
#include "ultramodern/config.hpp"
#include "librecomp/files.hpp"
#include "librecomp/config.hpp"
#include "util/file.h"
#include <filesystem>
#include <fstream>
#include <iomanip>
@@ -18,511 +22,125 @@
#include "apple/rt64_apple.h"
#endif
constexpr std::u8string_view general_filename = u8"general.json";
constexpr std::u8string_view graphics_filename = u8"graphics.json";
constexpr std::u8string_view controls_filename = u8"controls.json";
constexpr std::u8string_view sound_filename = u8"sound.json";
static void add_general_options(recomp::config::Config &config) {
using EnumOptionVector = const std::vector<recomp::config::ConfigOptionEnumOption>;
constexpr auto res_default = ultramodern::renderer::Resolution::Auto;
constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9;
constexpr auto api_default = ultramodern::renderer::GraphicsApi::Auto;
constexpr auto ar_default = ultramodern::renderer::AspectRatio::Expand;
constexpr auto msaa_default = ultramodern::renderer::Antialiasing::MSAA2X;
constexpr auto rr_default = ultramodern::renderer::RefreshRate::Display;
constexpr auto hpfb_default = ultramodern::renderer::HighPrecisionFramebuffer::Off;
constexpr int ds_default = 1;
constexpr int rr_manual_default = 60;
constexpr bool developer_mode_default = false;
static EnumOptionVector note_saving_mode_options = {
{banjo::NoteSavingMode::Off, "Off", "Off"},
{banjo::NoteSavingMode::On, "On", "On"},
};
config.add_enum_option(
banjo::configkeys::general::note_saving_mode,
"Note Saving",
"Saves collected notes so that you don't need to collect them again when revisiting a level. <recomp-color primary>On</recomp-color> is the default, while <recomp-color primary>off</recomp-color> matches the original game.",
note_saving_mode_options,
banjo::NoteSavingMode::On
);
static bool is_steam_deck = false;
static EnumOptionVector camera_invert_mode_options = {
{banjo::CameraInvertMode::InvertNone, "InvertNone", "None"},
{banjo::CameraInvertMode::InvertX, "InvertX", "Invert X"},
{banjo::CameraInvertMode::InvertY, "InvertY", "Invert Y"},
{banjo::CameraInvertMode::InvertBoth, "InvertBoth", "Invert Both"}
};
config.add_enum_option(
banjo::configkeys::general::camera_invert_mode,
"Aiming Camera Inverting",
// TODO: Update for banjo
"Inverts the camera controls. <recomp-color primary>Invert Y</recomp-color> is the default and matches the original game.",
camera_invert_mode_options,
banjo::CameraInvertMode::InvertY
);
ultramodern::renderer::WindowMode wm_default() {
return is_steam_deck ? ultramodern::renderer::WindowMode::Fullscreen : ultramodern::renderer::WindowMode::Windowed;
}
#ifdef __gnu_linux__
void detect_steam_deck() {
// Check if the board vendor is Valve.
std::ifstream board_vendor_file("/sys/devices/virtual/dmi/id/board_vendor");
std::string line;
if (std::getline(board_vendor_file, line).good() && line == "Valve") {
is_steam_deck = true;
return;
}
// Check if the SteamDeck variable is set to 1.
const char* steam_deck_env = getenv("SteamDeck");
if (steam_deck_env != nullptr && std::string{steam_deck_env} == "1") {
is_steam_deck = true;
return;
}
is_steam_deck = false;
return;
}
#else
void detect_steam_deck() { is_steam_deck = false; }
#endif
template <typename T>
T from_or_default(const json& j, const std::string& key, T default_value) {
T ret;
auto find_it = j.find(key);
if (find_it != j.end()) {
find_it->get_to(ret);
}
else {
ret = default_value;
}
return ret;
}
template <typename T>
void call_if_key_exists(void (*func)(T), const json& j, const std::string& key) {
auto find_it = j.find(key);
if (find_it != j.end()) {
T val;
find_it->get_to(val);
func(val);
}
}
namespace ultramodern {
void to_json(json& j, const renderer::GraphicsConfig& config) {
j = json{
{"res_option", config.res_option},
{"wm_option", config.wm_option},
{"hr_option", config.hr_option},
{"api_option", config.api_option},
{"ds_option", config.ds_option},
{"ar_option", config.ar_option},
{"msaa_option", config.msaa_option},
{"rr_option", config.rr_option},
{"hpfb_option", config.hpfb_option},
{"rr_manual_value", config.rr_manual_value},
{"developer_mode", config.developer_mode},
};
}
void from_json(const json& j, renderer::GraphicsConfig& config) {
config.res_option = from_or_default(j, "res_option", res_default);
config.wm_option = from_or_default(j, "wm_option", wm_default());
config.hr_option = from_or_default(j, "hr_option", hr_default);
config.api_option = from_or_default(j, "api_option", api_default);
config.ds_option = from_or_default(j, "ds_option", ds_default);
config.ar_option = from_or_default(j, "ar_option", ar_default);
config.msaa_option = from_or_default(j, "msaa_option", msaa_default);
config.rr_option = from_or_default(j, "rr_option", rr_default);
config.hpfb_option = from_or_default(j, "hpfb_option", hpfb_default);
config.rr_manual_value = from_or_default(j, "rr_manual_value", rr_manual_default);
config.developer_mode = from_or_default(j, "developer_mode", developer_mode_default);
}
}
namespace recomp {
void to_json(json& j, const InputField& field) {
j = json{ {"input_type", field.input_type}, {"input_id", field.input_id} };
}
void from_json(const json& j, InputField& field) {
j.at("input_type").get_to(field.input_type);
j.at("input_id").get_to(field.input_id);
}
}
std::filesystem::path banjo::get_app_folder_path() {
// directly check for portable.txt (windows and native linux binary)
if (std::filesystem::exists("portable.txt")) {
return std::filesystem::current_path();
}
#if defined(__APPLE__)
// Check for portable file in the directory containing the app bundle.
const auto app_bundle_path = banjo::get_bundle_directory().parent_path();
if (std::filesystem::exists(app_bundle_path / "portable.txt")) {
return app_bundle_path;
}
#endif
std::filesystem::path recomp_dir{};
#if defined(_WIN32)
// Deduce local app data path.
PWSTR known_path = NULL;
HRESULT result = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &known_path);
if (result == S_OK) {
recomp_dir = std::filesystem::path{known_path} / banjo::program_id;
}
CoTaskMemFree(known_path);
#elif defined(__linux__) || defined(__APPLE__)
// check for APP_FOLDER_PATH env var
if (getenv("APP_FOLDER_PATH") != nullptr) {
return std::filesystem::path{getenv("APP_FOLDER_PATH")};
}
#if defined(__APPLE__)
const auto supportdir = banjo::get_application_support_directory();
if (supportdir) {
return *supportdir / banjo::program_id;
}
#endif
const char *homedir;
if ((homedir = getenv("HOME")) == nullptr) {
#if defined(__linux__)
homedir = getpwuid(getuid())->pw_dir;
#elif defined(__APPLE__)
homedir = GetHomeDirectory();
#endif
}
if (homedir != nullptr) {
recomp_dir = std::filesystem::path{homedir} / (std::u8string{u8".config/"} + std::u8string{banjo::program_id});
}
#endif
return recomp_dir;
}
bool read_json(std::ifstream input_file, nlohmann::json& json_out) {
if (!input_file.good()) {
return false;
}
try {
input_file >> json_out;
}
catch (nlohmann::json::parse_error&) {
return false;
}
return true;
}
bool read_json_with_backups(const std::filesystem::path& path, nlohmann::json& json_out) {
// Try reading and parsing the base file.
if (read_json(std::ifstream{path}, json_out)) {
return true;
}
// Try reading and parsing the backup file.
if (read_json(recomp::open_input_backup_file(path), json_out)) {
return true;
}
// Both reads failed.
return false;
}
bool save_json_with_backups(const std::filesystem::path& path, const nlohmann::json& json_data) {
{
std::ofstream output_file = recomp::open_output_file_with_backup(path);
if (!output_file.good()) {
return false;
}
output_file << std::setw(4) << json_data;
}
return recomp::finalize_output_file_with_backup(path);
}
bool save_general_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
recomp::to_json(config_json["background_input_mode"], recomp::get_background_input_mode());
config_json["rumble_strength"] = recomp::get_rumble_strength();
config_json["gyro_sensitivity"] = recomp::get_gyro_sensitivity();
config_json["mouse_sensitivity"] = recomp::get_mouse_sensitivity();
config_json["joystick_deadzone"] = recomp::get_joystick_deadzone();
config_json["camera_invert_mode"] = banjo::get_camera_invert_mode();
config_json["analog_cam_mode"] = banjo::get_analog_cam_mode();
config_json["analog_camera_invert_mode"] = banjo::get_analog_camera_invert_mode();
config_json["note_saving_mode"] = banjo::get_note_saving_mode();
config_json["debug_mode"] = banjo::get_debug_mode_enabled();
return save_json_with_backups(path, config_json);
}
void set_general_settings_from_json(const nlohmann::json& config_json) {
recomp::set_background_input_mode(from_or_default(config_json, "background_input_mode", recomp::BackgroundInputMode::On));
recomp::set_rumble_strength(from_or_default(config_json, "rumble_strength", 25));
recomp::set_gyro_sensitivity(from_or_default(config_json, "gyro_sensitivity", 50));
recomp::set_mouse_sensitivity(from_or_default(config_json, "mouse_sensitivity", is_steam_deck ? 50 : 0));
recomp::set_joystick_deadzone(from_or_default(config_json, "joystick_deadzone", 5));
banjo::set_camera_invert_mode(from_or_default(config_json, "camera_invert_mode", banjo::CameraInvertMode::InvertY));
banjo::set_analog_cam_mode(from_or_default(config_json, "analog_cam_mode", banjo::AnalogCamMode::Off));
banjo::set_analog_camera_invert_mode(from_or_default(config_json, "analog_camera_invert_mode", banjo::CameraInvertMode::InvertNone));
banjo::set_note_saving_mode(from_or_default(config_json, "note_saving_mode", banjo::NoteSavingMode::On));
banjo::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false));
}
bool load_general_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
if (!read_json_with_backups(path, config_json)) {
return false;
}
set_general_settings_from_json(config_json);
return true;
}
void assign_mapping(recomp::InputDevice device, recomp::GameInput input, const std::vector<recomp::InputField>& value) {
for (size_t binding_index = 0; binding_index < std::min(value.size(), recomp::bindings_per_input); binding_index++) {
recomp::set_input_binding(input, binding_index, device, value[binding_index]);
}
};
// same as assign_mapping, except will clear unassigned bindings if not in value
void assign_mapping_complete(recomp::InputDevice device, recomp::GameInput input, const std::vector<recomp::InputField>& value) {
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) {
if (binding_index >= value.size()) {
recomp::set_input_binding(input, binding_index, device, recomp::InputField{});
} else {
recomp::set_input_binding(input, binding_index, device, value[binding_index]);
}
}
};
void assign_all_mappings(recomp::InputDevice device, const recomp::DefaultN64Mappings& values) {
assign_mapping_complete(device, recomp::GameInput::A, values.a);
assign_mapping_complete(device, recomp::GameInput::B, values.b);
assign_mapping_complete(device, recomp::GameInput::Z, values.z);
assign_mapping_complete(device, recomp::GameInput::START, values.start);
assign_mapping_complete(device, recomp::GameInput::DPAD_UP, values.dpad_up);
assign_mapping_complete(device, recomp::GameInput::DPAD_DOWN, values.dpad_down);
assign_mapping_complete(device, recomp::GameInput::DPAD_LEFT, values.dpad_left);
assign_mapping_complete(device, recomp::GameInput::DPAD_RIGHT, values.dpad_right);
assign_mapping_complete(device, recomp::GameInput::L, values.l);
assign_mapping_complete(device, recomp::GameInput::R, values.r);
assign_mapping_complete(device, recomp::GameInput::C_UP, values.c_up);
assign_mapping_complete(device, recomp::GameInput::C_DOWN, values.c_down);
assign_mapping_complete(device, recomp::GameInput::C_LEFT, values.c_left);
assign_mapping_complete(device, recomp::GameInput::C_RIGHT, values.c_right);
assign_mapping_complete(device, recomp::GameInput::X_AXIS_NEG, values.analog_left);
assign_mapping_complete(device, recomp::GameInput::X_AXIS_POS, values.analog_right);
assign_mapping_complete(device, recomp::GameInput::Y_AXIS_NEG, values.analog_down);
assign_mapping_complete(device, recomp::GameInput::Y_AXIS_POS, values.analog_up);
assign_mapping_complete(device, recomp::GameInput::TOGGLE_MENU, values.toggle_menu);
assign_mapping_complete(device, recomp::GameInput::ACCEPT_MENU, values.accept_menu);
assign_mapping_complete(device, recomp::GameInput::APPLY_MENU, values.apply_menu);
};
void banjo::reset_input_bindings() {
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
}
void banjo::reset_cont_input_bindings() {
assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
}
void banjo::reset_kb_input_bindings() {
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
}
void banjo::reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input) {
assign_mapping_complete(
device,
input,
recomp::get_default_mapping_for_input(
device == recomp::InputDevice::Keyboard ?
recomp::default_n64_keyboard_mappings :
recomp::default_n64_controller_mappings,
input
)
static EnumOptionVector analog_cam_mode_options = {
{banjo::AnalogCamMode::Off, "Off", "Off"},
{banjo::AnalogCamMode::On, "On", "On"},
};
config.add_enum_option(
banjo::configkeys::general::analog_cam_mode,
"Analog Camera",
// TODO: Update for banjo
"Enables the analog camera.",
analog_cam_mode_options,
banjo::AnalogCamMode::Off
);
config.add_enum_option(
banjo::configkeys::general::analog_camera_invert_mode,
"Analog Camera Inverting",
// TODO: Update for banjo
"Inverts the camera controls for the analog camera if it's enabled. <recomp-color primary>None</recomp-color> is the default.",
camera_invert_mode_options,
banjo::CameraInvertMode::InvertNone
);
config.add_option_disable_dependency(
banjo::configkeys::general::analog_camera_invert_mode,
banjo::configkeys::general::analog_cam_mode,
banjo::AnalogCamMode::Off
);
}
void reset_graphics_options() {
ultramodern::renderer::GraphicsConfig new_config{};
new_config.res_option = res_default;
new_config.wm_option = wm_default();
new_config.hr_option = hr_default;
new_config.ds_option = ds_default;
new_config.ar_option = ar_default;
new_config.msaa_option = msaa_default;
new_config.rr_option = rr_default;
new_config.hpfb_option = hpfb_default;
new_config.rr_manual_value = rr_manual_default;
new_config.developer_mode = developer_mode_default;
ultramodern::renderer::set_graphics_config(new_config);
template <typename T = uint32_t>
T get_general_config_enum_value(const std::string& option_id) {
return static_cast<T>(std::get<uint32_t>(recompui::config::get_general_config().get_option_value(option_id)));
}
bool save_graphics_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
ultramodern::to_json(config_json, ultramodern::renderer::get_graphics_config());
return save_json_with_backups(path, config_json);
banjo::NoteSavingMode banjo::get_note_saving_mode() {
return get_general_config_enum_value<banjo::NoteSavingMode>(banjo::configkeys::general::note_saving_mode);
}
bool load_graphics_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
if (!read_json_with_backups(path, config_json)) {
return false;
}
ultramodern::renderer::GraphicsConfig new_config{};
ultramodern::from_json(config_json, new_config);
ultramodern::renderer::set_graphics_config(new_config);
return true;
banjo::CameraInvertMode banjo::get_camera_invert_mode() {
return get_general_config_enum_value<banjo::CameraInvertMode>(banjo::configkeys::general::camera_invert_mode);
}
void add_input_bindings(nlohmann::json& out, recomp::GameInput input, recomp::InputDevice device) {
const std::string& input_name = recomp::get_input_enum_name(input);
nlohmann::json& out_array = out[input_name];
out_array = nlohmann::json::array();
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) {
out_array[binding_index] = recomp::get_input_binding(input, binding_index, device);
}
};
bool save_controls_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
config_json["keyboard"] = {};
config_json["controller"] = {};
for (size_t i = 0; i < recomp::get_num_inputs(); i++) {
recomp::GameInput cur_input = static_cast<recomp::GameInput>(i);
add_input_bindings(config_json["keyboard"], cur_input, recomp::InputDevice::Keyboard);
add_input_bindings(config_json["controller"], cur_input, recomp::InputDevice::Controller);
}
return save_json_with_backups(path, config_json);
banjo::CameraInvertMode banjo::get_analog_camera_invert_mode() {
return get_general_config_enum_value<banjo::CameraInvertMode>(banjo::configkeys::general::analog_camera_invert_mode);
}
bool load_input_device_from_json(const nlohmann::json& config_json, recomp::InputDevice device, const std::string& key) {
// Check if the json object for the given key exists.
auto find_it = config_json.find(key);
if (find_it == config_json.end()) {
return false;
}
const nlohmann::json& mappings_json = *find_it;
for (size_t i = 0; i < recomp::get_num_inputs(); i++) {
recomp::GameInput cur_input = static_cast<recomp::GameInput>(i);
const std::string& input_name = recomp::get_input_enum_name(cur_input);
// Check if the json object for the given input exists and that it's an array.
auto find_input_it = mappings_json.find(input_name);
if (find_input_it == mappings_json.end() || !find_input_it->is_array()) {
assign_mapping(
device,
cur_input,
recomp::get_default_mapping_for_input(
device == recomp::InputDevice::Keyboard ?
recomp::default_n64_keyboard_mappings :
recomp::default_n64_controller_mappings,
cur_input
)
);
continue;
}
const nlohmann::json& input_json = *find_input_it;
// Deserialize all the bindings from the json array (up to the max number of bindings per input).
for (size_t binding_index = 0; binding_index < std::min(recomp::bindings_per_input, input_json.size()); binding_index++) {
recomp::InputField cur_field{};
recomp::from_json(input_json[binding_index], cur_field);
recomp::set_input_binding(cur_input, binding_index, device, cur_field);
}
}
return true;
banjo::AnalogCamMode banjo::get_analog_cam_mode() {
return get_general_config_enum_value<banjo::AnalogCamMode>(banjo::configkeys::general::analog_cam_mode);
}
bool load_controls_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
if (!read_json_with_backups(path, config_json)) {
return false;
}
if (!load_input_device_from_json(config_json, recomp::InputDevice::Keyboard, "keyboard")) {
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
}
if (!load_input_device_from_json(config_json, recomp::InputDevice::Controller, "controller")) {
assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
}
return true;
static void add_sound_options(recomp::config::Config &config) {
config.add_percent_number_option(
banjo::configkeys::sound::bgm_volume,
"Background Music Volume",
"Controls the overall volume of background music.",
100.0f
);
}
template <typename T = uint32_t>
T get_sound_config_number_value(const std::string& option_id) {
return static_cast<T>(std::get<double>(recompui::config::get_sound_config().get_option_value(option_id)));
}
bool save_sound_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
config_json["main_volume"] = banjo::get_main_volume();
config_json["bgm_volume"] = banjo::get_bgm_volume();
return save_json_with_backups(path, config_json);
int banjo::get_bgm_volume() {
return get_sound_config_number_value<int>(banjo::configkeys::sound::bgm_volume);
}
bool load_sound_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
if (!read_json_with_backups(path, config_json)) {
return false;
}
banjo::reset_sound_settings();
call_if_key_exists(banjo::set_main_volume, config_json, "main_volume");
call_if_key_exists(banjo::set_bgm_volume, config_json, "bgm_volume");
return true;
}
void banjo::load_config() {
detect_steam_deck();
std::filesystem::path recomp_dir = banjo::get_app_folder_path();
std::filesystem::path general_path = recomp_dir / general_filename;
std::filesystem::path graphics_path = recomp_dir / graphics_filename;
std::filesystem::path controls_path = recomp_dir / controls_filename;
std::filesystem::path sound_path = recomp_dir / sound_filename;
void banjo::init_config() {
std::filesystem::path recomp_dir = recompui::file::get_app_folder_path();
if (!recomp_dir.empty()) {
std::filesystem::create_directories(recomp_dir);
}
// TODO error handling for failing to save config files after resetting them.
recompui::config::GeneralTabOptions general_options{};
general_options.has_rumble_strength = true;
general_options.has_gyro_sensitivity = false;
general_options.has_mouse_sensitivity = false;
if (!load_general_config(general_path)) {
// Set the general settings from an empty json to use defaults.
set_general_settings_from_json({});
save_general_config(general_path);
}
auto &general_config = recompui::config::create_general_tab(general_options);
add_general_options(general_config);
if (!load_graphics_config(graphics_path)) {
reset_graphics_options();
save_graphics_config(graphics_path);
}
auto &graphics_config = recompui::config::create_graphics_tab();
if (!load_controls_config(controls_path)) {
banjo::reset_input_bindings();
save_controls_config(controls_path);
}
recompui::config::create_controls_tab();
auto &sound_config = recompui::config::create_sound_tab();
add_sound_options(sound_config);
recompui::config::create_mods_tab();
recompui::config::finalize();
if (!load_sound_config(sound_path)) {
banjo::reset_sound_settings();
save_sound_config(sound_path);
}
}
void banjo::save_config() {
std::filesystem::path recomp_dir = banjo::get_app_folder_path();
if (recomp_dir.empty()) {
return;
}
std::filesystem::create_directories(recomp_dir);
// TODO error handling for failing to save config files.
save_general_config(recomp_dir / general_filename);
save_graphics_config(recomp_dir / graphics_filename);
save_controls_config(recomp_dir / controls_filename);
save_sound_config(recomp_dir / sound_filename);
}
-116
View File
@@ -1,116 +0,0 @@
#include <array>
#include "librecomp/helpers.hpp"
#include "recomp_input.h"
#include "ultramodern/ultramodern.hpp"
// Arrays that hold the mappings for every input for keyboard and controller respectively.
using input_mapping = std::array<recomp::InputField, recomp::bindings_per_input>;
using input_mapping_array = std::array<input_mapping, static_cast<size_t>(recomp::GameInput::COUNT)>;
static input_mapping_array keyboard_input_mappings{};
static input_mapping_array controller_input_mappings{};
// Make the button value array, which maps a button index to its bit field.
#define DEFINE_INPUT(name, value, readable) uint16_t(value##u),
static const std::array n64_button_values = {
DEFINE_N64_BUTTON_INPUTS()
};
#undef DEFINE_INPUT
// Make the input name array.
#define DEFINE_INPUT(name, value, readable) readable,
static const std::vector<std::string> input_names = {
DEFINE_ALL_INPUTS()
};
#undef DEFINE_INPUT
// Make the input enum name array.
#define DEFINE_INPUT(name, value, readable) #name,
static const std::vector<std::string> input_enum_names = {
DEFINE_ALL_INPUTS()
};
#undef DEFINE_INPUT
size_t recomp::get_num_inputs() {
return (size_t)GameInput::COUNT;
}
const std::string& recomp::get_input_name(GameInput input) {
return input_names.at(static_cast<size_t>(input));
}
const std::string& recomp::get_input_enum_name(GameInput input) {
return input_enum_names.at(static_cast<size_t>(input));
}
recomp::GameInput recomp::get_input_from_enum_name(const std::string_view enum_name) {
auto find_it = std::find(input_enum_names.begin(), input_enum_names.end(), enum_name);
if (find_it == input_enum_names.end()) {
return recomp::GameInput::COUNT;
}
return static_cast<recomp::GameInput>(find_it - input_enum_names.begin());
}
// Due to an RmlUi limitation this can't be const. Ideally it would return a const reference or even just a straight up copy.
recomp::InputField& recomp::get_input_binding(GameInput input, size_t binding_index, recomp::InputDevice device) {
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings;
input_mapping& cur_input_mapping = device_mappings.at(static_cast<size_t>(input));
if (binding_index < cur_input_mapping.size()) {
return cur_input_mapping[binding_index];
}
else {
static recomp::InputField dummy_field = {};
return dummy_field;
}
}
void recomp::set_input_binding(recomp::GameInput input, size_t binding_index, recomp::InputDevice device, recomp::InputField value) {
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings;
input_mapping& cur_input_mapping = device_mappings.at(static_cast<size_t>(input));
if (binding_index < cur_input_mapping.size()) {
cur_input_mapping[binding_index] = value;
}
}
bool recomp::get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out) {
uint16_t cur_buttons = 0;
float cur_x = 0.0f;
float cur_y = 0.0f;
if (controller_num != 0) {
return false;
}
if (!recomp::game_input_disabled()) {
for (size_t i = 0; i < n64_button_values.size(); i++) {
size_t input_index = (size_t)GameInput::N64_BUTTON_START + i;
cur_buttons |= recomp::get_input_digital(keyboard_input_mappings[input_index]) ? n64_button_values[i] : 0;
cur_buttons |= recomp::get_input_digital(controller_input_mappings[input_index]) ? n64_button_values[i] : 0;
}
float joystick_deadzone = recomp::get_joystick_deadzone() / 100.0f;
float joystick_x = recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::X_AXIS_POS])
- recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::X_AXIS_NEG]);
float joystick_y = recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::Y_AXIS_POS])
- recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::Y_AXIS_NEG]);
recomp::apply_joystick_deadzone(joystick_x, joystick_y, &joystick_x, &joystick_y);
cur_x = recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::X_AXIS_POS])
- recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::X_AXIS_NEG]) + joystick_x;
cur_y = recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::Y_AXIS_POS])
- recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::Y_AXIS_NEG]) + joystick_y;
}
*buttons_out = cur_buttons;
*x_out = std::clamp(cur_x, -1.0f, 1.0f);
*y_out = std::clamp(cur_y, -1.0f, 1.0f);
return true;
}
-897
View File
@@ -1,897 +0,0 @@
#include <atomic>
#include <mutex>
#include "ultramodern/ultramodern.hpp"
#include "recomp.h"
#include "recomp_input.h"
#include "banjo_config.h"
#include "recomp_ui.h"
#include "SDL.h"
#include "promptfont.h"
#include "GamepadMotion.hpp"
constexpr float axis_threshold = 0.5f;
struct ControllerState {
SDL_GameController* controller;
std::array<float, 3> latest_accelerometer;
GamepadMotion motion;
uint32_t prev_gyro_timestamp;
ControllerState() : controller{}, latest_accelerometer{}, motion{}, prev_gyro_timestamp{} {
motion.Reset();
motion.SetCalibrationMode(GamepadMotionHelpers::CalibrationMode::Stillness | GamepadMotionHelpers::CalibrationMode::SensorFusion);
};
};
static struct {
const Uint8* keys = nullptr;
SDL_Keymod keymod = SDL_Keymod::KMOD_NONE;
int numkeys = 0;
std::atomic_int32_t mouse_wheel_pos = 0;
std::mutex cur_controllers_mutex;
std::vector<SDL_GameController*> cur_controllers{};
std::unordered_map<SDL_JoystickID, ControllerState> controller_states;
std::array<float, 2> rotation_delta{};
std::array<float, 2> mouse_delta{};
std::mutex pending_input_mutex;
std::array<float, 2> pending_rotation_delta{};
std::array<float, 2> pending_mouse_delta{};
float cur_rumble;
bool rumble_active;
} InputState;
static struct {
std::list<std::filesystem::path> files_dropped;
} DropState;
std::atomic<recomp::InputDevice> scanning_device = recomp::InputDevice::COUNT;
std::atomic<recomp::InputField> scanned_input;
enum class InputType {
None = 0, // Using zero for None ensures that default initialized InputFields are unbound.
Keyboard,
Mouse,
ControllerDigital,
ControllerAnalog // Axis input_id values are the SDL value + 1
};
void set_scanned_input(recomp::InputField value) {
scanning_device.store(recomp::InputDevice::COUNT);
scanned_input.store(value);
}
recomp::InputField recomp::get_scanned_input() {
recomp::InputField ret = scanned_input.load();
scanned_input.store({});
return ret;
}
void recomp::start_scanning_input(recomp::InputDevice device) {
scanned_input.store({});
scanning_device.store(device);
}
void recomp::stop_scanning_input() {
scanning_device.store(recomp::InputDevice::COUNT);
}
void queue_if_enabled(SDL_Event* event) {
if (!recomp::all_input_disabled()) {
recompui::queue_event(*event);
}
}
static std::atomic_bool cursor_enabled = true;
void recompui::set_cursor_visible(bool visible) {
cursor_enabled.store(visible);
}
bool should_override_keystate(SDL_Scancode key, SDL_Keymod mod) {
// Override Enter when Alt is held.
if (key == SDL_Scancode::SDL_SCANCODE_RETURN) {
if (mod & SDL_Keymod::KMOD_ALT) {
return true;
}
}
return false;
}
bool sdl_event_filter(void* userdata, SDL_Event* event) {
switch (event->type) {
case SDL_EventType::SDL_KEYDOWN:
{
SDL_KeyboardEvent* keyevent = &event->key;
// Skip repeated events when not in the menu
if (!recompui::is_context_capturing_input() &&
event->key.repeat) {
break;
}
if ((keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_RETURN && (keyevent->keysym.mod & SDL_Keymod::KMOD_ALT)) ||
keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_F11
) {
recompui::toggle_fullscreen();
}
if (scanning_device != recomp::InputDevice::COUNT) {
if (keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE) {
recomp::cancel_scanning_input();
}
else if (scanning_device == recomp::InputDevice::Keyboard) {
set_scanned_input({ (uint32_t)InputType::Keyboard, keyevent->keysym.scancode });
}
}
else {
if (!should_override_keystate(keyevent->keysym.scancode, static_cast<SDL_Keymod>(keyevent->keysym.mod))) {
queue_if_enabled(event);
}
}
}
break;
case SDL_EventType::SDL_CONTROLLERDEVICEADDED:
{
SDL_ControllerDeviceEvent* controller_event = &event->cdevice;
SDL_GameController* controller = SDL_GameControllerOpen(controller_event->which);
printf("Controller added: %d\n", controller_event->which);
if (controller != nullptr) {
printf(" Instance ID: %d\n", SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
ControllerState& state = InputState.controller_states[SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))];
state.controller = controller;
if (SDL_GameControllerHasSensor(controller, SDL_SensorType::SDL_SENSOR_GYRO) && SDL_GameControllerHasSensor(controller, SDL_SensorType::SDL_SENSOR_ACCEL)) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SensorType::SDL_SENSOR_GYRO, SDL_TRUE);
SDL_GameControllerSetSensorEnabled(controller, SDL_SensorType::SDL_SENSOR_ACCEL, SDL_TRUE);
}
}
}
break;
case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED:
{
SDL_ControllerDeviceEvent* controller_event = &event->cdevice;
printf("Controller removed: %d\n", controller_event->which);
InputState.controller_states.erase(controller_event->which);
}
break;
case SDL_EventType::SDL_QUIT: {
if (!ultramodern::is_game_started()) {
ultramodern::quit();
return true;
}
banjo::open_quit_game_prompt();
recompui::activate_mouse();
break;
}
case SDL_EventType::SDL_MOUSEWHEEL:
{
SDL_MouseWheelEvent* wheel_event = &event->wheel;
InputState.mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
}
queue_if_enabled(event);
break;
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN:
if (scanning_device != recomp::InputDevice::COUNT) {
auto menuToggleBinding0 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
auto menuToggleBinding1 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
// note - magic number: 0 is InputType::None
if ((menuToggleBinding0.input_type != 0 && event->cbutton.button == menuToggleBinding0.input_id) ||
(menuToggleBinding1.input_type != 0 && event->cbutton.button == menuToggleBinding1.input_id)) {
recomp::cancel_scanning_input();
}
else if (scanning_device == recomp::InputDevice::Controller) {
SDL_ControllerButtonEvent* button_event = &event->cbutton;
auto scanned_input_index = recomp::get_scanned_input_index();
if ((scanned_input_index == static_cast<int>(recomp::GameInput::TOGGLE_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::ACCEPT_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::APPLY_MENU)) && (
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP ||
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN ||
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
break;
}
set_scanned_input({ (uint32_t)InputType::ControllerDigital, button_event->button });
}
}
else {
queue_if_enabled(event);
}
break;
case SDL_EventType::SDL_CONTROLLERAXISMOTION:
if (scanning_device == recomp::InputDevice::Controller) {
auto scanned_input_index = recomp::get_scanned_input_index();
if (scanned_input_index == static_cast<int>(recomp::GameInput::TOGGLE_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::ACCEPT_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::APPLY_MENU)) {
break;
}
SDL_ControllerAxisEvent* axis_event = &event->caxis;
float axis_value = axis_event->value * (1/32768.0f);
if (axis_value > axis_threshold) {
SDL_Event set_stick_return_event;
set_stick_return_event.type = SDL_USEREVENT;
set_stick_return_event.user.code = axis_event->axis;
set_stick_return_event.user.data1 = nullptr;
set_stick_return_event.user.data2 = nullptr;
recompui::queue_event(set_stick_return_event);
set_scanned_input({ (uint32_t)InputType::ControllerAnalog, axis_event->axis + 1 });
}
else if (axis_value < -axis_threshold) {
SDL_Event set_stick_return_event;
set_stick_return_event.type = SDL_USEREVENT;
set_stick_return_event.user.code = axis_event->axis;
set_stick_return_event.user.data1 = nullptr;
set_stick_return_event.user.data2 = nullptr;
recompui::queue_event(set_stick_return_event);
set_scanned_input({ (uint32_t)InputType::ControllerAnalog, -axis_event->axis - 1 });
}
}
else {
queue_if_enabled(event);
}
break;
case SDL_EventType::SDL_CONTROLLERSENSORUPDATE:
if (event->csensor.sensor == SDL_SensorType::SDL_SENSOR_ACCEL) {
// Convert acceleration to g's.
float x = event->csensor.data[0] / SDL_STANDARD_GRAVITY;
float y = event->csensor.data[1] / SDL_STANDARD_GRAVITY;
float z = event->csensor.data[2] / SDL_STANDARD_GRAVITY;
ControllerState& state = InputState.controller_states[event->csensor.which];
state.latest_accelerometer[0] = x;
state.latest_accelerometer[1] = y;
state.latest_accelerometer[2] = z;
}
else if (event->csensor.sensor == SDL_SensorType::SDL_SENSOR_GYRO) {
// constexpr float gyro_threshold = 0.05f;
// Convert rotational velocity to degrees per second.
constexpr float rad_to_deg = 180.0f / M_PI;
float x = event->csensor.data[0] * rad_to_deg;
float y = event->csensor.data[1] * rad_to_deg;
float z = event->csensor.data[2] * rad_to_deg;
ControllerState& state = InputState.controller_states[event->csensor.which];
uint64_t cur_timestamp = event->csensor.timestamp;
uint32_t delta_ms = cur_timestamp - state.prev_gyro_timestamp;
state.motion.ProcessMotion(x, y, z, state.latest_accelerometer[0], state.latest_accelerometer[1], state.latest_accelerometer[2], delta_ms * 0.001f);
state.prev_gyro_timestamp = cur_timestamp;
float rot_x = 0.0f;
float rot_y = 0.0f;
state.motion.GetPlayerSpaceGyro(rot_x, rot_y);
{
std::lock_guard lock{ InputState.pending_input_mutex };
InputState.pending_rotation_delta[0] += rot_x;
InputState.pending_rotation_delta[1] += rot_y;
}
}
break;
case SDL_EventType::SDL_MOUSEMOTION:
if (!recomp::game_input_disabled()) {
SDL_MouseMotionEvent* motion_event = &event->motion;
std::lock_guard lock{ InputState.pending_input_mutex };
InputState.pending_mouse_delta[0] += motion_event->xrel;
InputState.pending_mouse_delta[1] += motion_event->yrel;
}
queue_if_enabled(event);
break;
case SDL_EventType::SDL_DROPBEGIN:
DropState.files_dropped.clear();
break;
case SDL_EventType::SDL_DROPFILE:
DropState.files_dropped.emplace_back(std::filesystem::path(std::u8string_view((const char8_t*)(event->drop.file))));
SDL_free(event->drop.file);
break;
case SDL_EventType::SDL_DROPCOMPLETE:
recompui::drop_files(DropState.files_dropped);
break;
case SDL_EventType::SDL_CONTROLLERBUTTONUP:
// Always queue button up events to avoid missing them during binding.
recompui::queue_event(*event);
break;
default:
queue_if_enabled(event);
break;
}
return false;
}
void recomp::handle_events() {
SDL_Event cur_event;
static bool started = false;
static bool exited = false;
while (SDL_PollEvent(&cur_event) && !exited) {
exited = sdl_event_filter(nullptr, &cur_event);
// Lock the cursor if all three conditions are true: mouse aiming is enabled, game input is not disabled, and the game has been started.
bool cursor_locked = (recomp::get_mouse_sensitivity() != 0) && !recomp::game_input_disabled() && ultramodern::is_game_started();
// Hide the cursor based on its enable state, but override visibility to false if the cursor is locked.
bool cursor_visible = cursor_enabled;
if (cursor_locked) {
cursor_visible = false;
}
SDL_ShowCursor(cursor_visible ? SDL_ENABLE : SDL_DISABLE);
SDL_SetRelativeMouseMode(cursor_locked ? SDL_TRUE : SDL_FALSE);
}
if (!started && ultramodern::is_game_started()) {
started = true;
recompui::process_game_started();
}
}
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A;
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B;
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X;
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y;
const recomp::DefaultN64Mappings recomp::default_n64_keyboard_mappings = {
.a = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_SPACE}
},
.b = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_LSHIFT}
},
.l = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_E}
},
.r = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_R}
},
.z = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_Q}
},
.start = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_RETURN}
},
.c_left = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_LEFT}
},
.c_right = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_RIGHT}
},
.c_up = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_UP}
},
.c_down = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_DOWN}
},
.dpad_left = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_J}
},
.dpad_right = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_L}
},
.dpad_up = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_I}
},
.dpad_down = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_K}
},
.analog_left = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_A}
},
.analog_right = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_D}
},
.analog_up = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_W}
},
.analog_down = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_S}
},
.toggle_menu = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_ESCAPE}
},
.accept_menu = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_RETURN}
},
.apply_menu = {
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_F}
}
};
const recomp::DefaultN64Mappings recomp::default_n64_controller_mappings = {
.a = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_SOUTH},
},
.b = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_WEST},
},
.l = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
},
.r = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERRIGHT + 1},
},
.z = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERLEFT + 1},
},
.start = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_START},
},
.c_left = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTX + 1)},
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_NORTH},
},
.c_right = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTX + 1},
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_EAST},
},
.c_up = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTY + 1)},
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_RIGHTSTICK},
},
.c_down = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTY + 1},
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
},
.dpad_left = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_LEFT},
},
.dpad_right = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
},
.dpad_up = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_UP},
},
.dpad_down = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_DOWN},
},
.analog_left = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTX + 1)},
},
.analog_right = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTX + 1},
},
.analog_up = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTY + 1)},
},
.analog_down = {
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTY + 1},
},
.toggle_menu = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_BACK},
},
.accept_menu = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_SOUTH},
},
.apply_menu = {
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_WEST},
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_START}
}
};
void recomp::poll_inputs() {
InputState.keys = SDL_GetKeyboardState(&InputState.numkeys);
InputState.keymod = SDL_GetModState();
{
std::lock_guard lock{ InputState.cur_controllers_mutex };
InputState.cur_controllers.clear();
for (const auto& [id, state] : InputState.controller_states) {
(void)id; // Avoid unused variable warning.
SDL_GameController* controller = state.controller;
if (controller != nullptr) {
InputState.cur_controllers.push_back(controller);
}
}
}
// Read the deltas while resetting them to zero.
{
std::lock_guard lock{ InputState.pending_input_mutex };
InputState.rotation_delta = InputState.pending_rotation_delta;
InputState.pending_rotation_delta = { 0.0f, 0.0f };
InputState.mouse_delta = InputState.pending_mouse_delta;
InputState.pending_mouse_delta = { 0.0f, 0.0f };
}
}
void recomp::set_rumble(int controller_num, bool on) {
if (controller_num == 0) {
InputState.rumble_active = on;
}
}
ultramodern::input::connected_device_info_t recomp::get_connected_device_info(int controller_num) {
switch (controller_num) {
case 0:
return ultramodern::input::connected_device_info_t{
.connected_device = ultramodern::input::Device::Controller,
.connected_pak = ultramodern::input::Pak::RumblePak,
};
}
return ultramodern::input::connected_device_info_t{
.connected_device = ultramodern::input::Device::None,
.connected_pak = ultramodern::input::Pak::None,
};
}
static float smoothstep(float from, float to, float amount) {
amount = (amount * amount) * (3.0f - 2.0f * amount);
return std::lerp(from, to, amount);
}
// Update rumble to attempt to mimic the way n64 rumble ramps up and falls off
void recomp::update_rumble() {
// Note: values are not accurate! just approximations based on feel
if (InputState.rumble_active) {
InputState.cur_rumble += 0.17f;
if (InputState.cur_rumble > 1) InputState.cur_rumble = 1;
}
else {
InputState.cur_rumble *= 0.92f;
InputState.cur_rumble -= 0.01f;
if (InputState.cur_rumble < 0) InputState.cur_rumble = 0;
}
float smooth_rumble = smoothstep(0, 1, InputState.cur_rumble);
uint16_t rumble_strength = smooth_rumble * (recomp::get_rumble_strength() * 0xFFFF / 100);
uint32_t duration = 1000000; // Dummy duration value that lasts long enough to matter as the game will reset rumble on its own.
{
std::lock_guard lock{ InputState.cur_controllers_mutex };
for (const auto& controller : InputState.cur_controllers) {
SDL_GameControllerRumble(controller, 0, rumble_strength, duration);
}
}
}
bool controller_button_state(int32_t input_id) {
if (input_id >= 0 && input_id < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX) {
SDL_GameControllerButton button = (SDL_GameControllerButton)input_id;
bool ret = false;
{
std::lock_guard lock{ InputState.cur_controllers_mutex };
for (const auto& controller : InputState.cur_controllers) {
ret |= SDL_GameControllerGetButton(controller, button);
}
}
return ret;
}
return false;
}
static std::atomic_bool right_analog_suppressed = false;
float controller_axis_state(int32_t input_id, bool allow_suppression) {
if (abs(input_id) - 1 < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX) {
SDL_GameControllerAxis axis = (SDL_GameControllerAxis)(abs(input_id) - 1);
bool negative_range = input_id < 0;
float ret = 0.0f;
{
std::lock_guard lock{ InputState.cur_controllers_mutex };
for (const auto& controller : InputState.cur_controllers) {
float cur_val = SDL_GameControllerGetAxis(controller, axis) * (1/32768.0f);
if (negative_range) {
cur_val = -cur_val;
}
// Check if this input is a right analog axis and suppress it accordingly.
if (allow_suppression && right_analog_suppressed.load() &&
(axis == SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX || axis == SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY)) {
cur_val = 0;
}
ret += std::clamp(cur_val, 0.0f, 1.0f);
}
}
return std::clamp(ret, 0.0f, 1.0f);
}
return false;
}
float recomp::get_input_analog(const recomp::InputField& field) {
switch ((InputType)field.input_type) {
case InputType::Keyboard:
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
if (should_override_keystate(static_cast<SDL_Scancode>(field.input_id), InputState.keymod)) {
return 0.0f;
}
return InputState.keys[field.input_id] ? 1.0f : 0.0f;
}
return 0.0f;
case InputType::ControllerDigital:
return controller_button_state(field.input_id) ? 1.0f : 0.0f;
case InputType::ControllerAnalog:
return controller_axis_state(field.input_id, true);
case InputType::Mouse:
// TODO mouse support
return 0.0f;
case InputType::None:
return false;
}
}
float recomp::get_input_analog(const std::span<const recomp::InputField> fields) {
float ret = 0.0f;
for (const auto& field : fields) {
ret += get_input_analog(field);
}
return std::clamp(ret, 0.0f, 1.0f);
}
bool recomp::get_input_digital(const recomp::InputField& field) {
switch ((InputType)field.input_type) {
case InputType::Keyboard:
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
if (should_override_keystate(static_cast<SDL_Scancode>(field.input_id), InputState.keymod)) {
return false;
}
return InputState.keys[field.input_id] != 0;
}
return false;
case InputType::ControllerDigital:
return controller_button_state(field.input_id);
case InputType::ControllerAnalog:
// TODO adjustable threshold
return controller_axis_state(field.input_id, true) >= axis_threshold;
case InputType::Mouse:
// TODO mouse support
return false;
case InputType::None:
return false;
}
}
bool recomp::get_input_digital(const std::span<const recomp::InputField> fields) {
bool ret = 0;
for (const auto& field : fields) {
ret |= get_input_digital(field);
}
return ret;
}
void recomp::get_gyro_deltas(float* x, float* y) {
std::array<float, 2> cur_rotation_delta = InputState.rotation_delta;
float sensitivity = (float)recomp::get_gyro_sensitivity() / 100.0f;
*x = cur_rotation_delta[0] * sensitivity;
*y = cur_rotation_delta[1] * sensitivity;
}
void recomp::get_mouse_deltas(float* x, float* y) {
std::array<float, 2> cur_mouse_delta = InputState.mouse_delta;
float sensitivity = (float)recomp::get_mouse_sensitivity() / 100.0f;
*x = cur_mouse_delta[0] * sensitivity;
*y = cur_mouse_delta[1] * sensitivity;
}
void recomp::apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out) {
float joystick_deadzone = (float)recomp::get_joystick_deadzone() / 100.0f;
if (fabsf(x_in) < joystick_deadzone) {
x_in = 0.0f;
}
else {
if (x_in > 0.0f) {
x_in -= joystick_deadzone;
}
else {
x_in += joystick_deadzone;
}
x_in /= (1.0f - joystick_deadzone);
}
if (fabsf(y_in) < joystick_deadzone) {
y_in = 0.0f;
}
else {
if (y_in > 0.0f) {
y_in -= joystick_deadzone;
}
else {
y_in += joystick_deadzone;
}
y_in /= (1.0f - joystick_deadzone);
}
*x_out = x_in;
*y_out = y_in;
}
void recomp::get_right_analog(float* x, float* y) {
float x_val =
controller_axis_state((SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false) -
controller_axis_state(-(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false);
float y_val =
controller_axis_state((SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false) -
controller_axis_state(-(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false);
recomp::apply_joystick_deadzone(x_val, y_val, x, y);
}
void recomp::set_right_analog_suppressed(bool suppressed) {
right_analog_suppressed.store(suppressed);
}
bool recomp::game_input_disabled() {
// Disable input if any menu that blocks input is open.
return recompui::is_context_capturing_input();
}
bool recomp::all_input_disabled() {
// Disable all input if an input is being polled.
return scanning_device != recomp::InputDevice::COUNT;
}
std::string controller_button_to_string(SDL_GameControllerButton button) {
switch (button) {
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A:
return PF_GAMEPAD_A;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B:
return PF_GAMEPAD_B;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X:
return PF_GAMEPAD_X;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y:
return PF_GAMEPAD_Y;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK:
return PF_XBOX_VIEW;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_GUIDE:
return PF_GAMEPAD_HOME;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START:
return PF_XBOX_MENU;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK:
return PF_ANALOG_L_CLICK;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK:
return PF_ANALOG_R_CLICK;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
return PF_XBOX_LEFT_SHOULDER;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
return PF_XBOX_RIGHT_SHOULDER;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP:
return PF_DPAD_UP;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN:
return PF_DPAD_DOWN;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT:
return PF_DPAD_LEFT;
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
return PF_DPAD_RIGHT;
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MISC1:
// return "";
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE1:
// return "";
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE2:
// return "";
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE3:
// return "";
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE4:
// return "";
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_TOUCHPAD:
return PF_SONY_TOUCHPAD;
default:
return "Button " + std::to_string(button);
}
}
std::unordered_map<SDL_Scancode, std::string> scancode_codepoints{
{SDL_SCANCODE_LEFT, PF_KEYBOARD_LEFT},
// NOTE: UP and RIGHT are swapped with promptfont.
{SDL_SCANCODE_UP, PF_KEYBOARD_RIGHT},
{SDL_SCANCODE_RIGHT, PF_KEYBOARD_UP},
{SDL_SCANCODE_DOWN, PF_KEYBOARD_DOWN},
{SDL_SCANCODE_A, PF_KEYBOARD_A},
{SDL_SCANCODE_B, PF_KEYBOARD_B},
{SDL_SCANCODE_C, PF_KEYBOARD_C},
{SDL_SCANCODE_D, PF_KEYBOARD_D},
{SDL_SCANCODE_E, PF_KEYBOARD_E},
{SDL_SCANCODE_F, PF_KEYBOARD_F},
{SDL_SCANCODE_G, PF_KEYBOARD_G},
{SDL_SCANCODE_H, PF_KEYBOARD_H},
{SDL_SCANCODE_I, PF_KEYBOARD_I},
{SDL_SCANCODE_J, PF_KEYBOARD_J},
{SDL_SCANCODE_K, PF_KEYBOARD_K},
{SDL_SCANCODE_L, PF_KEYBOARD_L},
{SDL_SCANCODE_M, PF_KEYBOARD_M},
{SDL_SCANCODE_N, PF_KEYBOARD_N},
{SDL_SCANCODE_O, PF_KEYBOARD_O},
{SDL_SCANCODE_P, PF_KEYBOARD_P},
{SDL_SCANCODE_Q, PF_KEYBOARD_Q},
{SDL_SCANCODE_R, PF_KEYBOARD_R},
{SDL_SCANCODE_S, PF_KEYBOARD_S},
{SDL_SCANCODE_T, PF_KEYBOARD_T},
{SDL_SCANCODE_U, PF_KEYBOARD_U},
{SDL_SCANCODE_V, PF_KEYBOARD_V},
{SDL_SCANCODE_W, PF_KEYBOARD_W},
{SDL_SCANCODE_X, PF_KEYBOARD_X},
{SDL_SCANCODE_Y, PF_KEYBOARD_Y},
{SDL_SCANCODE_Z, PF_KEYBOARD_Z},
{SDL_SCANCODE_0, PF_KEYBOARD_0},
{SDL_SCANCODE_1, PF_KEYBOARD_1},
{SDL_SCANCODE_2, PF_KEYBOARD_2},
{SDL_SCANCODE_3, PF_KEYBOARD_3},
{SDL_SCANCODE_4, PF_KEYBOARD_4},
{SDL_SCANCODE_5, PF_KEYBOARD_5},
{SDL_SCANCODE_6, PF_KEYBOARD_6},
{SDL_SCANCODE_7, PF_KEYBOARD_7},
{SDL_SCANCODE_8, PF_KEYBOARD_8},
{SDL_SCANCODE_9, PF_KEYBOARD_9},
{SDL_SCANCODE_ESCAPE, PF_KEYBOARD_ESCAPE},
{SDL_SCANCODE_F1, PF_KEYBOARD_F1},
{SDL_SCANCODE_F2, PF_KEYBOARD_F2},
{SDL_SCANCODE_F3, PF_KEYBOARD_F3},
{SDL_SCANCODE_F4, PF_KEYBOARD_F4},
{SDL_SCANCODE_F5, PF_KEYBOARD_F5},
{SDL_SCANCODE_F6, PF_KEYBOARD_F6},
{SDL_SCANCODE_F7, PF_KEYBOARD_F7},
{SDL_SCANCODE_F8, PF_KEYBOARD_F8},
{SDL_SCANCODE_F9, PF_KEYBOARD_F9},
{SDL_SCANCODE_F10, PF_KEYBOARD_F10},
{SDL_SCANCODE_F11, PF_KEYBOARD_F11},
{SDL_SCANCODE_F12, PF_KEYBOARD_F12},
{SDL_SCANCODE_PRINTSCREEN, PF_KEYBOARD_PRINT_SCREEN},
{SDL_SCANCODE_SCROLLLOCK, PF_KEYBOARD_SCROLL_LOCK},
{SDL_SCANCODE_PAUSE, PF_KEYBOARD_PAUSE},
{SDL_SCANCODE_INSERT, PF_KEYBOARD_INSERT},
{SDL_SCANCODE_HOME, PF_KEYBOARD_HOME},
{SDL_SCANCODE_PAGEUP, PF_KEYBOARD_PAGE_UP},
{SDL_SCANCODE_DELETE, PF_KEYBOARD_DELETE},
{SDL_SCANCODE_END, PF_KEYBOARD_END},
{SDL_SCANCODE_PAGEDOWN, PF_KEYBOARD_PAGE_DOWN},
{SDL_SCANCODE_SPACE, PF_KEYBOARD_SPACE},
{SDL_SCANCODE_BACKSPACE, PF_KEYBOARD_BACKSPACE},
{SDL_SCANCODE_TAB, PF_KEYBOARD_TAB},
{SDL_SCANCODE_RETURN, PF_KEYBOARD_ENTER},
{SDL_SCANCODE_CAPSLOCK, PF_KEYBOARD_CAPS},
{SDL_SCANCODE_NUMLOCKCLEAR, PF_KEYBOARD_NUM_LOCK},
{SDL_SCANCODE_LSHIFT, "L" PF_KEYBOARD_SHIFT},
{SDL_SCANCODE_RSHIFT, "R" PF_KEYBOARD_SHIFT},
};
std::string keyboard_input_to_string(SDL_Scancode key) {
if (scancode_codepoints.find(key) != scancode_codepoints.end()) {
return scancode_codepoints[key];
}
return std::to_string(key);
}
std::string controller_axis_to_string(int axis) {
bool positive = axis > 0;
SDL_GameControllerAxis actual_axis = SDL_GameControllerAxis(abs(axis) - 1);
switch (actual_axis) {
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX:
return positive ? "\u21C0" : "\u21BC";
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY:
return positive ? "\u21C2" : "\u21BE";
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX:
return positive ? "\u21C1" : "\u21BD";
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY:
return positive ? "\u21C3" : "\u21BF";
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT:
return positive ? "\u2196" : "\u21DC";
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
return positive ? "\u2197" : "\u21DD";
default:
return "Axis " + std::to_string(actual_axis) + (positive ? '+' : '-');
}
}
std::string recomp::InputField::to_string() const {
switch ((InputType)input_type) {
case InputType::None:
return "";
case InputType::ControllerDigital:
return controller_button_to_string((SDL_GameControllerButton)input_id);
case InputType::ControllerAnalog:
return controller_axis_to_string(input_id);
case InputType::Keyboard:
return keyboard_input_to_string((SDL_Scancode)input_id);
default:
return std::to_string(input_type) + "," + std::to_string(input_id);
}
}
+11 -8
View File
@@ -4,8 +4,9 @@
#include "librecomp/overlays.hpp"
#include "librecomp/addresses.hpp"
#include "banjo_config.h"
#include "recomp_input.h"
#include "recomp_ui.h"
#include "recompinput/recompinput.h"
#include "recompui/recompui.h"
#include "recompui/renderer.h"
#include "banjo_render.h"
#include "banjo_sound.h"
#include "librecomp/helpers.hpp"
@@ -17,7 +18,7 @@
#include "../lib/N64ModernRuntime/thirdparty/xxHash/xxh3.h"
extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) {
recomp::poll_inputs();
recompinput::poll_inputs();
}
extern "C" void recomp_puts(uint8_t* rdram, recomp_context* ctx) {
@@ -50,14 +51,15 @@ extern "C" void recomp_get_gyro_deltas(uint8_t* rdram, recomp_context* ctx) {
float* x_out = _arg<0, float*>(rdram, ctx);
float* y_out = _arg<1, float*>(rdram, ctx);
recomp::get_gyro_deltas(x_out, y_out);
// TODO: use controller number
recompinput::get_gyro_deltas(0, x_out, y_out);
}
extern "C" void recomp_get_mouse_deltas(uint8_t* rdram, recomp_context* ctx) {
float* x_out = _arg<0, float*>(rdram, ctx);
float* y_out = _arg<1, float*>(rdram, ctx);
recomp::get_mouse_deltas(x_out, y_out);
recompinput::get_mouse_deltas(x_out, y_out);
}
extern "C" void recomp_powf(uint8_t* rdram, recomp_context* ctx) {
@@ -123,7 +125,7 @@ extern "C" void recomp_load_overlays(uint8_t * rdram, recomp_context * ctx) {
}
extern "C" void recomp_high_precision_fb_enabled(uint8_t * rdram, recomp_context * ctx) {
_return(ctx, static_cast<s32>(banjo::renderer::RT64HighPrecisionFBEnabled()));
_return(ctx, static_cast<s32>(recompui::renderer::RT64HighPrecisionFBEnabled()));
}
extern "C" void recomp_get_resolution_scale(uint8_t* rdram, recomp_context* ctx) {
@@ -167,7 +169,8 @@ extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
float x, y;
recomp::get_right_analog(&x, &y);
// TODO: use controller number
recompinput::get_right_analog(0, &x, &y);
float magnitude = sqrtf(x * x + y * y);
@@ -187,7 +190,7 @@ extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
extern "C" void recomp_set_right_analog_suppressed(uint8_t* rdram, recomp_context* ctx) {
s32 suppressed = _arg<0, s32>(rdram, ctx);
recomp::set_right_analog_suppressed(suppressed);
recompinput::set_right_analog_suppressed(suppressed);
}
constexpr uint32_t k1_to_phys(uint32_t addr) {
+1 -1
View File
@@ -5,7 +5,7 @@
#include "slot_map.h"
#include "recomp_data.h"
#include "recomp_ui.h"
#include "recompui/recompui.h"
#include "librecomp/helpers.hpp"
#include "librecomp/overlays.hpp"
#include "librecomp/addresses.hpp"
+1 -1
View File
@@ -7,7 +7,7 @@
#include "librecomp/helpers.hpp"
#include "librecomp/addresses.hpp"
#include "ultramodern/error_handling.hpp"
#include "recomp_ui.h"
#include "recompui/recompui.h"
#include "recomp_data.h"
#include "../patches/object_extension_funcs.h"
+53 -19
View File
@@ -27,8 +27,14 @@
#undef Always
#endif
#include "recomp_ui.h"
#include "recomp_input.h"
#include "recompui/recompui.h"
#include "recompui/program_config.h"
#include "recompui/renderer.h"
#include "recompui/config.h"
#include "util/file.h"
#include "recompinput/input_events.h"
#include "recompinput/recompinput.h"
#include "recompinput/profiles.h"
#include "banjo_config.h"
#include "banjo_sound.h"
#include "banjo_render.h"
@@ -174,7 +180,7 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::
}
void update_gfx(void*) {
recomp::handle_events();
recompinput::handle_events();
}
static SDL_AudioCVT audio_convert;
@@ -216,7 +222,7 @@ void queue_samples(int16_t* audio_data, size_t sample_count) {
// Convert the audio from 16-bit values to floats and swap the audio channels into the
// swap buffer to correct for the address xor caused by endianness handling.
float cur_main_volume = banjo::get_main_volume() / 100.0f; // Get the current main volume, normalized to 0.0-1.0.
float cur_main_volume = static_cast<float>(recompui::config::sound::get_main_volume()) / 100.0f; // Get the current main volume, normalized to 0.0-1.0.
for (size_t i = 0; i < sample_count; i += input_channels) {
swap_buffer[i + 0 + duplicated_input_frames * input_channels] = audio_data[i + 1] * (0.5f / 32768.0f) * cur_main_volume;
swap_buffer[i + 1 + duplicated_input_frames * input_channels] = audio_data[i + 0] * (0.5f / 32768.0f) * cur_main_volume;
@@ -520,15 +526,34 @@ void release_preload(PreloadContext& context) {
#endif
void enable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
banjo::renderer::enable_texture_pack(context, mod);
recompui::renderer::enable_texture_pack(context, mod);
}
void disable_texture_pack(recomp::mods::ModContext&, const recomp::mods::ModHandle& mod) {
banjo::renderer::disable_texture_pack(mod);
recompui::renderer::disable_texture_pack(mod);
}
void reorder_texture_pack(recomp::mods::ModContext&) {
banjo::renderer::trigger_texture_pack_update();
recompui::renderer::trigger_texture_pack_update();
}
void on_launcher_init(recompui::LauncherMenu *menu) {
auto game_options_menu = menu->init_game_options_menu(
supported_games[0].game_id,
supported_games[0].mod_game_id,
recompui::GameOptionsMenuLayout::Center
);
game_options_menu->add_default_options();
// TODO: Style launcher and get better background.
auto bg_element = menu->set_launcher_background_svg("banjkazoobg.svg");
bg_element->set_top(0.0f);
bg_element->set_bottom(0.0f);
bg_element->set_left(50.0f, recompui::Unit::Percent);
bg_element->set_height(1080.0f, recompui::Unit::Dp);
bg_element->set_width(1920.0f, recompui::Unit::Dp);
bg_element->set_translate_2D(-50.0f, 0.0f, recompui::Unit::Percent);
bg_element->set_opacity(0.25f);
}
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
@@ -608,12 +633,15 @@ int main(int argc, char** argv) {
reset_audio(48000);
// Source controller mappings file
std::u8string controller_db_path = (banjo::get_program_path() / "recompcontrollerdb.txt").u8string();
std::u8string controller_db_path = (recompui::file::get_program_path() / "recompcontrollerdb.txt").u8string();
if (SDL_GameControllerAddMappingsFromFile(reinterpret_cast<const char *>(controller_db_path.c_str())) < 0) {
fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError());
}
recomp::register_config_path(banjo::get_app_folder_path());
recompui::programconfig::set_program_name(banjo::program_name);
recompui::programconfig::set_program_id(banjo::program_id);
recompui::register_primary_font("Suplexmentary Comic NC.ttf", "Suplexmentary Comic NC");
recomp::register_config_path(recompui::file::get_app_folder_path());
// Register supported games and patches
for (const auto& game : supported_games) {
@@ -627,8 +655,8 @@ int main(int argc, char** argv) {
REGISTER_FUNC(recomp_get_analog_cam_enabled);
REGISTER_FUNC(recomp_get_camera_inputs);
REGISTER_FUNC(recomp_get_bgm_volume);
REGISTER_FUNC(recomp_get_gyro_deltas);
REGISTER_FUNC(recomp_get_mouse_deltas);
// REGISTER_FUNC(recomp_get_gyro_deltas);
// REGISTER_FUNC(recomp_get_mouse_deltas);
REGISTER_FUNC(recomp_get_inverted_axes);
REGISTER_FUNC(recomp_get_analog_inverted_axes);
recompui::register_ui_exports();
@@ -637,16 +665,22 @@ int main(int argc, char** argv) {
banjo::register_bk_overlays();
banjo::register_bk_patches();
// Register extensions for two types: Props and ActorMarkers.
recomputil::init_extended_object_data(2);
banjo::load_config();
recompinput::players::set_single_player_mode(true);
banjo::init_config();
recompui::register_launcher_init_callback(on_launcher_init);
recomp::rsp::callbacks_t rsp_callbacks{
.get_rsp_microcode = get_rsp_microcode,
};
ultramodern::renderer::callbacks_t renderer_callbacks{
.create_render_context = banjo::renderer::create_render_context,
.create_render_context = recompui::renderer::create_render_context,
};
ultramodern::gfx_callbacks_t gfx_callbacks{
@@ -662,15 +696,15 @@ int main(int argc, char** argv) {
};
ultramodern::input::callbacks_t input_callbacks{
.poll_input = recomp::poll_inputs,
.get_input = recomp::get_n64_input,
.set_rumble = recomp::set_rumble,
.get_connected_device_info = recomp::get_connected_device_info,
.poll_input = recompinput::poll_inputs,
.get_input = recompinput::profiles::get_n64_input,
.set_rumble = recompinput::set_rumble,
.get_connected_device_info = recompinput::get_connected_device_info,
};
ultramodern::events::callbacks_t thread_callbacks{
.vi_callback = recomp::update_rumble,
.gfx_init_callback = recompui::update_supported_options,
.vi_callback = recompinput::update_rumble,
.gfx_init_callback = nullptr,
};
ultramodern::error_handling::callbacks_t error_handling_callbacks{
-535
View File
@@ -1,535 +0,0 @@
#include <memory>
#include <cstring>
#include <variant>
#include <algorithm>
#define HLSL_CPU
#include "hle/rt64_application.h"
#include "rt64_render_hooks.h"
#include "overloaded.h"
#include "ultramodern/ultramodern.hpp"
#include "ultramodern/config.hpp"
#include "banjo_render.h"
#include "recomp_ui.h"
#include "concurrentqueue.h"
static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
static bool sample_positions_supported = false;
static bool high_precision_fb_enabled = false;
static uint8_t DMEM[0x1000];
static uint8_t IMEM[0x1000];
struct TexturePackEnableAction {
std::string mod_id;
};
struct TexturePackDisableAction {
std::string mod_id;
};
struct TexturePackSecondaryEnableAction {
std::string mod_id;
};
struct TexturePackSecondaryDisableAction {
std::string mod_id;
};
struct TexturePackUpdateAction {
};
using TexturePackAction = std::variant<TexturePackEnableAction, TexturePackDisableAction, TexturePackSecondaryEnableAction, TexturePackSecondaryDisableAction, TexturePackUpdateAction>;
static moodycamel::ConcurrentQueue<TexturePackAction> texture_pack_action_queue;
unsigned int MI_INTR_REG = 0;
unsigned int DPC_START_REG = 0;
unsigned int DPC_END_REG = 0;
unsigned int DPC_CURRENT_REG = 0;
unsigned int DPC_STATUS_REG = 0;
unsigned int DPC_CLOCK_REG = 0;
unsigned int DPC_BUFBUSY_REG = 0;
unsigned int DPC_PIPEBUSY_REG = 0;
unsigned int DPC_TMEM_REG = 0;
void dummy_check_interrupts() {}
RT64::UserConfiguration::Antialiasing compute_max_supported_aa(plume::RenderSampleCounts bits) {
if (bits & plume::RenderSampleCount::Bits::COUNT_2) {
if (bits & plume::RenderSampleCount::Bits::COUNT_4) {
if (bits & plume::RenderSampleCount::Bits::COUNT_8) {
return RT64::UserConfiguration::Antialiasing::MSAA8X;
}
return RT64::UserConfiguration::Antialiasing::MSAA4X;
}
return RT64::UserConfiguration::Antialiasing::MSAA2X;
};
return RT64::UserConfiguration::Antialiasing::None;
}
RT64::UserConfiguration::AspectRatio to_rt64(ultramodern::renderer::AspectRatio option) {
switch (option) {
case ultramodern::renderer::AspectRatio::Original:
return RT64::UserConfiguration::AspectRatio::Original;
case ultramodern::renderer::AspectRatio::Expand:
return RT64::UserConfiguration::AspectRatio::Expand;
case ultramodern::renderer::AspectRatio::Manual:
return RT64::UserConfiguration::AspectRatio::Manual;
case ultramodern::renderer::AspectRatio::OptionCount:
return RT64::UserConfiguration::AspectRatio::OptionCount;
}
}
RT64::UserConfiguration::Antialiasing to_rt64(ultramodern::renderer::Antialiasing option) {
switch (option) {
case ultramodern::renderer::Antialiasing::None:
return RT64::UserConfiguration::Antialiasing::None;
case ultramodern::renderer::Antialiasing::MSAA2X:
return RT64::UserConfiguration::Antialiasing::MSAA2X;
case ultramodern::renderer::Antialiasing::MSAA4X:
return RT64::UserConfiguration::Antialiasing::MSAA4X;
case ultramodern::renderer::Antialiasing::MSAA8X:
return RT64::UserConfiguration::Antialiasing::MSAA8X;
case ultramodern::renderer::Antialiasing::OptionCount:
return RT64::UserConfiguration::Antialiasing::OptionCount;
}
}
RT64::UserConfiguration::RefreshRate to_rt64(ultramodern::renderer::RefreshRate option) {
switch (option) {
case ultramodern::renderer::RefreshRate::Original:
return RT64::UserConfiguration::RefreshRate::Original;
case ultramodern::renderer::RefreshRate::Display:
return RT64::UserConfiguration::RefreshRate::Display;
case ultramodern::renderer::RefreshRate::Manual:
return RT64::UserConfiguration::RefreshRate::Manual;
case ultramodern::renderer::RefreshRate::OptionCount:
return RT64::UserConfiguration::RefreshRate::OptionCount;
}
}
RT64::UserConfiguration::InternalColorFormat to_rt64(ultramodern::renderer::HighPrecisionFramebuffer option) {
switch (option) {
case ultramodern::renderer::HighPrecisionFramebuffer::Off:
return RT64::UserConfiguration::InternalColorFormat::Standard;
case ultramodern::renderer::HighPrecisionFramebuffer::On:
return RT64::UserConfiguration::InternalColorFormat::High;
case ultramodern::renderer::HighPrecisionFramebuffer::Auto:
return RT64::UserConfiguration::InternalColorFormat::Automatic;
case ultramodern::renderer::HighPrecisionFramebuffer::OptionCount:
return RT64::UserConfiguration::InternalColorFormat::OptionCount;
}
}
void set_application_user_config(RT64::Application* application, const ultramodern::renderer::GraphicsConfig& config) {
switch (config.res_option) {
default:
case ultramodern::renderer::Resolution::Auto:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::WindowIntegerScale;
application->userConfig.downsampleMultiplier = 1;
break;
case ultramodern::renderer::Resolution::Original:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
application->userConfig.resolutionMultiplier = std::max(config.ds_option, 1);
application->userConfig.downsampleMultiplier = std::max(config.ds_option, 1);
break;
case ultramodern::renderer::Resolution::Original2x:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
application->userConfig.resolutionMultiplier = 2.0 * std::max(config.ds_option, 1);
application->userConfig.downsampleMultiplier = std::max(config.ds_option, 1);
break;
}
switch (config.hr_option) {
default:
case ultramodern::renderer::HUDRatioMode::Original:
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Original;
break;
case ultramodern::renderer::HUDRatioMode::Clamp16x9:
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Manual;
application->userConfig.extAspectTarget = 16.0/9.0;
break;
case ultramodern::renderer::HUDRatioMode::Full:
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Expand;
break;
}
application->userConfig.aspectRatio = to_rt64(config.ar_option);
application->userConfig.antialiasing = to_rt64(config.msaa_option);
application->userConfig.refreshRate = to_rt64(config.rr_option);
application->userConfig.refreshRateTarget = config.rr_manual_value;
application->userConfig.internalColorFormat = to_rt64(config.hpfb_option);
application->userConfig.displayBuffering = RT64::UserConfiguration::DisplayBuffering::Triple;
}
ultramodern::renderer::SetupResult map_setup_result(RT64::Application::SetupResult rt64_result) {
switch (rt64_result) {
case RT64::Application::SetupResult::Success:
return ultramodern::renderer::SetupResult::Success;
case RT64::Application::SetupResult::DynamicLibrariesNotFound:
return ultramodern::renderer::SetupResult::DynamicLibrariesNotFound;
case RT64::Application::SetupResult::InvalidGraphicsAPI:
return ultramodern::renderer::SetupResult::InvalidGraphicsAPI;
case RT64::Application::SetupResult::GraphicsAPINotFound:
return ultramodern::renderer::SetupResult::GraphicsAPINotFound;
case RT64::Application::SetupResult::GraphicsDeviceNotFound:
return ultramodern::renderer::SetupResult::GraphicsDeviceNotFound;
}
fprintf(stderr, "Unhandled `RT64::Application::SetupResult` ?\n");
assert(false);
std::exit(EXIT_FAILURE);
}
ultramodern::renderer::GraphicsApi map_graphics_api(RT64::UserConfiguration::GraphicsAPI api) {
switch (api) {
case RT64::UserConfiguration::GraphicsAPI::D3D12:
return ultramodern::renderer::GraphicsApi::D3D12;
case RT64::UserConfiguration::GraphicsAPI::Vulkan:
return ultramodern::renderer::GraphicsApi::Vulkan;
case RT64::UserConfiguration::GraphicsAPI::Metal:
return ultramodern::renderer::GraphicsApi::Metal;
case RT64::UserConfiguration::GraphicsAPI::Automatic:
return ultramodern::renderer::GraphicsApi::Auto;
}
fprintf(stderr, "Unhandled `RT64::UserConfiguration::GraphicsAPI` ?\n");
assert(false);
std::exit(EXIT_FAILURE);
}
banjo::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool debug) {
static unsigned char dummy_rom_header[0x40];
recompui::set_render_hooks();
// Set up the RT64 application core fields.
RT64::Application::Core appCore{};
#if defined(_WIN32)
appCore.window = window_handle.window;
#elif defined(__linux__) || defined(__ANDROID__)
appCore.window = window_handle;
#elif defined(__APPLE__)
appCore.window.window = window_handle.window;
appCore.window.view = window_handle.view;
#endif
appCore.checkInterrupts = dummy_check_interrupts;
appCore.HEADER = dummy_rom_header;
appCore.RDRAM = rdram;
appCore.DMEM = DMEM;
appCore.IMEM = IMEM;
appCore.MI_INTR_REG = &MI_INTR_REG;
appCore.DPC_START_REG = &DPC_START_REG;
appCore.DPC_END_REG = &DPC_END_REG;
appCore.DPC_CURRENT_REG = &DPC_CURRENT_REG;
appCore.DPC_STATUS_REG = &DPC_STATUS_REG;
appCore.DPC_CLOCK_REG = &DPC_CLOCK_REG;
appCore.DPC_BUFBUSY_REG = &DPC_BUFBUSY_REG;
appCore.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG;
appCore.DPC_TMEM_REG = &DPC_TMEM_REG;
ultramodern::renderer::ViRegs* vi_regs = ultramodern::renderer::get_vi_regs();
appCore.VI_STATUS_REG = &vi_regs->VI_STATUS_REG;
appCore.VI_ORIGIN_REG = &vi_regs->VI_ORIGIN_REG;
appCore.VI_WIDTH_REG = &vi_regs->VI_WIDTH_REG;
appCore.VI_INTR_REG = &vi_regs->VI_INTR_REG;
appCore.VI_V_CURRENT_LINE_REG = &vi_regs->VI_V_CURRENT_LINE_REG;
appCore.VI_TIMING_REG = &vi_regs->VI_TIMING_REG;
appCore.VI_V_SYNC_REG = &vi_regs->VI_V_SYNC_REG;
appCore.VI_H_SYNC_REG = &vi_regs->VI_H_SYNC_REG;
appCore.VI_LEAP_REG = &vi_regs->VI_LEAP_REG;
appCore.VI_H_START_REG = &vi_regs->VI_H_START_REG;
appCore.VI_V_START_REG = &vi_regs->VI_V_START_REG;
appCore.VI_V_BURST_REG = &vi_regs->VI_V_BURST_REG;
appCore.VI_X_SCALE_REG = &vi_regs->VI_X_SCALE_REG;
appCore.VI_Y_SCALE_REG = &vi_regs->VI_Y_SCALE_REG;
// Set up the RT64 application configuration fields.
RT64::ApplicationConfiguration appConfig;
appConfig.useConfigurationFile = false;
// Create the RT64 application.
app = std::make_unique<RT64::Application>(appCore, appConfig);
// Set initial user config settings based on the current settings.
auto& cur_config = ultramodern::renderer::get_graphics_config();
set_application_user_config(app.get(), cur_config);
app->userConfig.developerMode = debug;
// Force gbi depth branches to prevent LODs from kicking in.
app->enhancementConfig.f3dex.forceBranch = true;
// Scale LODs based on the output resolution.
app->enhancementConfig.textureLOD.scale = true;
// Pick an API if the user has set an override.
switch (cur_config.api_option) {
case ultramodern::renderer::GraphicsApi::D3D12:
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::D3D12;
break;
case ultramodern::renderer::GraphicsApi::Vulkan:
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Vulkan;
break;
case ultramodern::renderer::GraphicsApi::Metal:
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Metal;
break;
case ultramodern::renderer::GraphicsApi::Auto:
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Automatic;
break;
}
// Set up the RT64 application.
uint32_t thread_id = 0;
#ifdef _WIN32
thread_id = window_handle.thread_id;
#endif
setup_result = map_setup_result(app->setup(thread_id));
// Get the API that RT64 chose.
chosen_api = map_graphics_api(app->chosenGraphicsAPI);
if (setup_result != ultramodern::renderer::SetupResult::Success) {
app = nullptr;
return;
}
// Set the application's fullscreen state.
app->setFullScreen(cur_config.wm_option == ultramodern::renderer::WindowMode::Fullscreen);
// Check if the selected device actually supports MSAA sample positions and MSAA for for the formats that will be used
// and downgrade the configuration accordingly.
if (app->device->getCapabilities().sampleLocations) {
plume::RenderSampleCounts color_sample_counts = app->device->getSampleCountsSupported(plume::RenderFormat::R8G8B8A8_UNORM);
plume::RenderSampleCounts depth_sample_counts = app->device->getSampleCountsSupported(plume::RenderFormat::D32_FLOAT);
plume::RenderSampleCounts common_sample_counts = color_sample_counts & depth_sample_counts;
device_max_msaa = compute_max_supported_aa(common_sample_counts);
sample_positions_supported = true;
}
else {
device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
sample_positions_supported = false;
}
high_precision_fb_enabled = app->shaderLibrary->usesHDR;
}
banjo::renderer::RT64Context::~RT64Context() = default;
void banjo::renderer::RT64Context::send_dl(const OSTask* task) {
check_texture_pack_actions();
app->state->rsp->reset();
app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true);
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
}
void banjo::renderer::RT64Context::update_screen() {
app->updateScreen();
}
void banjo::renderer::RT64Context::shutdown() {
if (app != nullptr) {
app->end();
}
}
bool banjo::renderer::RT64Context::update_config(const ultramodern::renderer::GraphicsConfig& old_config, const ultramodern::renderer::GraphicsConfig& new_config) {
if (old_config == new_config) {
return false;
}
if (new_config.wm_option != old_config.wm_option) {
app->setFullScreen(new_config.wm_option == ultramodern::renderer::WindowMode::Fullscreen);
}
set_application_user_config(app.get(), new_config);
// When updating the user configuration, only discard framebuffers if an option was changed that affects the resolution.
bool resolution_changed = new_config.res_option != old_config.res_option;
bool aspect_ratio_changed = new_config.ar_option != old_config.ar_option;
bool downsampling_changed = new_config.ds_option != old_config.ds_option;
bool msaa_changed = new_config.msaa_option != old_config.msaa_option;
app->updateUserConfig(resolution_changed || aspect_ratio_changed || downsampling_changed || msaa_changed);
if (msaa_changed) {
app->updateMultisampling();
}
return true;
}
void banjo::renderer::RT64Context::enable_instant_present() {
// Enable the present early presentation mode for minimal latency.
app->enhancementConfig.presentation.mode = RT64::EnhancementConfiguration::Presentation::Mode::PresentEarly;
app->updateEnhancementConfig();
}
uint32_t banjo::renderer::RT64Context::get_display_framerate() const {
return app->presentQueue->ext.sharedResources->swapChainRate;
}
float banjo::renderer::RT64Context::get_resolution_scale() const {
constexpr int ReferenceHeight = 240;
switch (app->userConfig.resolution) {
case RT64::UserConfiguration::Resolution::WindowIntegerScale:
if (app->sharedQueueResources->swapChainHeight > 0) {
return std::max(float((app->sharedQueueResources->swapChainHeight + ReferenceHeight - 1) / ReferenceHeight), 1.0f);
}
else {
return 1.0f;
}
case RT64::UserConfiguration::Resolution::Manual:
return float(app->userConfig.resolutionMultiplier);
case RT64::UserConfiguration::Resolution::Original:
default:
return 1.0f;
}
}
void banjo::renderer::RT64Context::check_texture_pack_actions() {
bool packs_changed = false;
TexturePackAction cur_action;
while (texture_pack_action_queue.try_dequeue(cur_action)) {
std::visit(overloaded{
[&](TexturePackDisableAction &to_disable) {
enabled_texture_packs.erase(to_disable.mod_id);
packs_changed = true;
},
[&](TexturePackEnableAction &to_enable) {
enabled_texture_packs.insert(to_enable.mod_id);
packs_changed = true;
},
[&](TexturePackSecondaryDisableAction &to_override_disable) {
secondary_disabled_texture_packs.insert(to_override_disable.mod_id);
packs_changed = true;
},
[&](TexturePackSecondaryEnableAction &to_override_enable) {
secondary_disabled_texture_packs.erase(to_override_enable.mod_id);
packs_changed = true;
},
[&](TexturePackUpdateAction &) {
packs_changed = true;
}
}, cur_action);
}
// If any packs were disabled, unload all packs and load all the active ones.
if (packs_changed) {
// Sort the enabled texture packs in reverse order so that earlier ones override later ones.
std::vector<std::string> sorted_texture_packs{};
sorted_texture_packs.reserve(enabled_texture_packs.size());
for (const std::string& mod : enabled_texture_packs) {
if (!secondary_disabled_texture_packs.contains(mod)) {
sorted_texture_packs.emplace_back(mod);
}
}
std::sort(sorted_texture_packs.begin(), sorted_texture_packs.end(),
[](const std::string& lhs, const std::string& rhs) {
return recomp::mods::get_mod_order_index(lhs) > recomp::mods::get_mod_order_index(rhs);
}
);
// Build the path list from the sorted mod list.
std::vector<RT64::ReplacementDirectory> replacement_directories;
replacement_directories.reserve(enabled_texture_packs.size());
for (const std::string &mod_id : sorted_texture_packs) {
replacement_directories.emplace_back(RT64::ReplacementDirectory(recomp::mods::get_mod_filename(mod_id)));
}
if (!replacement_directories.empty()) {
app->textureCache->loadReplacementDirectories(replacement_directories);
}
else {
app->textureCache->clearReplacementDirectories();
}
}
}
RT64::UserConfiguration::Antialiasing banjo::renderer::RT64MaxMSAA() {
return device_max_msaa;
}
std::unique_ptr<ultramodern::renderer::RendererContext> banjo::renderer::create_render_context(uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode) {
return std::make_unique<banjo::renderer::RT64Context>(rdram, window_handle, developer_mode);
}
bool banjo::renderer::RT64SamplePositionsSupported() {
return sample_positions_supported;
}
bool banjo::renderer::RT64HighPrecisionFBEnabled() {
return high_precision_fb_enabled;
}
void banjo::renderer::trigger_texture_pack_update() {
texture_pack_action_queue.enqueue(TexturePackUpdateAction{});
}
void banjo::renderer::enable_texture_pack(const recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
texture_pack_action_queue.enqueue(TexturePackEnableAction{mod.manifest.mod_id});
// Check for the texture pack enabled config option.
const recomp::mods::ConfigSchema& config_schema = context.get_mod_config_schema(mod.manifest.mod_id);
auto find_it = config_schema.options_by_id.find(banjo::renderer::special_option_texture_pack_enabled);
if (find_it != config_schema.options_by_id.end()) {
const recomp::mods::ConfigOption& config_option = config_schema.options[find_it->second];
if (is_texture_pack_enable_config_option(config_option, false)) {
recomp::mods::ConfigValueVariant value_variant = context.get_mod_config_value(mod.manifest.mod_id, config_option.id);
uint32_t value;
if (uint32_t* value_ptr = std::get_if<uint32_t>(&value_variant)) {
value = *value_ptr;
}
else {
value = 0;
}
if (value) {
banjo::renderer::secondary_enable_texture_pack(mod.manifest.mod_id);
}
else {
banjo::renderer::secondary_disable_texture_pack(mod.manifest.mod_id);
}
}
}
}
void banjo::renderer::disable_texture_pack(const recomp::mods::ModHandle& mod) {
texture_pack_action_queue.enqueue(TexturePackDisableAction{mod.manifest.mod_id});
}
void banjo::renderer::secondary_enable_texture_pack(const std::string& mod_id) {
texture_pack_action_queue.enqueue(TexturePackSecondaryEnableAction{mod_id});
}
void banjo::renderer::secondary_disable_texture_pack(const std::string& mod_id) {
texture_pack_action_queue.enqueue(TexturePackSecondaryDisableAction{mod_id});
}
// HD texture enable option. Must be an enum with two options.
// The first option is treated as disabled and the second option is treated as enabled.
bool banjo::renderer::is_texture_pack_enable_config_option(const recomp::mods::ConfigOption& option, bool show_errors) {
if (option.id == banjo::renderer::special_option_texture_pack_enabled) {
if (option.type != recomp::mods::ConfigOptionType::Enum) {
if (show_errors) {
recompui::message_box(("Mod has the special config option id for enabling an HD texture pack (\"" + banjo::renderer::special_option_texture_pack_enabled + "\"), but the config option is not an enum.").c_str());
}
return false;
}
const recomp::mods::ConfigOptionEnum &option_enum = std::get<recomp::mods::ConfigOptionEnum>(option.variant);
if (option_enum.options.size() != 2) {
if (show_errors) {
recompui::message_box(("Mod has the special config option id for enabling an HD texture pack (\"" + banjo::renderer::special_option_texture_pack_enabled + "\"), but the config option doesn't have exactly 2 values.").c_str());
}
return false;
}
return true;
}
return false;
}
-94
View File
@@ -1,94 +0,0 @@
#include "banjo_support.h"
#include <SDL.h>
#include "nfd.h"
#include "RmlUi/Core.h"
namespace banjo {
// MARK: - Internal Helpers
void perform_file_dialog_operation(const std::function<void(bool, const std::filesystem::path&)>& callback) {
nfdnchar_t* native_path = nullptr;
nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr);
bool success = (result == NFD_OKAY);
std::filesystem::path path;
if (success) {
path = std::filesystem::path{native_path};
NFD_FreePathN(native_path);
}
callback(success, path);
}
void perform_file_dialog_operation_multiple(const std::function<void(bool, const std::list<std::filesystem::path>&)>& callback) {
const nfdpathset_t* native_paths = nullptr;
nfdresult_t result = NFD_OpenDialogMultipleN(&native_paths, nullptr, 0, nullptr);
bool success = (result == NFD_OKAY);
std::list<std::filesystem::path> paths;
nfdpathsetsize_t count = 0;
if (success) {
NFD_PathSet_GetCount(native_paths, &count);
for (nfdpathsetsize_t i = 0; i < count; i++) {
nfdnchar_t* cur_path = nullptr;
nfdresult_t cur_result = NFD_PathSet_GetPathN(native_paths, i, &cur_path);
if (cur_result == NFD_OKAY) {
paths.emplace_back(std::filesystem::path{cur_path});
}
}
NFD_PathSet_Free(native_paths);
}
callback(success, paths);
}
// MARK: - Public API
std::filesystem::path get_program_path() {
#if defined(__APPLE__)
return get_bundle_resource_directory();
#elif defined(__linux__) && defined(RECOMP_FLATPAK)
return "/app/bin";
#else
return "";
#endif
}
std::filesystem::path get_asset_path(const char* asset) {
return get_program_path() / "assets" / asset;
}
void open_file_dialog(std::function<void(bool success, const std::filesystem::path& path)> callback) {
#ifdef __APPLE__
dispatch_on_ui_thread([callback]() {
perform_file_dialog_operation(callback);
});
#else
perform_file_dialog_operation(callback);
#endif
}
void open_file_dialog_multiple(std::function<void(bool success, const std::list<std::filesystem::path>& paths)> callback) {
#ifdef __APPLE__
dispatch_on_ui_thread([callback]() {
perform_file_dialog_operation_multiple(callback);
});
#else
perform_file_dialog_operation_multiple(callback);
#endif
}
void show_error_message_box(const char *title, const char *message) {
#ifdef __APPLE__
std::string title_copy(title);
std::string message_copy(message);
dispatch_on_ui_thread([title_copy, message_copy] {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title_copy.c_str(), message_copy.c_str(), nullptr);
});
#else
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, nullptr);
#endif
}
}
+86 -82
View File
@@ -1,88 +1,92 @@
#include "recomp_ui.h"
#include "elements/ui_theme.h"
#include "theme.h"
void recomptheme::set_custom_theme() {
recompui::set_theme_color(recompui::ThemeColor::Background1, recompui::Color{2, 7, 18, 255});
recompui::set_theme_color(recompui::ThemeColor::Background2, recompui::Color{7, 15, 34, 255});
recompui::set_theme_color(recompui::ThemeColor::Background3, recompui::Color{18, 24, 38, 255});
recompui::set_theme_color(recompui::ThemeColor::BGOverlay, recompui::Color{182, 194, 221, 26});
recompui::set_theme_color(recompui::ThemeColor::ModalOverlay, recompui::Color{2, 7, 18, 229});
recompui::set_theme_color(recompui::ThemeColor::BGShadow, recompui::Color{0, 0, 0, 89});
recompui::set_theme_color(recompui::ThemeColor::BGShadow2, recompui::Color{2, 7, 18, 184});
recompui::set_theme_color(recompui::ThemeColor::Text, recompui::Color{242, 242, 242, 255});
recompui::set_theme_color(recompui::ThemeColor::TextActive, recompui::Color{245, 245, 245, 255});
recompui::set_theme_color(recompui::ThemeColor::TextDim, recompui::Color{204, 204, 204, 255});
recompui::set_theme_color(recompui::ThemeColor::TextInactive, recompui::Color{255, 255, 255, 153});
recompui::set_theme_color(recompui::ThemeColor::TextA5, recompui::Color{242, 242, 242, 13});
recompui::set_theme_color(recompui::ThemeColor::TextA20, recompui::Color{242, 242, 242, 51});
recompui::set_theme_color(recompui::ThemeColor::TextA30, recompui::Color{242, 242, 242, 77});
recompui::set_theme_color(recompui::ThemeColor::TextA50, recompui::Color{242, 242, 242, 128});
recompui::set_theme_color(recompui::ThemeColor::TextA80, recompui::Color{242, 242, 242, 204});
recompui::set_theme_color(recompui::ThemeColor::Primary, recompui::Color{29, 93, 226, 255});
recompui::set_theme_color(recompui::ThemeColor::PrimaryL, recompui::Color{167, 191, 241, 255});
recompui::set_theme_color(recompui::ThemeColor::PrimaryD, recompui::Color{0, 38, 117, 255});
recompui::set_theme_color(recompui::ThemeColor::PrimaryA5, recompui::Color{29, 93, 226, 13});
recompui::set_theme_color(recompui::ThemeColor::PrimaryA20, recompui::Color{29, 93, 226, 51});
recompui::set_theme_color(recompui::ThemeColor::PrimaryA30, recompui::Color{29, 93, 226, 77});
recompui::set_theme_color(recompui::ThemeColor::PrimaryA50, recompui::Color{29, 93, 226, 128});
recompui::set_theme_color(recompui::ThemeColor::PrimaryA80, recompui::Color{29, 93, 226, 204});
recompui::set_theme_color(recompui::ThemeColor::Secondary, recompui::Color{247, 158, 8, 255});
recompui::set_theme_color(recompui::ThemeColor::SecondaryL, recompui::Color{255, 215, 148, 255});
recompui::set_theme_color(recompui::ThemeColor::SecondaryD, recompui::Color{224, 141, 0, 255});
recompui::set_theme_color(recompui::ThemeColor::SecondaryA5, recompui::Color{247, 158, 8, 13});
recompui::set_theme_color(recompui::ThemeColor::SecondaryA20, recompui::Color{247, 158, 8, 51});
recompui::set_theme_color(recompui::ThemeColor::SecondaryA30, recompui::Color{247, 158, 8, 77});
recompui::set_theme_color(recompui::ThemeColor::SecondaryA50, recompui::Color{247, 158, 8, 128});
recompui::set_theme_color(recompui::ThemeColor::SecondaryA80, recompui::Color{247, 158, 8, 204});
recompui::set_theme_color(recompui::ThemeColor::Warning, recompui::Color{255, 254, 0, 255});
recompui::set_theme_color(recompui::ThemeColor::WarningL, recompui::Color{255, 254, 143, 255});
recompui::set_theme_color(recompui::ThemeColor::WarningD, recompui::Color{197, 163, 2, 255});
recompui::set_theme_color(recompui::ThemeColor::WarningA5, recompui::Color{255, 254, 0, 13});
recompui::set_theme_color(recompui::ThemeColor::WarningA20, recompui::Color{255, 254, 0, 51});
recompui::set_theme_color(recompui::ThemeColor::WarningA30, recompui::Color{255, 254, 0, 77});
recompui::set_theme_color(recompui::ThemeColor::WarningA50, recompui::Color{255, 254, 0, 128});
recompui::set_theme_color(recompui::ThemeColor::WarningA80, recompui::Color{255, 254, 0, 204});
recompui::set_theme_color(recompui::ThemeColor::Danger, recompui::Color{255, 53, 31, 255});
recompui::set_theme_color(recompui::ThemeColor::DangerL, recompui::Color{255, 149, 138, 255});
recompui::set_theme_color(recompui::ThemeColor::DangerD, recompui::Color{163, 16, 0, 255});
recompui::set_theme_color(recompui::ThemeColor::DangerA5, recompui::Color{255, 53, 31, 13});
recompui::set_theme_color(recompui::ThemeColor::DangerA20, recompui::Color{255, 53, 31, 51});
recompui::set_theme_color(recompui::ThemeColor::DangerA30, recompui::Color{255, 53, 31, 77});
recompui::set_theme_color(recompui::ThemeColor::DangerA50, recompui::Color{255, 53, 31, 128});
recompui::set_theme_color(recompui::ThemeColor::DangerA80, recompui::Color{255, 53, 31, 204});
recompui::set_theme_color(recompui::ThemeColor::Success, recompui::Color{40, 238, 32, 255});
recompui::set_theme_color(recompui::ThemeColor::SuccessL, recompui::Color{155, 247, 151, 255});
recompui::set_theme_color(recompui::ThemeColor::SuccessD, recompui::Color{18, 157, 12, 255});
recompui::set_theme_color(recompui::ThemeColor::SuccessA5, recompui::Color{40, 238, 32, 13});
recompui::set_theme_color(recompui::ThemeColor::SuccessA20, recompui::Color{40, 238, 32, 51});
recompui::set_theme_color(recompui::ThemeColor::SuccessA30, recompui::Color{40, 238, 32, 77});
recompui::set_theme_color(recompui::ThemeColor::SuccessA50, recompui::Color{40, 238, 32, 128});
recompui::set_theme_color(recompui::ThemeColor::SuccessA80, recompui::Color{40, 238, 32, 204});
recompui::set_theme_color(recompui::ThemeColor::Border, recompui::Color{255, 255, 255, 51});
recompui::set_theme_color(recompui::ThemeColor::BorderSoft, recompui::Color{255, 255, 255, 26});
recompui::set_theme_color(recompui::ThemeColor::BorderHard, recompui::Color{255, 255, 255, 77});
recompui::set_theme_color(recompui::ThemeColor::BorderSolid, recompui::Color{255, 255, 255, 153});
recompui::set_theme_color(recompui::ThemeColor::Transparent, recompui::Color{0, 0, 0, 0});
recompui::set_theme_color(recompui::ThemeColor::A, recompui::Color{51, 51, 255, 255});
recompui::set_theme_color(recompui::ThemeColor::AL, recompui::Color{178, 178, 255, 255});
recompui::set_theme_color(recompui::ThemeColor::AD, recompui::Color{32, 32, 172, 255});
recompui::set_theme_color(recompui::ThemeColor::AA5, recompui::Color{51, 51, 255, 13});
recompui::set_theme_color(recompui::ThemeColor::AA20, recompui::Color{51, 51, 255, 51});
recompui::set_theme_color(recompui::ThemeColor::AA30, recompui::Color{51, 51, 255, 77});
recompui::set_theme_color(recompui::ThemeColor::AA50, recompui::Color{51, 51, 255, 128});
recompui::set_theme_color(recompui::ThemeColor::AA80, recompui::Color{51, 51, 255, 204});
recompui::set_theme_color(recompui::ThemeColor::White, recompui::Color{255, 255, 255, 255});
recompui::set_theme_color(recompui::ThemeColor::WhiteA5, recompui::Color{255, 255, 255, 13});
recompui::set_theme_color(recompui::ThemeColor::WhiteA20, recompui::Color{255, 255, 255, 51});
recompui::set_theme_color(recompui::ThemeColor::WhiteA30, recompui::Color{255, 255, 255, 77});
recompui::set_theme_color(recompui::ThemeColor::WhiteA50, recompui::Color{255, 255, 255, 128});
recompui::set_theme_color(recompui::ThemeColor::WhiteA80, recompui::Color{255, 255, 255, 204});
recompui::set_theme_color(recompui::ThemeColor::BW05, recompui::Color{13, 13, 13, 255});
recompui::set_theme_color(recompui::ThemeColor::BW10, recompui::Color{26, 26, 26, 255});
recompui::set_theme_color(recompui::ThemeColor::BW25, recompui::Color{64, 64, 64, 255});
recompui::set_theme_color(recompui::ThemeColor::BW50, recompui::Color{128, 128, 128, 255});
recompui::set_theme_color(recompui::ThemeColor::BW75, recompui::Color{191, 191, 191, 255});
recompui::set_theme_color(recompui::ThemeColor::BW90, recompui::Color{229, 229, 229, 255});
recompui::theme::set_theme_color(recompui::theme::color::Background1, recompui::Color{10, 10, 11, 255});
recompui::theme::set_theme_color(recompui::theme::color::Background2, recompui::Color{19, 20, 21, 255});
recompui::theme::set_theme_color(recompui::theme::color::Background3, recompui::Color{27, 27, 29, 255});
recompui::theme::set_theme_color(recompui::theme::color::BGOverlay, recompui::Color{199, 200, 204, 26}); // actually the overlay for the modal
recompui::theme::set_theme_color(recompui::theme::color::ModalOverlay, recompui::Color{10, 10, 11, 242}); // actually the background color of the modal itself
recompui::theme::set_theme_color(recompui::theme::color::BGShadow, recompui::Color{0, 0, 0, 89});
recompui::theme::set_theme_color(recompui::theme::color::BGShadow2, recompui::Color{10, 10, 11, 184});
recompui::theme::set_theme_color(recompui::theme::color::Text, recompui::Color{242, 242, 242, 255});
recompui::theme::set_theme_color(recompui::theme::color::TextActive, recompui::Color{245, 245, 245, 255});
recompui::theme::set_theme_color(recompui::theme::color::TextDim, recompui::Color{204, 204, 204, 255});
recompui::theme::set_theme_color(recompui::theme::color::TextInactive, recompui::Color{255, 255, 255, 153});
recompui::theme::set_theme_color(recompui::theme::color::TextA5, recompui::Color{242, 242, 242, 13});
recompui::theme::set_theme_color(recompui::theme::color::TextA20, recompui::Color{242, 242, 242, 51});
recompui::theme::set_theme_color(recompui::theme::color::TextA30, recompui::Color{242, 242, 242, 77});
recompui::theme::set_theme_color(recompui::theme::color::TextA50, recompui::Color{242, 242, 242, 128});
recompui::theme::set_theme_color(recompui::theme::color::TextA80, recompui::Color{242, 242, 242, 204});
recompui::theme::set_theme_color(recompui::theme::color::Primary, recompui::Color{29, 93, 226, 255});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryL, recompui::Color{167, 191, 241, 255});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryD, recompui::Color{0, 38, 117, 255});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryA5, recompui::Color{29, 93, 226, 13});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryA20, recompui::Color{29, 93, 226, 51});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryA30, recompui::Color{29, 93, 226, 77});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryA50, recompui::Color{29, 93, 226, 128});
recompui::theme::set_theme_color(recompui::theme::color::PrimaryA80, recompui::Color{29, 93, 226, 204});
recompui::theme::set_theme_color(recompui::theme::color::Secondary, recompui::Color{247, 158, 8, 255});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryL, recompui::Color{255, 215, 148, 255});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryD, recompui::Color{224, 141, 0, 255});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryA5, recompui::Color{247, 158, 8, 13});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryA20, recompui::Color{247, 158, 8, 51});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryA30, recompui::Color{247, 158, 8, 77});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryA50, recompui::Color{247, 158, 8, 128});
recompui::theme::set_theme_color(recompui::theme::color::SecondaryA80, recompui::Color{247, 158, 8, 204});
recompui::theme::set_theme_color(recompui::theme::color::Warning, recompui::Color{255, 254, 0, 255});
recompui::theme::set_theme_color(recompui::theme::color::WarningL, recompui::Color{255, 254, 143, 255});
recompui::theme::set_theme_color(recompui::theme::color::WarningD, recompui::Color{197, 163, 2, 255});
recompui::theme::set_theme_color(recompui::theme::color::WarningA5, recompui::Color{255, 254, 0, 13});
recompui::theme::set_theme_color(recompui::theme::color::WarningA20, recompui::Color{255, 254, 0, 51});
recompui::theme::set_theme_color(recompui::theme::color::WarningA30, recompui::Color{255, 254, 0, 77});
recompui::theme::set_theme_color(recompui::theme::color::WarningA50, recompui::Color{255, 254, 0, 128});
recompui::theme::set_theme_color(recompui::theme::color::WarningA80, recompui::Color{255, 254, 0, 204});
recompui::theme::set_theme_color(recompui::theme::color::Danger, recompui::Color{255, 53, 31, 255});
recompui::theme::set_theme_color(recompui::theme::color::DangerL, recompui::Color{255, 149, 138, 255});
recompui::theme::set_theme_color(recompui::theme::color::DangerD, recompui::Color{163, 16, 0, 255});
recompui::theme::set_theme_color(recompui::theme::color::DangerA5, recompui::Color{255, 53, 31, 13});
recompui::theme::set_theme_color(recompui::theme::color::DangerA20, recompui::Color{255, 53, 31, 51});
recompui::theme::set_theme_color(recompui::theme::color::DangerA30, recompui::Color{255, 53, 31, 77});
recompui::theme::set_theme_color(recompui::theme::color::DangerA50, recompui::Color{255, 53, 31, 128});
recompui::theme::set_theme_color(recompui::theme::color::DangerA80, recompui::Color{255, 53, 31, 204});
recompui::theme::set_theme_color(recompui::theme::color::Success, recompui::Color{40, 238, 32, 255});
recompui::theme::set_theme_color(recompui::theme::color::SuccessL, recompui::Color{155, 247, 151, 255});
recompui::theme::set_theme_color(recompui::theme::color::SuccessD, recompui::Color{18, 157, 12, 255});
recompui::theme::set_theme_color(recompui::theme::color::SuccessA5, recompui::Color{40, 238, 32, 13});
recompui::theme::set_theme_color(recompui::theme::color::SuccessA20, recompui::Color{40, 238, 32, 51});
recompui::theme::set_theme_color(recompui::theme::color::SuccessA30, recompui::Color{40, 238, 32, 77});
recompui::theme::set_theme_color(recompui::theme::color::SuccessA50, recompui::Color{40, 238, 32, 128});
recompui::theme::set_theme_color(recompui::theme::color::SuccessA80, recompui::Color{40, 238, 32, 204});
recompui::theme::set_theme_color(recompui::theme::color::Border, recompui::Color{255, 255, 255, 51});
recompui::theme::set_theme_color(recompui::theme::color::BorderSoft, recompui::Color{255, 255, 255, 26});
recompui::theme::set_theme_color(recompui::theme::color::BorderHard, recompui::Color{255, 255, 255, 77});
recompui::theme::set_theme_color(recompui::theme::color::BorderSolid, recompui::Color{255, 255, 255, 153});
recompui::theme::set_theme_color(recompui::theme::color::Transparent, recompui::Color{0, 0, 0, 0});
recompui::theme::set_theme_color(recompui::theme::color::A, recompui::Color{51, 51, 255, 255});
recompui::theme::set_theme_color(recompui::theme::color::AL, recompui::Color{178, 178, 255, 255});
recompui::theme::set_theme_color(recompui::theme::color::AD, recompui::Color{32, 32, 172, 255});
recompui::theme::set_theme_color(recompui::theme::color::AA5, recompui::Color{51, 51, 255, 13});
recompui::theme::set_theme_color(recompui::theme::color::AA20, recompui::Color{51, 51, 255, 51});
recompui::theme::set_theme_color(recompui::theme::color::AA30, recompui::Color{51, 51, 255, 77});
recompui::theme::set_theme_color(recompui::theme::color::AA50, recompui::Color{51, 51, 255, 128});
recompui::theme::set_theme_color(recompui::theme::color::AA80, recompui::Color{51, 51, 255, 204});
recompui::theme::set_theme_color(recompui::theme::color::White, recompui::Color{255, 255, 255, 255});
recompui::theme::set_theme_color(recompui::theme::color::WhiteA5, recompui::Color{255, 255, 255, 13});
recompui::theme::set_theme_color(recompui::theme::color::WhiteA20, recompui::Color{255, 255, 255, 51});
recompui::theme::set_theme_color(recompui::theme::color::WhiteA30, recompui::Color{255, 255, 255, 77});
recompui::theme::set_theme_color(recompui::theme::color::WhiteA50, recompui::Color{255, 255, 255, 128});
recompui::theme::set_theme_color(recompui::theme::color::WhiteA80, recompui::Color{255, 255, 255, 204});
recompui::theme::set_theme_color(recompui::theme::color::BW05, recompui::Color{13, 13, 13, 255});
recompui::theme::set_theme_color(recompui::theme::color::BW10, recompui::Color{26, 26, 26, 255});
recompui::theme::set_theme_color(recompui::theme::color::BW25, recompui::Color{64, 64, 64, 255});
recompui::theme::set_theme_color(recompui::theme::color::BW50, recompui::Color{128, 128, 128, 255});
recompui::theme::set_theme_color(recompui::theme::color::BW75, recompui::Color{191, 191, 191, 255});
recompui::theme::set_theme_color(recompui::theme::color::BW90, recompui::Color{229, 229, 229, 255});
recompui::theme::border::radius_sm = 12.0f;
recompui::theme::border::radius_md = 18.0f;
recompui::theme::border::radius_lg = 24.0f;
};
-725
View File
@@ -1,725 +0,0 @@
#include <mutex>
#include <string>
#include <unordered_map>
#include <fstream>
#include "slot_map.h"
#include "RmlUi/Core/StreamMemory.h"
#include "ultramodern/error_handling.hpp"
#include "recomp_ui.h"
#include "ui_context.h"
#include "../elements/ui_element.h"
// Hash implementations for ContextId and ResourceId.
template <>
struct std::hash<recompui::ContextId> {
std::size_t operator()(const recompui::ContextId& id) const {
return std::hash<uint32_t>()(id.slot_id);
}
};
template <>
struct std::hash<recompui::ResourceId> {
std::size_t operator()(const recompui::ResourceId& id) const {
return std::hash<uint32_t>()(id.slot_id);
}
};
using resource_slotmap = dod::slot_map32<std::unique_ptr<recompui::Style>>;
namespace recompui {
struct Context {
std::mutex context_lock;
resource_slotmap resources;
Rml::ElementDocument* document;
Element root_element;
Element* autofocus_element = nullptr;
std::vector<Element*> loose_elements;
std::unordered_set<ResourceId> to_update;
std::vector<std::tuple<Element*, ResourceId, std::string>> to_set_text;
bool captures_input = true;
bool captures_mouse = true;
Context(Rml::ElementDocument* document) : document(document), root_element(document) {}
};
} // namespace recompui
using context_slotmap = dod::slot_map32<recompui::Context>;
static struct {
std::recursive_mutex all_contexts_lock;
context_slotmap all_contexts;
std::unordered_set<recompui::ContextId> opened_contexts;
std::unordered_map<Rml::ElementDocument*, recompui::ContextId> documents_to_contexts;
Rml::SharedPtr<Rml::StyleSheetContainer> style_sheet;
} context_state;
thread_local recompui::Context* opened_context = nullptr;
thread_local recompui::ContextId opened_context_id = recompui::ContextId::null();
enum class ContextErrorType {
OpenWithoutClose,
OpenInvalidContext,
CloseWithoutOpen,
CloseWrongContext,
DestroyInvalidContext,
GetContextWithoutOpen,
AddResourceWithoutOpen,
AddResourceToWrongContext,
UpdateElementWithoutContext,
UpdateElementInWrongContext,
SetTextElementWithoutContext,
SetTextElementInWrongContext,
GetResourceWithoutOpen,
GetResourceFailed,
DestroyResourceWithoutOpen,
DestroyResourceInWrongContext,
DestroyResourceNotFound,
GetDocumentInvalidContext,
GetAutofocusInvalidContext,
SetAutofocusInvalidContext,
InternalError,
};
enum class SlotTag : uint8_t {
Style = 0,
Element = 1,
};
void context_error(recompui::ContextId id, ContextErrorType type) {
(void)id;
const char* error_message = "";
switch (type) {
case ContextErrorType::OpenWithoutClose:
error_message = "Attempted to open a UI context without closing another UI context";
break;
case ContextErrorType::OpenInvalidContext:
error_message = "Attempted to open an invalid UI context";
break;
case ContextErrorType::CloseWithoutOpen:
error_message = "Attempted to close a UI context without one being open";
break;
case ContextErrorType::CloseWrongContext:
error_message = "Attempted to close a different UI context than the one that's open";
break;
case ContextErrorType::DestroyInvalidContext:
error_message = "Attempted to destroy an invalid UI element";
break;
case ContextErrorType::GetContextWithoutOpen:
error_message = "Attempted to get the current UI context with no UI context open";
break;
case ContextErrorType::AddResourceWithoutOpen:
error_message = "Attempted to create a UI resource with no open UI context";
break;
case ContextErrorType::AddResourceToWrongContext:
error_message = "Attempted to create a UI resource in a different UI context than the one that's open";
break;
case ContextErrorType::UpdateElementWithoutContext:
error_message = "Attempted to update a UI element with no open UI context";
break;
case ContextErrorType::UpdateElementInWrongContext:
error_message = "Attempted to update a UI element in a different UI context than the one that's open";
break;
case ContextErrorType::SetTextElementWithoutContext:
error_message = "Attempted to set the text of a UI element with no open UI context";
break;
case ContextErrorType::SetTextElementInWrongContext:
error_message = "Attempted to set the text of a UI element in a different UI context than the one that's open";
break;
case ContextErrorType::GetResourceWithoutOpen:
error_message = "Attempted to get a UI resource with no open UI context";
break;
case ContextErrorType::GetResourceFailed:
error_message = "Failed to get a UI resource from the current open UI context";
break;
case ContextErrorType::DestroyResourceWithoutOpen:
error_message = "Attempted to destroy a UI resource with no open UI context";
break;
case ContextErrorType::DestroyResourceInWrongContext:
error_message = "Attempted to destroy a UI resource in a different UI context than the one that's open";
break;
case ContextErrorType::DestroyResourceNotFound:
error_message = "Attempted to destroy a UI resource that doesn't exist in the current context";
break;
case ContextErrorType::GetDocumentInvalidContext:
error_message = "Attempted to get the document of an invalid UI context";
break;
case ContextErrorType::GetAutofocusInvalidContext:
error_message = "Attempted to get the autofocus element of an invalid UI context";
break;
case ContextErrorType::SetAutofocusInvalidContext:
error_message = "Attempted to set the autofocus element of an invalid UI context";
break;
case ContextErrorType::InternalError:
error_message = "Internal error in UI context";
break;
default:
error_message = "Unknown UI context error";
break;
}
// This assumes the error is coming from a mod, as it's unlikely that an end user will see a UI context error
// in the base recomp.
recompui::message_box((std::string{"Fatal error in mod - "} + error_message + ".").c_str());
assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
}
recompui::ContextId create_context_impl(Rml::ElementDocument* document) {
static Rml::ElementDocument dummy_document{""};
bool add_to_dict = true;
if (document == nullptr) {
document = &dummy_document;
add_to_dict = false;
}
recompui::ContextId ret;
{
std::lock_guard lock{ context_state.all_contexts_lock };
ret = { context_state.all_contexts.emplace(document).raw };
if (add_to_dict) {
context_state.documents_to_contexts.emplace(document, ret);
}
}
return ret;
}
void recompui::init_styling(const std::filesystem::path& rcss_file) {
std::string style{};
{
std::ifstream style_stream{rcss_file};
style_stream.seekg(0, std::ios::end);
style.resize(style_stream.tellg());
style_stream.seekg(0, std::ios::beg);
style_stream.read(style.data(), style.size());
}
std::unique_ptr<Rml::StreamMemory> rml_stream = std::make_unique<Rml::StreamMemory>(reinterpret_cast<Rml::byte*>(style.data()), style.size());
rml_stream->SetSourceURL(rcss_file.filename().string());
context_state.style_sheet = Rml::Factory::InstanceStyleSheetStream(rml_stream.get());
}
recompui::ContextId recompui::create_context(const std::filesystem::path& path) {
ContextId new_context = create_context_impl(nullptr);
auto workingdir = std::filesystem::current_path();
new_context.open();
Rml::ElementDocument* doc = recompui::load_document(path.string());
opened_context->document = doc;
opened_context->root_element.base = doc;
new_context.close();
{
std::lock_guard lock{ context_state.all_contexts_lock };
context_state.documents_to_contexts.emplace(doc, new_context);
}
return new_context;
}
recompui::ContextId recompui::create_context(Rml::ElementDocument* document) {
assert(document != nullptr);
return create_context_impl(document);
}
recompui::ContextId recompui::create_context() {
Rml::ElementDocument* doc = create_empty_document();
doc->SetStyleSheetContainer(context_state.style_sheet);
ContextId ret = create_context_impl(doc);
Element* root = ret.get_root_element();
// Mark the root element as not being a shim, as that's only needed for elements that were parented to Rml ones manually.
root->shim = false;
ret.open();
root->set_width(100.0f, Unit::Percent);
root->set_height(100.0f, Unit::Percent);
root->set_display(Display::Flex);
ret.close();
doc->Hide();
return ret;
}
void recompui::destroy_context(ContextId id) {
bool existed = false;
// TODO prevent deletion of a context while its mutex is in use. Second lock on the context's mutex before popping
// from the slotmap?
// Check if the provided id exists.
{
std::lock_guard lock{ context_state.all_contexts_lock };
// Check if the target context is currently open.
existed = context_state.all_contexts.has_key(context_slotmap::key{ id.slot_id });
}
// Raise an error if the context didn't exist.
if (!existed) {
context_error(id, ContextErrorType::DestroyInvalidContext);
}
id.open();
id.clear_children();
id.close();
// Delete the provided id.
{
std::lock_guard lock{ context_state.all_contexts_lock };
context_state.all_contexts.erase(context_slotmap::key{ id.slot_id });
}
}
void recompui::destroy_all_contexts() {
recompui::hide_all_contexts();
std::lock_guard lock{ context_state.all_contexts_lock };
// TODO prevent deletion of a context while its mutex is in use. Second lock on the context's mutex before popping
// from the slotmap
std::vector<context_slotmap::key> keys{};
for (const auto& [key, item] : context_state.all_contexts.items()) {
keys.push_back(key);
}
for (auto key : keys) {
Context* ctx = context_state.all_contexts.get(key);
std::lock_guard context_lock{ ctx->context_lock };
opened_context = ctx;
opened_context_id = ContextId{ key };
opened_context_id.clear_children();
opened_context = nullptr;
opened_context_id = ContextId::null();
}
context_state.all_contexts.reset();
context_state.documents_to_contexts.clear();
}
void recompui::ContextId::open() {
// Ensure no other context is opened by this thread already.
if (opened_context_id != ContextId::null()) {
context_error(*this, ContextErrorType::OpenWithoutClose);
}
// Get the context with this id.
Context* ctx;
{
std::lock_guard lock{ context_state.all_contexts_lock };
ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
// If the context was found, add it to the opened contexts.
if (ctx != nullptr) {
context_state.opened_contexts.emplace(*this);
}
}
// Check if the context exists.
if (ctx == nullptr) {
context_error(*this, ContextErrorType::OpenInvalidContext);
}
// Take ownership of the target context.
ctx->context_lock.lock();
opened_context = ctx;
opened_context_id = *this;
}
bool recompui::ContextId::open_if_not_already() {
if (opened_context_id == *this) {
return false;
}
open();
return true;
}
void recompui::ContextId::close() {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::CloseWithoutOpen);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::CloseWrongContext);
}
// Release ownership of the target context.
opened_context->context_lock.unlock();
opened_context = nullptr;
opened_context_id = ContextId::null();
// Remove this context from the opened contexts.
{
std::lock_guard lock{ context_state.all_contexts_lock };
context_state.opened_contexts.erase(*this);
}
}
recompui::ContextId recompui::try_close_current_context() {
if (opened_context_id != ContextId::null()) {
ContextId prev_context = opened_context_id;
opened_context_id.close();
return prev_context;
}
return ContextId::null();
}
void recompui::ContextId::process_updates() {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::InternalError);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::InternalError);
}
// Move the current update set into a local variable. This clears the update set
// and allows it to be used to queue updates from any element callbacks.
std::unordered_set<ResourceId> to_update = std::move(opened_context->to_update);
Event update_event = Event::update_event();
for (auto cur_resource_id : to_update) {
resource_slotmap::key cur_key{ cur_resource_id.slot_id };
// Ignore any resources that aren't elements.
if (cur_key.get_tag() != static_cast<uint8_t>(SlotTag::Element)) {
// Assert to catch errors of queueing other resource types for update.
// This isn't an actual error, so there's no issue with continuing in release builds.
assert(false);
continue;
}
// Get the resource being updaten from the context.
std::unique_ptr<Style>* cur_resource = opened_context->resources.get(cur_key);
// Make sure the resource exists before dispatching the event. It may have been deleted
// after being queued for a update, so just continue to the next element if it doesn't exist.
if (cur_resource == nullptr) {
continue;
}
static_cast<Element*>(cur_resource->get())->handle_event(update_event);
}
std::vector<std::tuple<Element*, ResourceId, std::string>> to_set_text = std::move(opened_context->to_set_text);
// Delete the Rml elements that are pending deletion.
for (auto cur_text_update : to_set_text) {
Element* element_ptr = std::get<0>(cur_text_update);
ResourceId resource = std::get<1>(cur_text_update);
std::string& text = std::get<2>(cur_text_update);
// If the resource ID is valid, prefer that as we can quickly validate if the resource still exists.
if (resource != ResourceId::null()) {
resource_slotmap::key cur_key{ resource.slot_id };
std::unique_ptr<Style>* cur_resource = opened_context->resources.get(cur_key);
// Make sure the resource exists before setting its text, as it may have been deleted.
if (cur_resource == nullptr) {
continue;
}
// Perform the text update.
static_cast<Element*>(cur_resource->get())->base->SetInnerRML(text);
}
// Otherwise we use the element pointer, but we need to validate that it still exists before doing so.
else {
// Scan the current resources to find the target element.
for (const std::unique_ptr<Style>& cur_e : opened_context->resources) {
if (cur_e.get() == element_ptr) {
element_ptr->base->SetInnerRML(text);
// We can stop after finding the element.
break;
}
}
}
}
}
bool recompui::ContextId::captures_input() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return false;
}
return ctx->captures_input;
}
bool recompui::ContextId::captures_mouse() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return false;
}
return ctx->captures_mouse;
}
void recompui::ContextId::set_captures_input(bool captures_input) {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return;
}
ctx->captures_input = captures_input;
}
void recompui::ContextId::set_captures_mouse(bool captures_mouse) {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return;
}
ctx->captures_mouse = captures_mouse;
}
recompui::Style* recompui::ContextId::add_resource_impl(std::unique_ptr<Style>&& resource) {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::AddResourceWithoutOpen);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::AddResourceToWrongContext);
}
bool is_element = resource->is_element();
Style* resource_ptr = resource.get();
auto key = opened_context->resources.emplace(std::move(resource));
if (is_element) {
Element* element_ptr = static_cast<Element*>(resource_ptr);
element_ptr->set_id(std::string{element_ptr->get_type_name()} + "-" + std::to_string(key.raw));
key.set_tag(static_cast<uint8_t>(SlotTag::Element));
// Send one update to the element.
opened_context->to_update.emplace(ResourceId{ key.raw });
}
else {
key.set_tag(static_cast<uint8_t>(SlotTag::Style));
}
resource_ptr->resource_id = { key.raw };
return resource_ptr;
}
void recompui::ContextId::add_loose_element(Element* element) {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::AddResourceWithoutOpen);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::AddResourceToWrongContext);
}
opened_context->loose_elements.emplace_back(element);
}
void recompui::ContextId::queue_element_update(ResourceId element) {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::UpdateElementWithoutContext);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::UpdateElementInWrongContext);
}
// Check that the element that was specified is in the open context.
auto* elementPtr = opened_context->resources.get(resource_slotmap::key{ element.slot_id });
if (elementPtr == nullptr) {
context_error(*this, ContextErrorType::UpdateElementInWrongContext);
}
opened_context->to_update.emplace(element);
}
void recompui::ContextId::queue_set_text(Element* element, std::string&& text) {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::SetTextElementWithoutContext);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::SetTextElementInWrongContext);
}
opened_context->to_set_text.emplace_back(std::make_tuple(element, element->resource_id, std::move(text)));
}
recompui::Style* recompui::ContextId::create_style() {
return add_resource_impl(std::make_unique<Style>());
}
void recompui::ContextId::destroy_resource(Style* resource) {
destroy_resource(resource->resource_id);
}
void recompui::ContextId::destroy_resource(ResourceId resource) {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::DestroyResourceWithoutOpen);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::DestroyResourceInWrongContext);
}
// Try to remove the resource from the current context.
auto pop_result = opened_context->resources.pop(resource_slotmap::key{ resource.slot_id });
if (!pop_result.has_value()) {
context_error(*this, ContextErrorType::DestroyResourceNotFound);
}
}
void recompui::ContextId::clear_children() {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(*this, ContextErrorType::DestroyResourceWithoutOpen);
}
// Check that the context that was specified is the same one that's currently open.
if (*this != opened_context_id) {
context_error(*this, ContextErrorType::DestroyResourceInWrongContext);
}
// Remove the root element's children.
opened_context->root_element.clear_children();
// Remove any loose resources.
for (Element* e : opened_context->loose_elements) {
destroy_resource(e->resource_id);
}
opened_context->loose_elements.clear();
}
Rml::ElementDocument* recompui::ContextId::get_document() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
context_error(*this, ContextErrorType::GetDocumentInvalidContext);
}
return ctx->document;
}
recompui::Element* recompui::ContextId::get_root_element() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
context_error(*this, ContextErrorType::GetDocumentInvalidContext);
}
return &ctx->root_element;
}
recompui::Element* recompui::ContextId::get_autofocus_element() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
context_error(*this, ContextErrorType::GetAutofocusInvalidContext);
}
return ctx->autofocus_element;
}
void recompui::ContextId::set_autofocus_element(Element* element) {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
context_error(*this, ContextErrorType::SetAutofocusInvalidContext);
}
ctx->autofocus_element = element;
}
recompui::ContextId recompui::get_current_context() {
// Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) {
context_error(ContextId::null(), ContextErrorType::GetContextWithoutOpen);
}
return opened_context_id;
}
recompui::Style* get_resource_from_current_context(resource_slotmap::key key) {
// Ensure a context is currently opened by this thread.
if (opened_context_id == recompui::ContextId::null()) {
context_error(recompui::ContextId::null(), ContextErrorType::GetResourceWithoutOpen);
}
auto* value = opened_context->resources.get(key);
if (value == nullptr) {
context_error(opened_context_id, ContextErrorType::GetResourceFailed);
}
return value->get();
}
const recompui::Style* recompui::ResourceId::operator*() const {
resource_slotmap::key key{ slot_id };
return get_resource_from_current_context(key);
}
recompui::Style* recompui::ResourceId::operator*() {
resource_slotmap::key key{ slot_id };
return get_resource_from_current_context(key);
}
const recompui::Element* recompui::ResourceId::as_element() const {
resource_slotmap::key key{ slot_id };
uint8_t tag = key.get_tag();
assert(tag == static_cast<uint8_t>(SlotTag::Element));
return static_cast<Element*>(get_resource_from_current_context(key));
}
recompui::Element* recompui::ResourceId::as_element() {
resource_slotmap::key key{ slot_id };
uint8_t tag = key.get_tag();
assert(tag == static_cast<uint8_t>(SlotTag::Element));
return static_cast<Element*>(get_resource_from_current_context(key));
}
recompui::ContextId recompui::get_context_from_document(Rml::ElementDocument* document) {
std::lock_guard lock{ context_state.all_contexts_lock };
auto find_it = context_state.documents_to_contexts.find(document);
if (find_it == context_state.documents_to_contexts.end()) {
return ContextId::null();
}
return find_it->second;
}
-70
View File
@@ -1,70 +0,0 @@
#pragma once
#include <cstdint>
#include <memory>
#include <utility>
#include <filesystem>
#include <functional>
#include "RmlUi/Core.h"
#include "ui_resource.h"
namespace recompui {
class Style;
class Element;
class ContextId {
Style* add_resource_impl(std::unique_ptr<Style>&& resource);
public:
uint32_t slot_id;
auto operator<=>(const ContextId& rhs) const = default;
template <typename T, typename... Args>
T* create_element(Args... args) {
return static_cast<T*>(add_resource_impl(std::make_unique<T>(std::forward<Args>(args)...)));
}
template <typename T>
T* create_element(T&& element) {
return static_cast<T*>(add_resource_impl(std::make_unique<T>(std::move(element))));
}
void add_loose_element(Element* element);
void queue_element_update(ResourceId element);
void queue_set_text(Element* element, std::string&& text);
Style* create_style();
void destroy_resource(Style* resource);
void destroy_resource(ResourceId resource);
void clear_children();
Rml::ElementDocument* get_document();
Element* get_root_element();
Element* get_autofocus_element();
void set_autofocus_element(Element* element);
void open();
bool open_if_not_already();
void close();
void process_updates();
static constexpr ContextId null() { return ContextId{ .slot_id = uint32_t(-1) }; }
bool captures_input();
bool captures_mouse();
void set_captures_input(bool captures_input);
void set_captures_mouse(bool captures_input);
};
ContextId create_context(const std::filesystem::path& path);
ContextId create_context(Rml::ElementDocument* document);
ContextId create_context();
void destroy_context(ContextId id);
ContextId get_current_context();
ContextId get_context_from_document(Rml::ElementDocument* document);
void destroy_all_contexts();
void register_ui_exports();
} // namespace recompui
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#include <cstdint>
namespace recompui {
class Style;
class Element;
struct ResourceId {
uint32_t slot_id;
bool operator==(const ResourceId& rhs) const = default;
const Style* operator*() const;
Style* operator*();
const Style* operator->() const { return *(*this); }
Style* operator->() { return *(*this); }
const Element* as_element() const;
Element* as_element();
static constexpr ResourceId null() { return ResourceId{ uint32_t(-1) }; }
};
} // namespace recompui
-131
View File
@@ -1,131 +0,0 @@
#include "ui_button.h"
#include <cassert>
namespace recompui {
Button::Button(Element *parent, const std::string &text, ButtonStyle style) : Element(parent, Events(EventType::Click, EventType::Hover, EventType::Enable, EventType::Focus), "button", true) {
this->style = style;
enable_focus();
set_text(text);
set_display(Display::Block);
set_padding(23.0f);
set_border_width(1.1f);
set_border_radius(12.0f);
set_font_size(28.0f);
set_letter_spacing(3.08f);
set_line_height(28.0f);
set_font_style(FontStyle::Normal);
set_font_weight(700);
set_cursor(Cursor::Pointer);
set_color(ThemeColor::Text);
set_tab_index(TabIndex::Auto);
hover_style.set_color(ThemeColor::Text);
focus_style.set_color(ThemeColor::Text);
disabled_style.set_color(ThemeColor::TextDim, 128);
hover_disabled_style.set_color(ThemeColor::Text, 128);
apply_button_style(style);
// transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out;
}
void Button::process_event(const Event &e) {
switch (e.type) {
case EventType::Click:
if (is_enabled()) {
for (const auto &function : pressed_callbacks) {
function();
}
}
break;
case EventType::Hover:
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
break;
case EventType::Enable:
{
bool enable_active = std::get<EventEnable>(e.variant).active;
set_style_enabled(disabled_state, !enable_active);
if (enable_active) {
set_cursor(Cursor::Pointer);
set_focusable(true);
}
else {
set_cursor(Cursor::None);
set_focusable(false);
}
}
break;
case EventType::Focus:
set_style_enabled(focus_state, std::get<EventFocus>(e.variant).active);
break;
case EventType::Update:
break;
default:
assert(false && "Unknown event type.");
break;
}
}
void Button::add_pressed_callback(std::function<void()> callback) {
pressed_callbacks.emplace_back(callback);
}
void Button::apply_button_style(ButtonStyle new_style) {
style = new_style;
switch (style) {
case ButtonStyle::Primary: {
apply_theme_style(ThemeColor::Primary);
break;
}
case ButtonStyle::Secondary: {
apply_theme_style(ThemeColor::Secondary);
break;
}
case ButtonStyle::Tertiary: {
apply_theme_style(ThemeColor::Text);
break;
}
case ButtonStyle::Success: {
apply_theme_style(ThemeColor::Success);
break;
}
case ButtonStyle::Warning: {
apply_theme_style(ThemeColor::Warning);
break;
}
case ButtonStyle::Danger: {
apply_theme_style(ThemeColor::Danger);
break;
}
default:
assert(false && "Unknown button style.");
break;
}
}
void Button::apply_theme_style(recompui::ThemeColor color) {
const uint8_t border_opacity = 204;
const uint8_t background_opacity = 13;
const uint8_t border_hover_opacity = 255;
const uint8_t background_hover_opacity = 77;
set_border_color(color, border_opacity);
set_background_color(color, background_opacity);
hover_style.set_border_color(color, border_hover_opacity);
hover_style.set_background_color(color, background_hover_opacity);
focus_style.set_border_color(color, border_hover_opacity);
focus_style.set_background_color(color, background_hover_opacity);
disabled_style.set_border_color(color, border_opacity / 4);
disabled_style.set_background_color(color, background_opacity / 4);
hover_disabled_style.set_border_color(color, border_hover_opacity / 4);
hover_disabled_style.set_background_color(color, background_hover_opacity / 4);
add_style(&hover_style, hover_state);
add_style(&focus_style, focus_state);
add_style(&disabled_style, disabled_state);
add_style(&hover_disabled_style, { hover_state, disabled_state });
}
};
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#include "ui_element.h"
namespace recompui {
enum class ButtonStyle {
Primary,
Secondary,
Tertiary,
Success,
Warning,
Danger,
};
class Button : public Element {
protected:
ButtonStyle style = ButtonStyle::Primary;
Style hover_style;
Style focus_style;
Style disabled_style;
Style hover_disabled_style;
std::list<std::function<void()>> pressed_callbacks;
// Element overrides.
virtual void process_event(const Event &e) override;
std::string_view get_type_name() override { return "Button"; }
public:
Button(Element *parent, const std::string &text, ButtonStyle style);
void add_pressed_callback(std::function<void()> callback);
Style* get_hover_style() { return &hover_style; }
Style* get_focus_style() { return &focus_style; }
Style* get_disabled_style() { return &disabled_style; }
Style* get_hover_disabled_style() { return &hover_disabled_style; }
void apply_button_style(ButtonStyle new_style);
private:
void apply_theme_style(recompui::ThemeColor color);
};
} // namespace recompui
-77
View File
@@ -1,77 +0,0 @@
#include "ui_clickable.h"
namespace recompui {
Clickable::Clickable(Element *parent, bool draggable) : Element(parent, Events(EventType::Click, EventType::MouseButton, EventType::Hover, EventType::Enable, draggable ? EventType::Drag : EventType::None)) {
set_cursor(Cursor::Pointer);
if (draggable) {
set_drag(Drag::Drag);
}
}
void Clickable::process_event(const Event &e) {
switch (e.type) {
case EventType::Click: {
if (is_enabled()) {
const EventClick &click = std::get<EventClick>(e.variant);
for (const auto &function : clicked_callbacks) {
function(click.x, click.y);
}
break;
}
}
case EventType::MouseButton: {
if (is_enabled()) {
const EventMouseButton &mousebutton = std::get<EventMouseButton>(e.variant);
if (mousebutton.button == MouseButton::Left && mousebutton.pressed) {
for (const auto &function : pressed_callbacks) {
function(mousebutton.x, mousebutton.y);
}
}
break;
}
}
case EventType::Hover:
set_style_enabled(hover_state, std::get<EventHover>(e.variant).active && is_enabled());
break;
case EventType::Enable:
{
bool enable_active = std::get<EventEnable>(e.variant).active;
set_style_enabled(disabled_state, !enable_active);
if (enable_active) {
set_cursor(Cursor::Pointer);
set_focusable(true);
}
else {
set_cursor(Cursor::None);
set_focusable(false);
}
}
break;
case EventType::Drag: {
if (is_enabled()) {
const EventDrag &drag = std::get<EventDrag>(e.variant);
for (const auto &function : dragged_callbacks) {
function(drag.x, drag.y, drag.phase);
}
break;
}
}
default:
break;
}
}
void Clickable::add_clicked_callback(std::function<void(float, float)> callback) {
clicked_callbacks.emplace_back(callback);
}
void Clickable::add_pressed_callback(std::function<void(float, float)> callback) {
pressed_callbacks.emplace_back(callback);
}
void Clickable::add_dragged_callback(std::function<void(float, float, DragPhase)> callback) {
dragged_callbacks.emplace_back(callback);
}
};
-23
View File
@@ -1,23 +0,0 @@
#pragma once
#include "ui_element.h"
namespace recompui {
class Clickable : public Element {
protected:
std::vector<std::function<void(float, float)>> clicked_callbacks;
std::vector<std::function<void(float, float)>> pressed_callbacks;
std::vector<std::function<void(float, float, DragPhase)>> dragged_callbacks;
// Element overrides.
virtual void process_event(const Event &e) override;
std::string_view get_type_name() override { return "Clickable"; }
public:
Clickable(Element *parent, bool draggable = false);
void add_clicked_callback(std::function<void(float, float)> callback);
void add_pressed_callback(std::function<void(float, float)> callback);
void add_dragged_callback(std::function<void(float, float, DragPhase)> callback);
};
} // namespace recompui
-13
View File
@@ -1,13 +0,0 @@
#include "ui_container.h"
#include <cassert>
namespace recompui {
Container::Container(Element *parent, FlexDirection direction, JustifyContent justify_content, uint32_t events_enabled) : Element(parent, events_enabled) {
set_display(Display::Flex);
set_flex_direction(direction);
set_justify_content(justify_content);
}
};

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