Compare commits

..

86 Commits

Author SHA1 Message Date
Martin Prokoph
d668707300 fix: depend on SpruceUI instead of ObsidianUI 2025-10-30 22:12:45 +01:00
Martin Prokoph
dac5e55ee1 release: v1.11.3-beta.2 2025-10-29 15:16:27 +01:00
Martin Prokoph
6b89dbf8e5 feat: back to SpruceUI!
- SpruceUI is now also available for NeoForge, so I can ditch ObsidianUI and move back to an actively maintained UI library :)
2025-10-03 18:47:27 +02:00
Martin Prokoph
faf24ced17 feat: improve options screen with tooltips & better reset 2025-10-02 17:24:25 +02:00
Martin Prokoph
d7ea484e71 clean: remove double tap to sprint option
Now configurable in vanilla
2025-10-02 17:23:00 +02:00
Martin Prokoph
e24ecdc78b config: reduce default deadzones 2025-10-02 16:24:15 +02:00
Martin Prokoph
66874b7164 feat: fancy input mode icons 2025-10-02 16:22:47 +02:00
Martin Prokoph
238ea583a2 feat: re-order config class 2025-10-02 16:22:22 +02:00
Martin Prokoph
b3b3eb4d55 feat: switch to new MidnightLib-based screen
What used to be the advanced config screen is now the one-and-only default config screen
2025-10-02 16:21:48 +02:00
Martin Prokoph
c2cab16989 fix: controller mouse click simulation in screens 2025-10-02 00:13:40 +02:00
Martin Prokoph
f2372cf406 release: 1.11.3-alpha.2 2025-10-01 17:07:27 +02:00
Martin Prokoph
ffa1bb5a33 fix: get debug hud toggle working correctly 2025-10-01 17:06:20 +02:00
Martin Prokoph
1d9a4287d7 fix: touchscreen interaction finger outline 2025-10-01 16:55:55 +02:00
Martin Prokoph
b3a170f862 fix: server list moving entries instead of cycling 2025-10-01 16:55:09 +02:00
Martin Prokoph
15e1b08e8f fix: attack key not working properly for mobs 2025-10-01 16:54:24 +02:00
Martin Prokoph
7660f6b0ee release: 1.11.3-alpha.1 2025-10-01 14:14:20 +02:00
Martin Prokoph
5c1657aef2 feat: more mouse cursors 🐁 2025-10-01 14:13:47 +02:00
Martin Prokoph
830ac38b02 fix: button category names 2025-10-01 14:12:12 +02:00
Martin Prokoph
4d85879c87 feat: add more functionality to MidnightLib-based config
- The plan is to slowly phase out ObsidianUI, as it is not updated fast enough
2025-10-01 14:11:47 +02:00
Martin Prokoph
60f5142796 fix: correctly register keybind category
This drastically changed in 1.21.9
2025-09-28 13:51:55 +02:00
Martin Prokoph
b8ee08bf81 feat: support more mouse cursor styles 2025-09-28 13:46:09 +02:00
Martin Prokoph
db7f1daa02 port: The Copper Age (1.21.9) 2025-09-27 23:15:20 +02:00
Martin Prokoph
e50128d75d chore: bump version 2025-09-27 21:52:37 +02:00
Martin Prokoph
837ead55fb build: switch to official ObsidianUI version
- Previously, I used my own build to achieve 1.21.6 compatibility
2025-09-27 21:46:26 +02:00
Martin Prokoph
c00d5893e9 Merge pull request #365 from FugLong/fix/macosLaunchCrash
fix: resolve ConcurrentModificationException in InputManager.updateBindings
2025-09-27 21:43:58 +02:00
Elijah Stephenson
af4b40e88a fix: resolve ConcurrentModificationException in InputManager.updateBindings
- Make BINDINGS list thread-safe using Collections.synchronizedList()
- Add synchronized blocks around all BINDINGS access methods
- Prevents crash on macOS clients during launch
- Fixes race condition between main thread and controller input thread
2025-09-26 20:20:51 -05:00
Martin Prokoph
994cd0d155 build: use ObsidianUI from MidnightDust maven 2025-06-21 09:30:11 +02:00
Martin Prokoph
9e4686be32 fix: get book working again with virtualkeyboard 2025-06-21 09:29:37 +02:00
Martin Prokoph
b472503ec4 port: Chase the Skies (1.21.6)
- Needs my ObsidianUI PR
- Virtual keyboard currently does not work in book edit screen
2025-06-17 23:33:43 +02:00
Martin Prokoph
54a43d41c0 chore: bump version 2025-05-19 23:31:20 +02:00
Martin Prokoph
c86df8714c fix: crash on NeoForge 1.21.5 2025-05-19 23:30:06 +02:00
Martin Prokoph
ccad5e1d44 clean: remove GlStateManager calls 2025-05-19 23:14:17 +02:00
Martin Prokoph
58fa3b5bc1 Merge pull request #350 from TeamMidnightDust/feat/virtual-keyboard
feat: virtual keyboard support!
2025-05-19 23:12:39 +02:00
Martin Prokoph
0f407ac245 feat: edit signs using the virtual keyboard! 2025-05-19 22:49:26 +02:00
Martin Prokoph
7375e5ad20 feat: add reset button icon 2025-05-19 20:28:06 +02:00
Martin Prokoph
7b723513ae feat: easy switching between virtual keyboard layouts 2025-05-19 19:18:18 +02:00
Martin Prokoph
0dfd1994dc feat: data-driven virtual keyboard layouts 2025-05-19 16:20:46 +02:00
Martin Prokoph
ecb7cfd888 feat: load virtual keyboard layouts from JSON 2025-05-10 10:38:54 +02:00
Martin Prokoph
18e69c1cb3 Merge pull request #343 from cryy/feat/virtual-keyboard
Feat: Virtual keyboard support
2025-05-08 09:53:58 +02:00
Martin Prokoph
f5be8eb14e Merge branch 'feat/virtual-keyboard' into feat/virtual-keyboard 2025-05-08 09:53:33 +02:00
Martin Prokoph
4a502730bf Merge pull request #338 from muznyo/cz-lang
Add czech language
2025-05-08 09:46:14 +02:00
Martin Prokoph
f263908f5f fix: multiple touch-related bug fixes 2025-05-08 00:08:27 +02:00
Martin Prokoph
28de7312f8 fix: get touchscreen controls working again 2025-05-07 15:08:52 +02:00
cryy
1231c231a9 Fix virtual keyboard config 2025-04-22 23:57:53 +02:00
cryy
24e58027b2 Add obisidianUI text field interop 2025-04-22 23:57:31 +02:00
cryy
33845e111b Fix handler naming 2025-04-22 23:08:48 +02:00
cryy
86622962f8 Fix tooltip entry in en_us locale 2025-04-22 23:05:02 +02:00
cryy
73c5fe1a82 Add virtual keyboard config option 2025-04-22 23:03:07 +02:00
cryy
70d923f959 Implement NeoForge support 2025-04-22 22:52:57 +02:00
cryy
0ef59057af Add missing row width calculation 2025-04-22 20:21:02 +02:00
cryy
9ebd1a9cea Increase displayed lines for newline support 2025-04-22 20:17:25 +02:00
cryy
bb5c6976c0 Replace KeyInfo with String
- KeyInfo was leftover code from toying around with various implementations. String is sufficient in current implementation.
2025-04-22 20:16:08 +02:00
cryy
041eeb29aa Make special key constant private 2025-04-22 19:39:56 +02:00
cryy
662bac3053 Finalize virtual keyboard screen
- Added backspace support
- Added space support
- Added new line support
- Added caps support
- Added symbol support
2025-04-22 19:36:26 +02:00
cryy
50103ce4cf Move cursor to end of text in book 2025-04-20 19:53:26 +02:00
cryy
9e12381471 Implement basic virtual keyboard support
- Listener for clicks inside of text fields and other text-based screens
- Virtual keyboard screen in a QWERTY layout
2025-04-18 19:43:01 +02:00
Ondřej Mužný
945b01a8d6 remove char 2025-03-29 12:02:39 +01:00
Ondřej Mužný
8aa006429d Added czech language 2025-03-29 11:59:47 +01:00
Martin Prokoph
114a72cdde chore: bump version 2025-03-29 00:59:43 +01:00
Martin Prokoph
25e9c9afe3 feat: improve advanced config screen
- Thanks to MidnightLib 1.7.0 :)
2025-03-29 00:58:41 +01:00
Martin Prokoph
d313115388 Merge pull request #327 from Kichura/ge_fun
Gradle 8.13, Change artifact path
2025-03-27 23:33:26 +01:00
Martin Prokoph
e46e855961 Merge branch 'architectury-1.21.5' into ge_fun 2025-03-27 21:22:50 +01:00
Martin Prokoph
38229bc827 port: Spring to Life (1.21.5)
- Still waiting for ObsidianUI to get the settings screen working. Guess I'm gonna have to port that myself again.
2025-03-27 16:10:03 +01:00
Martin Prokoph
f004f0a32d compat: improve/fix Emotecraft compat 2025-03-27 00:00:29 +01:00
Martin Prokoph
c07f3d94dd chore: bump version 2025-03-26 19:54:42 +01:00
Martin Prokoph
6007ef315d fix: controller options button not being clickable 2025-03-26 19:53:31 +01:00
Kichura
1e2dd30e3a Gradle 8.13. 2025-02-25 18:56:00 +01:00
Martin Prokoph
1c26eeed5e fix: catch errors in camera thread
- Solves camera movement breaking entirely when encountering an error
2025-02-15 22:20:29 +01:00
Kichura
f14387b6b4 Gradle 8.12.1. 2025-01-30 02:10:20 +01:00
Kichura
1344a949cd Do not persist credentials. 2025-01-07 22:57:18 +01:00
Kichura
65a0b1cf1f Gradle 8.12, Migrate to setup-gradle. 2025-01-06 23:43:22 +01:00
Kichura
23f7b2e199 Change artifacts path. 2025-01-06 21:16:18 +01:00
Martin Prokoph
e35850c5d5 Bump version 2024-12-11 18:10:57 +01:00
Martin Prokoph
78900ac83e Fix movement while riding boats and other entities 2024-12-11 18:08:40 +01:00
Martin Prokoph
7d791fac89 Port to 1.21.4
- For once, this has been easy :)
2024-12-06 21:11:39 +01:00
Martin Prokoph
bf60595c12 Merge pull request #322 from TeamMidnightDust/architectury-1.21
Sync translations
2024-12-06 21:02:04 +01:00
Martin Prokoph
b96547dafb Merge pull request #319 from FlocosDEV/architectury-1.21
Create pt-br.json
2024-12-06 21:00:56 +01:00
Martin Prokoph
ab869f4f76 First and final 1.21.3 release
- Moving on to 1.21.4
2024-12-06 20:53:14 +01:00
Martin Prokoph
84df412162 Merge pull request #321 from TeamMidnightDust/architectury-1.21
Add @Kichura 's workflow improvements to 1.21.3 branch
2024-12-05 22:10:05 +01:00
Martin Prokoph
afb80fd89c Merge pull request #316 from Kichura/architectury-1.21
Replace Java 16/17 -> 21, Upgrade workflow system
2024-12-04 13:44:12 +01:00
Gabriel
b835a6c4ca Create pt-br.json
Create pt-br.json
2024-11-19 21:36:33 -03:00
Martin Prokoph
3eec0dda4a Update ObsidianUI to my 1.21.3 port 2024-11-18 14:16:44 +01:00
Kichura
35ab81f696 Apply "chmod +x" to gradlew. 2024-10-30 19:44:35 +01:00
Kichura
e08547a641 Ensure fabric requests end-user to use Java 21 instead. 2024-10-29 23:02:21 +01:00
Kichura
c93cc729f4 Upgrade the entire workflow system. 2024-10-29 22:57:47 +01:00
Kichura
1595fffc2c Change Java version reference to 21. 2024-10-29 22:52:02 +01:00
117 changed files with 3030 additions and 853 deletions

29
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Build with Gradle
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
check-latest: true
- name: Build with Gradle
run: ./gradlew build
- name: Upload artifacts to GitHub
uses: actions/upload-artifact@v4
with:
name: MidnightControls-Artifacts
path: |
fabric/build/libs/
neoforge/build/libs/

View File

@@ -1,23 +0,0 @@
name: Gradle Build
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 16
uses: actions/setup-java@v1
with:
java-version: 16
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Gradle
run: ./gradlew build
- uses: actions/upload-artifact@v2
with:
name: Artifacts
path: ./build/libs/

View File

@@ -1,23 +0,0 @@
name: Gradle Build
on:
release:
types:
- published
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 16
uses: actions/setup-java@v1
with:
java-version: 16
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Gradle
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew publishModrinth

40
.github/workflows/gradlepublish.yml → .github/workflows/publish.yml vendored Executable file → Normal file
View File

@@ -1,30 +1,26 @@
name: Gradle Package
name: Publish to Modrinth
on:
push:
tags:
- '*'
release:
types:
- published
jobs:
build:
runs-on: ubuntu-latest
publish:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- name: Set up JDK 16
uses: actions/setup-java@v1
- name: Checkout Repository
uses: actions/checkout@v4
with:
java-version: 16
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Gradle
run: ./gradlew build
- uses: actions/upload-artifact@v2
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
name: Artifacts
path: ./build/libs/
distribution: 'temurin'
java-version: 21
check-latest: true
# The USERNAME and PASSWORD need to correspond to the credentials environment variables used in
# the publishing section of your build.gradle
- name: Publish to GitHub Packages and other Mavens
@@ -38,3 +34,7 @@ jobs:
midnightcontrols_MAVEN: ${{ secrets.MAVEN_URL }}
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
- name: Publish to Modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew publishModrinth

View File

@@ -8,7 +8,7 @@
[![Version](https://img.shields.io/github/v/tag/TeamMidnightDust/MidnightControls?label=version&style=round)](https://github.com/TeamMidnightDust/MidnightControls/releases)
![Environment: Client](https://img.shields.io/badge/environment-client-1976d2?style=round)
[![Mod loader: Quilt/Fabric]][Quilt]
[![Java 17](https://img.shields.io/badge/language-Java%2017-9B599A.svg?style=round)](https://www.oracle.com/java/technologies/downloads/#java17)
[![Java 21](https://img.shields.io/badge/language-Java%2021-9B599A.svg?style=round)](https://www.oracle.com/java/technologies/downloads/#java21)
[![GitHub license](https://img.shields.io/github/license/TeamMidnightDust/MidnightControls?style=round)](LICENSE)
A Fabric Minecraft mod which adds better controls, reach-around and controller support.

View File

@@ -1,6 +1,6 @@
plugins {
id "architectury-plugin" version "3.4-SNAPSHOT"
id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
id "dev.architectury.loom" version "1.11-SNAPSHOT" apply false
id "me.shedaniel.unified-publishing" version "0.1.+" apply false
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
}
@@ -13,6 +13,9 @@ repositories {
maven {
url = "https://api.modrinth.com/maven"
}
flatDir {
dirs("localMaven")
}
}
subprojects {
@@ -21,7 +24,14 @@ subprojects {
maven {
url = "https://api.modrinth.com/maven"
}
maven {
name 'Gegy'
url 'https://maven.gegy.dev'
}
maven { url 'https://jitpack.io' }
flatDir {
dirs("../localMaven")
}
}
dependencies {

View File

@@ -29,7 +29,7 @@ dependencies {
// Using the Fabric version of midnightlib here to create a common config and get useful utilities
// Just make sure NOT to use classes from the .fabric classpath
modCompileOnlyApi "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric"
modCompileOnlyApi "maven.modrinth:obsidianui:${rootProject.obsidianui_version}-fabric"
modCompileOnlyApi "dev.lambdaurora:spruceui:${project.spruceui_version}"
modCompileOnlyApi ("com.terraformersmc:modmenu:${project.modmenu_version}") {
exclude(group: "net.fabricmc.fabric-api")
}
@@ -39,7 +39,7 @@ dependencies {
modCompileOnlyApi "org.quiltmc:quilt-json5:1.0.0"
modImplementation "maven.modrinth:sodium:${project.sodium_version}-fabric"
modCompileOnlyApi "maven.modrinth:emi:${project.emi_version}"
modCompileOnlyApi "maven.modrinth:emotecraft:${project.emotecraft_version}"
modImplementation "maven.modrinth:emotecraft:${project.emotecraft_version}"
modCompileOnlyApi "io.github.kosmx:bendy-lib:${project.bendylib_version}"
modCompileOnlyApi "dev.isxander:yet-another-config-lib:${project.yacl_version}"
modCompileOnlyApi "maven.modrinth:inventory-tabs-updated:${project.inventorytabs_version}"

View File

@@ -9,12 +9,17 @@
package eu.midnightdust.midnightcontrols;
import net.minecraft.text.Text;
import net.minecraft.text.object.AtlasTextObjectContents;
import net.minecraft.util.Atlases;
import net.minecraft.util.TranslatableOption;
import org.jetbrains.annotations.NotNull;
import org.thinkingstudio.obsidianui.util.Nameable;
import java.util.Arrays;
import java.util.Optional;
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
/**
* Represents the controls mode.
*
@@ -22,10 +27,15 @@ import java.util.Optional;
* @version 1.7.0
* @since 1.0.0
*/
public enum ControlsMode {
DEFAULT,
CONTROLLER,
TOUCHSCREEN;
public enum ControlsMode implements TranslatableOption {
DEFAULT("icon/keyboard_mouse"),
CONTROLLER("icon/controller"),
TOUCHSCREEN("icon/touchscreen");
final String emoji;
ControlsMode(String emoji) {
this.emoji = emoji;
}
/**
* Returns the next controls mode available.
@@ -39,6 +49,16 @@ public enum ControlsMode {
return v[this.ordinal() + 1];
}
@Override
public int getId() {
return this.ordinal();
}
@Override
public Text getText() {
return Text.object(new AtlasTextObjectContents(Atlases.GUI, id(emoji))).append(" ").append(Text.translatable(getTranslationKey()));
}
/**
* Gets the translation key of this controls mode.
*
@@ -49,6 +69,7 @@ public enum ControlsMode {
return "midnightcontrols.controls_mode." + this.getName();
}
public @NotNull String getName() {
return this.name().toLowerCase();
}

View File

@@ -9,8 +9,8 @@
package eu.midnightdust.midnightcontrols;
import org.thinkingstudio.obsidianui.util.Nameable;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;

View File

@@ -26,8 +26,8 @@ import eu.midnightdust.midnightcontrols.client.mixin.KeyBindingIDAccessor;
import eu.midnightdust.midnightcontrols.client.ring.ButtonBindingRingAction;
import eu.midnightdust.midnightcontrols.client.ring.MidnightRing;
import eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.MouseClickInterceptor;
import net.minecraft.client.gui.screen.Screen;
import org.thinkingstudio.obsidianui.hud.HudManager;
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import eu.midnightdust.midnightcontrols.client.util.RainbowColor;
import eu.midnightdust.midnightcontrols.packet.ControlsModePayload;
@@ -55,48 +55,52 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class MidnightControlsClient extends MidnightControls {
public static boolean lateInitDone = false;
public static final KeyBinding.Category MIDNIGHTCONTROLS_CATEGORY = KeyBinding.Category.create(Identifier.of("midnightcontrols", "keybinds"));
public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(id("look_up"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.midnightcontrols");
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, MIDNIGHTCONTROLS_CATEGORY);
public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(id("look_right"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.midnightcontrols");
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, MIDNIGHTCONTROLS_CATEGORY);
public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(id("look_down"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.midnightcontrols");
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, MIDNIGHTCONTROLS_CATEGORY);
public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(id("look_left"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.midnightcontrols");
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, MIDNIGHTCONTROLS_CATEGORY);
public static final KeyBinding BINDING_RING = InputManager.makeKeyBinding(id("ring"),
InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), "key.categories.midnightcontrols");
InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), MIDNIGHTCONTROLS_CATEGORY);
public static final Identifier CONTROLLER_BUTTONS = id("textures/gui/controller_buttons.png");
public static final Identifier CONTROLLER_EXPANDED = id("textures/gui/controller_expanded.png");
public static final Identifier CONTROLLER_AXIS = id("textures/gui/controller_axis.png");
public static final Identifier WAYLAND_CURSOR_TEXTURE_LIGHT = id("cursor/light/mouse_pointer");
public static final Identifier WAYLAND_CURSOR_TEXTURE_DARK = id("cursor/dark/mouse_pointer");
public static final File MAPPINGS_FILE = new File("config/gamecontrollercustommappings.txt");
public static final MinecraftClient client = MinecraftClient.getInstance();
public static MinecraftClient client = MinecraftClient.getInstance();
public static final MidnightInput input = new MidnightInput();
public static final MidnightRing ring = new MidnightRing();
public static final MidnightReacharound reacharound = new MidnightReacharound();
public static final MouseClickInterceptor clickInterceptor = new MouseClickInterceptor();
public static boolean isWayland;
private static MidnightControlsHud hud;
private static ControlsMode previousControlsMode;
public static void initClient() {
client = MinecraftClient.getInstance();
ring.registerAction("buttonbinding", ButtonBindingRingAction.FACTORY);
int delay = 0; // delay for 0 sec.
int period = 1; // repeat every 0.001 sec. (1000 times a second)
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() { // TODO: Add a try/catch here after the alpha testing period
public void run() {
try {
if (lateInitDone && client.isRunning()) {
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && (client.isWindowFocused() || MidnightControlsConfig.unfocusedInput)) {
input.tickCameraStick();
if (MidnightControlsConfig.controlsMode != ControlsMode.DEFAULT && (client.isWindowFocused() || MidnightControlsConfig.unfocusedInput)) {
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) input.tickCameraStick();
input.updateCamera();
}
}
} catch (Exception | Error e) {
MidnightControls.logger.error("Exception encountered in camera loop: ",e);
}
}
}, delay, period);
HudManager.register(hud = new MidnightControlsHud());
isWayland = GLFW.glfwGetVersionString().contains("Wayland");
}
@@ -114,7 +118,7 @@ public class MidnightControlsClient extends MidnightControls {
MidnightControlsConfig.configVersion = 2;
MidnightControlsConfig.write(MidnightControlsConstants.NAMESPACE);
}
hud.setVisible(MidnightControlsConfig.hudEnable);
MidnightControlsHud.isVisible = MidnightControlsConfig.hudEnable;
Controller.updateMappings();
try {
GLFW.glfwSetJoystickCallback((jid, event) -> {
@@ -143,20 +147,20 @@ public class MidnightControlsClient extends MidnightControls {
if (PlatformFunctions.isModLoaded("wynntils") && KeyBindingIDAccessor.getKEYS_BY_ID().entrySet().stream().noneMatch(b -> Objects.equals(b.getValue().getCategory(), "Wynntils"))) return;
for (int i = 0; i < KeyBindingIDAccessor.getKEYS_BY_ID().size(); ++i) {
KeyBinding keyBinding = KeyBindingIDAccessor.getKEYS_BY_ID().entrySet().stream().toList().get(i).getValue();
if (MidnightControlsConfig.excludedKeybindings.stream().noneMatch(excluded -> keyBinding.getTranslationKey().startsWith(excluded))) {
if (!keyBinding.getTranslationKey().contains(MidnightControlsConstants.NAMESPACE)) {
if (MidnightControlsConfig.excludedKeybindings.stream().noneMatch(excluded -> keyBinding.getId().startsWith(excluded))) {
if (!keyBinding.getId().contains(MidnightControlsConstants.NAMESPACE)) {
AtomicReference<ButtonCategory> category = new AtomicReference<>();
InputManager.streamCategories().forEach(buttonCategory -> {
if (buttonCategory.getIdentifier().equals(validVanillaId(keyBinding.getCategory())))
if (buttonCategory.getIdentifier().equals(keyBinding.getCategory().id()))
category.set(buttonCategory);
});
if (category.get() == null) {
category.set(new ButtonCategory(validVanillaId(keyBinding.getCategory())));
category.set(new ButtonCategory(keyBinding.getCategory().id()));
InputManager.registerCategory(category.get());
}
ButtonBinding buttonBinding = new ButtonBinding.Builder(keyBinding.getTranslationKey()).category(category.get()).linkKeybind(keyBinding).register();
ButtonBinding buttonBinding = new ButtonBinding.Builder(keyBinding.getId()).category(category.get()).linkKeybind(keyBinding).register();
if (MidnightControlsConfig.debug) {
MidnightControls.log(keyBinding.getTranslationKey());
MidnightControls.log(keyBinding.getId());
MidnightControls.log(String.valueOf(buttonBinding));
}
}
@@ -165,14 +169,6 @@ public class MidnightControlsClient extends MidnightControls {
InputManager.loadButtonBindings();
lateInitDone = true;
}
private static Identifier validVanillaId(String path) {
for(int i = 0; i < path.length(); ++i) {
if (!Identifier.isPathCharacterValid(path.charAt(i))) {
path = path.replace(path.charAt(i), '_');
}
}
return Identifier.ofVanilla(path);
}
/**
* This method is called every Minecraft tick.
@@ -200,10 +196,12 @@ public class MidnightControlsClient extends MidnightControls {
RainbowColor.tick();
TouchInput.tick();
}
/**
* Called when opening a screen.
*/
public static void onScreenOpen(Screen screen) {
client = MinecraftClient.getInstance();
if (screen == null && MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN) {
screen = new TouchscreenOverlay();
screen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
@@ -247,7 +245,7 @@ public class MidnightControlsClient extends MidnightControls {
*/
public static void setHudEnabled(boolean enabled) {
MidnightControlsConfig.hudEnable = enabled;
hud.setVisible(enabled);
MidnightControlsHud.isVisible = enabled;
}
private static final MidnightControlsClient INSTANCE = new MidnightControlsClient();

View File

@@ -11,13 +11,17 @@ package eu.midnightdust.midnightcontrols.client;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mojang.blaze3d.platform.GlDebugInfo;
import com.mojang.blaze3d.platform.GLX;
import eu.midnightdust.lib.config.EntryInfo;
import eu.midnightdust.lib.config.MidnightConfig;
import eu.midnightdust.lib.config.MidnightConfigListWidget;
import eu.midnightdust.lib.config.MidnightConfigScreen;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
@@ -25,18 +29,29 @@ import eu.midnightdust.midnightcontrols.client.enums.CameraMode;
import eu.midnightdust.midnightcontrols.client.enums.ControllerType;
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
import eu.midnightdust.midnightcontrols.client.enums.VirtualMouseSkin;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
import eu.midnightdust.midnightcontrols.client.gui.config.ControllerBindingButton;
import eu.midnightdust.midnightcontrols.client.gui.config.ControllerSelectionButton;
import eu.midnightdust.midnightcontrols.client.gui.config.MappingsStringInputWidget;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.KeyboardLayoutManager;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.regex.Pattern;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
import static org.lwjgl.glfw.GLFW.*;
/**
@@ -49,86 +64,199 @@ public class MidnightControlsConfig extends MidnightConfig {
public static final String SCREENS = "screens";
public static final String VISUAL = "visual";
public static final String MISC = "misc";
public static final String BUTTONS = "buttons";
//public static final String MAPPING = "mapping";
public static boolean isEditing = false;
@Hidden @Entry public static int configVersion = 2;
// General
// Controller
@Entry(category = CONTROLLER, name = "Controller ID") @Hidden public static Object controllerID = 0;
@Entry(category = CONTROLLER, name = "2nd Controller ID") @Hidden public static Object secondControllerID = -1;
@Comment(category = CONTROLLER, centered = true, name="\uD83D\uDD90 Input Mode") public static Comment _input_mode;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.controls_mode") public static ControlsMode controlsMode = ControlsMode.DEFAULT;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.auto_switch_mode") public static boolean autoSwitchMode = true;
@Entry(category = MISC, name = "Debug") public static boolean debug = false;
// HUD
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_enable") public static boolean hudEnable = true;
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_side") public static HudSide hudSide = HudSide.LEFT;
@Entry(category = SCREENS, name = "midnightcontrols.menu.move_chat") public static boolean moveChat = false;
// Gameplay
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.analog_movement") public static boolean analogMovement = true;
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.double_tap_to_sprint") public static boolean doubleTapToSprint = true;
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sneak") public static boolean controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sprint") public static boolean controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fast_block_placing") public static boolean fastBlockPlacing = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fly_drifting") public static boolean flyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers. It can also conflict with some other mods.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fly_drifting_vertical") public static boolean verticalFlyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.horizontal") public static boolean horizontalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.vertical") public static boolean verticalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Entry(category = VISUAL, name = "Reacharound Outline") public static boolean shouldRenderReacharoundOutline = true;
@Entry(category = VISUAL, name = "Reacharound Outline Color", isColor = true) public static String reacharoundOutlineColorHex = "#ffffff";
@Entry(category = VISUAL, name = "Reacharound Outline Alpha", isSlider = true, min = 0, max = 255) public static int reacharoundOutlineColorAlpha = 102;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.right_dead_zone", isSlider = true, min = 0.05, max = 1) public static double rightDeadZone = 0.25;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.left_dead_zone", isSlider = true, min = 0.05, max = 1) public static double leftDeadZone = 0.25;
@Entry(category = CONTROLLER, name = "Trigger Dead-Zone", isSlider = true, min = 0.05, max = 1) public static double triggerDeadZone = 0.1;
@Comment(category = CONTROLLER, centered = true, name="\uD83D\uDCF7 Camera Settings") public static Comment _cameraSettings;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.invert_right_y_axis") public static boolean invertRightYAxis = false;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.invert_right_x_axis") public static boolean invertRightXAxis = false;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.rotation_speed", isSlider = true, min = 0, max = 100, precision = 10) public static double rotationSpeed = 35.0; //used for x-axis, name kept for compatibility
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.y_axis_rotation_speed", isSlider = true, min = 0, max = 100, precision = 10) public static double yAxisRotationSpeed = rotationSpeed;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.camera_mode") public static CameraMode cameraMode = CameraMode.FLAT;
@Comment(category = CONTROLLER, centered = true, name="\uD83D\uDC40 Eye Tracking") public static Comment _eyeTracker;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.eye_tracker_as_mouse") public static boolean eyeTrackerAsMouse = false;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.eye_tracker_deadzone", isSlider = true, min = 0, max = 0.4) public static double eyeTrackerDeadzone = 0.05;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.unfocused_input") public static boolean unfocusedInput = false;
@Comment(category = CONTROLLER, centered = true, name="\uD83D\uDD79 Max Analog Stick Values") public static Comment _maxAnalogValues;
@Entry(category = CONTROLLER, name = "Max analog value: Left X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftX = 1;
@Entry(category = CONTROLLER, name = "Max analog value: Left Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftY = 1;
@Entry(category = CONTROLLER, name = "Max analog value: Right X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightX = 1;
@Entry(category = CONTROLLER, name = "Max analog value: Right Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightY = 1;
@Comment(category = CONTROLLER, centered = true, name="☠ Dead Zones") public static Comment _deadZones;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.right_dead_zone", isSlider = true, min = 0.05, max = 1) public static double rightDeadZone = 0.15;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.left_dead_zone", isSlider = true, min = 0.05, max = 1) public static double leftDeadZone = 0.15;
@Entry(category = CONTROLLER, name = "Trigger Dead-Zone", isSlider = true, min = 0.05, max = 1) public static double triggerDeadZone = 0.1;
// Init button binding tab (see #onTabInit())
@Comment(category = BUTTONS) @Condition(requiredModId = "thisModDoesNotExist") public static Comment this_spacer_will_never_be_visible;
@Entry @Hidden public static Map<String, String> BINDING = new HashMap<>();
private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?");
// Visual
@Comment(category = VISUAL, centered = true, name="\uD83D\uDDB9 Hud") public static Comment _hud;
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_enable") public static boolean hudEnable = true;
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_side") public static HudSide hudSide = HudSide.LEFT;
@Entry(category = VISUAL, name = "midnightcontrols.menu.controller_type") public static ControllerType controllerType = ControllerType.DEFAULT;
@Comment(category = VISUAL, centered = true, name="⊽ Reacharound") public static Comment _reacharoundOutline;
@Condition(requiredModId = "midnightcontrols-extra")
@Entry(category = VISUAL, name = "Reacharound Outline") public static boolean shouldRenderReacharoundOutline = true;
@Condition(requiredModId = "midnightcontrols-extra", requiredOption = "shouldRenderReacharoundOutline")
@Entry(category = VISUAL, name = "Reacharound Outline Color", isColor = true) public static String reacharoundOutlineColorHex = "#ffffff";
@Condition(requiredModId = "midnightcontrols-extra", requiredOption = "shouldRenderReacharoundOutline")
@Entry(category = VISUAL, name = "Reacharound Outline Alpha", isSlider = true, min = 0, max = 255) public static int reacharoundOutlineColorAlpha = 102;
// Gameplay
@Comment(category = GAMEPLAY, centered = true, name="\uD83D\uDECB Comfort") public static Comment _comfort;
@Entry(category = GAMEPLAY, name = "Enable Hints") public static boolean enableHints = true;
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.analog_movement") public static boolean analogMovement = true;
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sneak") public static boolean controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sprint") public static boolean controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
@Condition(requiredModId = "midnightcontrols-extra")
@Comment(category = GAMEPLAY, centered = true, name="✨ Extras") public static Comment _extras;
@Condition(requiredModId = "midnightcontrols-extra")
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fast_block_placing") public static boolean fastBlockPlacing = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Condition(requiredModId = "midnightcontrols-extra")
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fly_drifting") public static boolean flyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers. It can also conflict with some other mods.
@Condition(requiredModId = "midnightcontrols-extra")
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fly_drifting_vertical") public static boolean verticalFlyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers.
@Condition(requiredModId = "midnightcontrols-extra")
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.horizontal") public static boolean horizontalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Condition(requiredModId = "midnightcontrols-extra")
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.vertical") public static boolean verticalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Condition(requiredModId = "midnightcontrols-extra")
// Screens
@Comment(category = SCREENS, centered = true, name="\uD83D\uDDB1 Mouse Behaviour") public static Comment _mouseBehaviour;
@Entry(category = SCREENS, name = "midnightcontrols.menu.mouse_speed", isSlider = true, min = 0, max = 150, precision = 10) public static double mouseSpeed = 25.0;
@Entry(category = SCREENS, name = "midnightcontrols.menu.joystick_as_mouse") public static boolean joystickAsMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.eye_tracker_as_mouse") public static boolean eyeTrackerAsMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.eye_tracker_deadzone", isSlider = true, min = 0, max = 0.4) public static double eyeTrackerDeadzone = 0.05;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.unfocused_input") public static boolean unfocusedInput = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse") public static boolean virtualMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse.skin") public static VirtualMouseSkin virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
@Entry(category = SCREENS, name = "midnightcontrols.menu.hide_cursor") public static boolean hideNormalMouse = false;
@Entry(category = CONTROLLER, name = "Controller ID") @Hidden public static Object controllerID = 0;
@Entry(category = CONTROLLER, name = "2nd Controller ID") @Hidden public static Object secondControllerID = -1;
@Entry(category = VISUAL, name = "midnightcontrols.menu.controller_type") public static ControllerType controllerType = ControllerType.DEFAULT;
@Entry(category = SCREENS, name = "Mouse screens") public static List<String> mouseScreens = Lists.newArrayList("net.minecraft.client.gui.screen.advancement",
"net.minecraft.class_457", "net.minecraft.class_408", "net.minecraft.class_3872", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen",
"hardcorequesting.client.interfaces.GuiQuestBook", "hardcorequesting.client.interfaces.GuiReward", "hardcorequesting.client.interfaces.EditTrackerScreen",
"me.shedaniel.clothconfig2.gui.ClothConfigScreen", "com.mamiyaotaru.voxelmap.gui.GuiWaypoints", "com.mamiyaotaru.voxelmap.gui.GuiPersistentMap");
@Entry(category = SCREENS, name = "Arrow screens") public static List<String> arrowScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName());
@Entry(category = SCREENS, name = "WASD screens") public static List<String> wasdScreens = Lists.newArrayList("com.ultreon.devices.core.Laptop");
@Entry(category = TOUCH, name = "Screens with close button") public static List<String> closeButtonScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName(), AdvancementsScreen.class.getCanonicalName(), RingScreen.class.getCanonicalName());
@Comment(category = SCREENS, centered = true, name="\uD83D\uDC46 Virtual Mouse") public static Comment _virtualMouse;
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse") public static boolean virtualMouse = false;
@Condition(requiredOption = "virtualMouse", visibleButLocked = true)
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse.skin") public static VirtualMouseSkin virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
@Entry(category = SCREENS, name = "midnightcontrols.menu.hide_cursor") public static boolean hideNormalMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_keyboard") public static boolean virtualKeyboard = false;
@Comment(category = SCREENS, centered = true, name="\uD83D\uDD27 UI Modifications") public static Comment _uiMods;
@Entry(category = SCREENS, name = "midnightcontrols.menu.move_chat") public static boolean moveChat = false;
@Entry(category = SCREENS, name = "Enable Shortcut in Controls Options") public static boolean shortcutInControls = true;
// Touch
@Comment(category = TOUCH, centered = true, name="\uD83E\uDE84 Behaviour") public static Comment _touchBehaviour;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_with_controller") public static boolean touchInControllerMode = false;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_speed", isSlider = true, min = 0, max = 150, precision = 10) public static double touchSpeed = 50.0;
@Entry(category = TOUCH, name = "midnightcontrols.menu.invert_touch") public static boolean invertTouch = false;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_mode") public static TouchMode touchMode = TouchMode.CROSSHAIR;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_break_delay", isSlider = true, min = 50, max = 500) public static int touchBreakDelay = 120;
@Comment(category = TOUCH, centered = true, name="\uD83D\uDCA1 Visuals") public static Comment _visuals;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_transparency", isSlider = true, min = 0, max = 100) public static int touchTransparency = 75;
@Entry(category = TOUCH, name = "Touch Outline Color", isColor = true) public static String touchOutlineColorHex = "#ffffff";
@Entry(category = TOUCH, name = "Touch Outline Alpha", isSlider = true, min = 0, max = 255) public static int touchOutlineColorAlpha = 150;
@Comment(category = TOUCH, centered = true, name="\uD83E\uDDEA Advanced") public static Comment _advanced;
@Entry(category = TOUCH, name = "Screens with close button") public static List<String> closeButtonScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName(), AdvancementsScreen.class.getCanonicalName(), RingScreen.class.getCanonicalName());
@Entry(category = TOUCH, name = "Left Touch button bindings") public static List<String> leftTouchBinds = Lists.newArrayList("debug_screen", "screenshot","toggle_perspective");
@Entry(category = TOUCH, name = "Right Touch button bindings") public static List<String> rightTouchBinds = Lists.newArrayList("screenshot","toggle_perspective", "use");
@Entry @Hidden public static Map<String, String> BINDING = new HashMap<>();
private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?");
@Entry(category = CONTROLLER, name = "Max analog value: Left X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftX = 1;
@Entry(category = CONTROLLER, name = "Max analog value: Left Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftY = 1;
@Entry(category = CONTROLLER, name = "Max analog value: Right X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightX = 1;
@Entry(category = CONTROLLER, name = "Max analog value: Right Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightY = 1;
@Entry(category = CONTROLLER, name = "Trigger button fix") public static boolean triggerFix = true;
@Entry(category = CONTROLLER, name = "Excluded Controllers (Name Regex)") public static List<String> excludedControllers = Lists.newArrayList(".*(Keyboard)$", ".*(Touchpad)$", ".*(Pen)$", ".*(Finger)$");
// Miscellaneous
@Entry(category = MISC) @Hidden public static String keyboardLayout = "en_US:qwerty";
@Entry(category = MISC, name = "Debug") public static boolean debug = false;
@Entry(category = MISC, name = "Excluded Keybindings") public static List<String> excludedKeybindings = Lists.newArrayList("key.forward", "key.left", "key.back", "key.right", "key.jump", "key.sneak", "key.sprint", "key.inventory",
"key.swapOffhand", "key.drop", "key.use", "key.attack", "key.chat", "key.playerlist", "key.screenshot", "key.togglePerspective", "key.smoothCamera", "key.fullscreen", "key.saveToolbarActivator", "key.loadToolbarActivator",
"key.pickItem", "key.hotbar.1", "key.hotbar.2", "key.hotbar.3", "key.hotbar.4", "key.hotbar.5", "key.hotbar.6", "key.hotbar.7", "key.hotbar.8", "key.hotbar.9");
@Entry(category = GAMEPLAY, name = "Enable Hints") public static boolean enableHints = true;
@Entry(category = SCREENS, name = "Enable Shortcut in Controls Options") public static boolean shortcutInControls = true;
@Entry(category = MISC, name = "Ring Bindings (WIP)") public static List<String> ringBindings = new ArrayList<>();
@Entry(category = MISC, name = "Ring Bindings (WIP)") @Hidden public static List<String> ringBindings = new ArrayList<>();
@Entry(category = MISC, name = "Ignored Unbound Keys") public static List<String> ignoredUnboundKeys = Lists.newArrayList("inventorytabs.key.next_tab");
@Comment(category = MISC, centered = true, name="☆ Other Options") public static Comment _otherOptions;
@Entry(category = MISC, name = "Trigger button fix") public static boolean triggerFix = true;
@Entry(category = MISC, name = "Excluded Controllers (Name Regex)") public static List<String> excludedControllers = Lists.newArrayList(".*(Keyboard)$", ".*(Touchpad)$", ".*(Pen)$", ".*(Finger)$");
// Init mapping tab (see #onTabInit())
//@Comment(category = MAPPING) @Condition(requiredModId = "thisModDoesNotExist") public static Comment this_spacer_will_never_be_visible_as_well;
@Entry @Hidden public static Map<String, Map<String, String>> controllerBindingProfiles = new HashMap<>();
private static Map<String, String> currentBindingProfile = new HashMap<>();
private static Controller prevController;
public void onTabInit(String tabName, MidnightConfigListWidget list, MidnightConfigScreen screen) {
EntryInfo centeredComment = new EntryInfo(null, "midnightcontrols");
centeredComment.comment = new Comment() {
public Class<? extends Annotation> annotationType() {return null;}
public String category() {return "";}
public String name() {return "";}
public String url() {return "";}
public String requiredMod() {return "";}
@Override
public boolean centered() {
return true;
}
};
if (BUTTONS.equals(tabName)) {
InputManager.streamCategories()
.sorted(Comparator.comparingInt(ButtonCategory::getPriority))
.forEach(category -> {
list.addButton(Lists.newArrayList(), Text.literal(category.getTranslatedName()), centeredComment);
category.getBindings().forEach(binding -> {
ControllerBindingButton.add(binding, list, screen);
});
});
}
if (MISC.equals(tabName)) {
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
MidnightControlsConfig.keyboardLayout = "en_US:qwerty";
screen.updateList();
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
resetButton.setPosition(screen.width - 205 + 150 + 25, 0);
ButtonWidget editButton = ButtonWidget.builder(Text.translatable(KeyboardLayoutManager.getById(MidnightControlsConfig.keyboardLayout).getTranslationKey()),
button -> {
MidnightControlsConfig.keyboardLayout = KeyboardLayoutManager.getNext(KeyboardLayoutManager.getById(MidnightControlsConfig.keyboardLayout)).getId();
resetButton.active = !MidnightControlsConfig.keyboardLayout.equals("en_US:qwerty");
button.setMessage(Text.translatable(KeyboardLayoutManager.getById(MidnightControlsConfig.keyboardLayout).getTranslationKey()));
}).dimensions(screen.width - 185, 0, 150, 20).build();
resetButton.active = !MidnightControlsConfig.keyboardLayout.equals("en_US:qwerty");
list.addButton(List.of(editButton, resetButton), Text.translatable("midnightcontrols.menu.virtual_keyboard_layout"), new EntryInfo(null, screen.modid));
}
if (CONTROLLER.equals(tabName)) {
list.addButton(List.of(), Text.of("\uD83C\uDFAE General"), centeredComment);
ControllerSelectionButton.add(list, screen, false);
ControllerSelectionButton.add(list, screen, true);
ButtonWidget editButton = ButtonWidget.builder(Text.of("OPEN"),
button -> {
client.setScreen(new MidnightControlsSettingsScreen(client.currentScreen, false));
}).dimensions(screen.width - 185, 0, 175, 20).build();
list.addButton(List.of(editButton), Text.of("Legacy Config UI"), new EntryInfo(null, screen.modid));
}
// if (MAPPING.equals(tabName)) {
// MappingsStringInputWidget.add(centeredComment, list, screen);
// }
}
/**
* Loads the configuration
*/
@@ -353,7 +481,6 @@ public class MidnightControlsConfig extends MidnightConfig {
hudEnable = true;
hudSide = HudSide.LEFT;
analogMovement = true;
doubleTapToSprint = true;
controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
fastBlockPlacing = false;
@@ -374,6 +501,7 @@ public class MidnightControlsConfig extends MidnightConfig {
unfocusedInput = false;
virtualMouse = false;
virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
virtualKeyboard = false;
controllerID = 0;
secondControllerID = -1;
controllerType = ControllerType.DEFAULT;
@@ -397,17 +525,23 @@ public class MidnightControlsConfig extends MidnightConfig {
* @return the controller name matches a type, else empty
*/
public static @NotNull ControllerType matchControllerToType() {
String controller = getController().getName().toLowerCase();
if (controller.contains("xbox 360")) return ControllerType.XBOX_360;
else if (controller.contains("xbox") || controller.contains("afterglow")) return ControllerType.XBOX;
else if (controller.contains("steam") && GlDebugInfo.getCpuInfo().contains("AMD Custom APU")) return ControllerType.STEAM_DECK;
else if (controller.contains("steam")) return ControllerType.STEAM_CONTROLLER;
else if (controller.contains("dualsense") || controller.contains("ps5")) return ControllerType.DUALSENSE;
else if (controller.contains("dualshock") || controller.contains("ps4") || controller.contains("sony")) return ControllerType.DUALSHOCK;
else if (controller.contains("switch") || controller.contains("joy-con") || controller.contains("wii") || controller.contains("nintendo")) return ControllerType.SWITCH;
else if (controller.contains("ouya")) return ControllerType.OUYA;
String name = getController().getName().toLowerCase();
if (containsAny(name, "xbox 360")) return ControllerType.XBOX_360;
else if (containsAny(name, "xbox") || name.contains("afterglow")) return ControllerType.XBOX;
else if (containsAny(name, "steam") && GLX._getCpuInfo().contains("AMD Custom APU")) return ControllerType.STEAM_DECK;
else if (containsAny(name, "steam")) return ControllerType.STEAM_CONTROLLER;
else if (containsAny(name, "dualsense", "ps5")) return ControllerType.DUALSENSE;
else if (containsAny(name, "dualshock", "ps4", "sony")) return ControllerType.DUALSHOCK;
else if (containsAny(name, "switch", "joy-con", "wii", "nintendo")) return ControllerType.SWITCH;
else if (containsAny(name, "ouya")) return ControllerType.OUYA;
else return ControllerType.DEFAULT;
}
private static boolean containsAny(String controller, String... substring) {
for (String s : substring) if (controller.contains(s)) return true;
return false;
}
public static boolean doMixedInput() {
return touchInControllerMode && controlsMode == ControlsMode.CONTROLLER;
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
/**
* Represents the API implementation of ModMenu for midnightcontrols.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.1.0
*/
public class MidnightControlsModMenu implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return parent -> new MidnightControlsSettingsScreen(parent, false);
}
}

View File

@@ -0,0 +1,16 @@
package eu.midnightdust.midnightcontrols.client;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.KeyboardLayoutManager;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.SynchronousResourceReloader;
public class MidnightControlsReloadListener implements SynchronousResourceReloader {
public static final MidnightControlsReloadListener INSTANCE = new MidnightControlsReloadListener();
private MidnightControlsReloadListener() {}
@Override
public void reload(ResourceManager manager) {
manager.findResources("keyboard_layouts", path -> path.toString().startsWith("midnightcontrols") && path.toString().endsWith(".json")).forEach(KeyboardLayoutManager::loadLayout);
}
}

View File

@@ -10,24 +10,36 @@
package eu.midnightdust.midnightcontrols.client;
import com.google.common.collect.ImmutableSet;
import dev.lambdaurora.spruceui.navigation.NavigationEvent;
import dev.lambdaurora.spruceui.screen.SpruceScreen;
import dev.lambdaurora.spruceui.widget.AbstractSprucePressableButtonWidget;
import dev.lambdaurora.spruceui.widget.AbstractSpruceWidget;
import dev.lambdaurora.spruceui.widget.SpruceElement;
import dev.lambdaurora.spruceui.widget.SpruceLabelWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceEntryListWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceParentWidget;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.client.compat.EmotecraftCompat;
import eu.midnightdust.midnightcontrols.client.compat.LibGuiCompat;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.compat.YACLCompat;
import eu.midnightdust.midnightcontrols.client.gui.config.ControlsInput;
import eu.midnightdust.midnightcontrols.client.mixin.AdvancementsScreenAccessor;
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
import eu.midnightdust.midnightcontrols.client.mixin.KeyboardAccessor;
import eu.midnightdust.midnightcontrols.client.mixin.MouseAccessor;
import eu.midnightdust.midnightcontrols.client.util.InventoryUtil;
import eu.midnightdust.midnightcontrols.client.util.storage.AxisStorage;
import eu.midnightdust.midnightcontrols.client.util.storage.ButtonStorage;
import net.minecraft.client.gui.Click;
import net.minecraft.client.gui.navigation.NavigationDirection;
import net.minecraft.client.gui.screen.option.KeybindsScreen;
import net.minecraft.client.gui.widget.EntryListWidget;
import net.minecraft.client.gui.widget.PressableWidget;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.client.input.KeyInput;
import net.minecraft.client.input.MouseInput;
import net.minecraft.entity.vehicle.BoatEntity;
import org.thinkingstudio.obsidianui.widget.AbstractSpruceWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceEntryListWidget;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
@@ -35,16 +47,9 @@ import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.enums.CameraMode;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import eu.midnightdust.midnightcontrols.client.util.MathUtil;
import org.thinkingstudio.obsidianui.navigation.NavigationDirection;
import org.thinkingstudio.obsidianui.screen.SpruceScreen;
import org.thinkingstudio.obsidianui.widget.AbstractSprucePressableButtonWidget;
import org.thinkingstudio.obsidianui.widget.SpruceElement;
import org.thinkingstudio.obsidianui.widget.SpruceLabelWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceParentWidget;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element;
@@ -54,9 +59,6 @@ import net.minecraft.client.gui.screen.advancement.AdvancementTab;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
import net.minecraft.client.gui.screen.ingame.StonecutterScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.gui.screen.world.WorldListWidget;
import net.minecraft.text.TranslatableTextContent;
@@ -82,6 +84,7 @@ import static org.lwjgl.glfw.GLFW.*;
*/
public class MidnightInput {
public static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
public static final KeyInput ENTER_KEY_INPUT = new KeyInput(GLFW_KEY_ENTER, 0, 0);
// Cooldowns
public int actionGuiCooldown = 0;
public int joystickCooldown = 0;
@@ -96,7 +99,7 @@ public class MidnightInput {
public int inventoryInteractionCooldown = 0;
public int screenCloseCooldown = 0;
private ControllerControlsWidget controlsInput = null;
private ControlsInput controlsInput = null;
public MidnightInput() {}
@@ -154,7 +157,7 @@ public class MidnightInput {
this.fetchJoystickInput(state, true, false);
});
boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null;
boolean allowInput = this.controlsInput == null || this.controlsInput.getFocusedBinding() == null;
if (allowInput)
InputManager.updateBindings();
@@ -165,11 +168,11 @@ public class MidnightInput {
});
}
if (this.controlsInput != null && InputManager.STATES.int2ObjectEntrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::isUnpressed)) {
if (MidnightControlsConfig.debug) MidnightControls.log("Starting MidnightInput Button Edit");
if (this.controlsInput.focusedBinding != null && !this.controlsInput.waiting) {
int[] buttons = new int[this.controlsInput.currentButtons.size()];
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
buttons[i] = this.controlsInput.currentButtons.get(i);
//if (MidnightControlsConfig.debug) MidnightControls.log("Starting MidnightInput Button Edit");
if (this.controlsInput.getFocusedBinding() != null && !this.controlsInput.isWaiting()) {
int[] buttons = new int[this.controlsInput.getCurrentButtons().size()];
for (int i = 0; i < this.controlsInput.getCurrentButtons().size(); i++)
buttons[i] = this.controlsInput.getCurrentButtons().get(i);
this.controlsInput.finishBindingEdit(buttons);
this.controlsInput = null;
}
@@ -214,10 +217,10 @@ public class MidnightInput {
return;
if (this.targetYaw != 0.f || this.targetPitch != 0.f) {
float rotationYaw = (float) (client.player.prevYaw + (this.targetYaw * 0.175));
float rotationPitch = (float) (client.player.prevPitch + (this.targetPitch * 0.175));
client.player.prevYaw = rotationYaw;
client.player.prevPitch = MathHelper.clamp(rotationPitch, -90.f, 90.f);
float rotationYaw = (float) (client.player.lastYaw + (this.targetYaw * 0.175));
float rotationPitch = (float) (client.player.lastPitch + (this.targetPitch * 0.175));
client.player.lastYaw = rotationYaw;
client.player.lastPitch = MathHelper.clamp(rotationPitch, -90.f, 90.f);
client.player.setYaw(rotationYaw);
client.player.setPitch(MathHelper.clamp(rotationPitch, -90.f, 90.f));
if (client.player.isRiding() && client.player.getVehicle() != null) {
@@ -244,11 +247,11 @@ public class MidnightInput {
this.inventoryInteractionCooldown = 5;
}
public void beginControlsInput(ControllerControlsWidget widget) {
public void beginControlsInput(ControlsInput widget) {
this.controlsInput = widget;
if (widget != null) {
this.controlsInput.currentButtons.clear();
this.controlsInput.waiting = true;
this.controlsInput.getCurrentButtons().clear();
this.controlsInput.setWaiting(true);
}
}
@@ -330,16 +333,17 @@ public class MidnightInput {
}
public void handleButton(ButtonStorage storage) {
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
if (storage.state == ButtonState.PRESS && !this.controlsInput.currentButtons.contains(storage.button)) {
this.controlsInput.currentButtons.add(storage.button);
if (this.controlsInput != null && this.controlsInput.getFocusedBinding() != null) {
if (storage.state == ButtonState.PRESS && !this.controlsInput.getCurrentButtons().contains(storage.button)) {
this.controlsInput.getCurrentButtons().add(storage.button);
var buttons = new int[this.controlsInput.currentButtons.size()];
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
buttons[i] = this.controlsInput.currentButtons.get(i);
this.controlsInput.focusedBinding.setButton(buttons);
var buttons = new int[this.controlsInput.getCurrentButtons().size()];
for (int i = 0; i < this.controlsInput.getCurrentButtons().size(); i++)
buttons[i] = this.controlsInput.getCurrentButtons().get(i);
this.controlsInput.getFocusedBinding().setButton(buttons);
this.controlsInput.update();
this.controlsInput.waiting = false;
this.controlsInput.setWaiting(false);
}
return;
}
@@ -393,9 +397,9 @@ public class MidnightInput {
accessor.midnightcontrols$onCursorPos(client.getWindow().getHandle(), client.mouse.getX(), client.mouse.getY());
switch (storage.state) {
// Button pressed
case PRESS -> accessor.midnightcontrols$onMouseButton(client.getWindow().getHandle(), GLFW_MOUSE_BUTTON_LEFT, 1, 0);
case PRESS -> accessor.midnightcontrols$onMouseButton(client.getWindow().getHandle(), new MouseInput(GLFW_MOUSE_BUTTON_LEFT, 0), 1);
case RELEASE -> { // Button released
accessor.midnightcontrols$onMouseButton(client.getWindow().getHandle(), GLFW_MOUSE_BUTTON_LEFT, 0, 0);
accessor.midnightcontrols$onMouseButton(client.getWindow().getHandle(), new MouseInput(GLFW_MOUSE_BUTTON_LEFT, 0), 0);
client.currentScreen.setDragging(false);
}
case REPEAT -> client.currentScreen.setDragging(true); // Button held down / dragging
@@ -412,8 +416,8 @@ public class MidnightInput {
if (client.currentScreen instanceof HandledScreen<?> handledScreen && ((HandledScreenAccessor) handledScreen).midnightcontrols$getSlotAt(
mouseX, mouseY) != null) return;
if (!this.ignoreNextXRelease && client.currentScreen != null) {
if (storage.state == ButtonState.PRESS) client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_2);
else if (storage.state == ButtonState.RELEASE) client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_2);
if (storage.state == ButtonState.PRESS) client.currentScreen.mouseClicked(new Click(mouseX, mouseY, new MouseInput(GLFW.GLFW_MOUSE_BUTTON_2, 1)), false);
else if (storage.state == ButtonState.RELEASE) client.currentScreen.mouseReleased(new Click(mouseX, mouseY, new MouseInput(GLFW.GLFW_MOUSE_BUTTON_2, 0)));
this.screenCloseCooldown = 5;
} else {
this.ignoreNextXRelease = false;
@@ -515,17 +519,18 @@ public class MidnightInput {
// @TODO allow rebinding to left stick
int preferredAxis = true ? GLFW_GAMEPAD_AXIS_RIGHT_Y : GLFW_GAMEPAD_AXIS_LEFT_Y;
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
if (storage.buttonState != ButtonState.NONE && !this.controlsInput.currentButtons.contains(storage.getButtonId(storage.buttonState == ButtonState.PRESS))) {
if (this.controlsInput != null && this.controlsInput.getFocusedBinding() != null) {
if (storage.buttonState != ButtonState.NONE && !this.controlsInput.getCurrentButtons().contains(storage.getButtonId(storage.buttonState == ButtonState.PRESS))) {
this.controlsInput.currentButtons.add(storage.getButtonId(storage.buttonState == ButtonState.PRESS));
this.controlsInput.getCurrentButtons().add(storage.getButtonId(storage.buttonState == ButtonState.PRESS));
int[] buttons = new int[this.controlsInput.currentButtons.size()];
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
buttons[i] = this.controlsInput.currentButtons.get(i);
this.controlsInput.focusedBinding.setButton(buttons);
int[] buttons = new int[this.controlsInput.getCurrentButtons().size()];
for (int i = 0; i < this.controlsInput.getCurrentButtons().size(); i++)
buttons[i] = this.controlsInput.getCurrentButtons().get(i);
this.controlsInput.getFocusedBinding().setButton(buttons);
this.controlsInput.update();
this.controlsInput.waiting = false;
this.controlsInput.setWaiting(false);
}
return true;
} else if (storage.absValue >= storage.deadZone) {
@@ -599,7 +604,7 @@ public class MidnightInput {
.map(element -> (EntryListWidget<?>) element)
.filter(element -> element.getType().isFocused())
.anyMatch(element -> {
element.mouseScrolled(0.0, 0.0, 0, -value);
element.mouseScrolled(0.0, 0.0, 0, -value / 30);
return true;
});
}
@@ -607,7 +612,7 @@ public class MidnightInput {
public boolean handleAButton(@NotNull Screen screen, @NotNull Element focused) {
if (focused instanceof PressableWidget widget) {
widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
widget.onPress();
widget.onPress(ENTER_KEY_INPUT);
return true;
} else if (focused instanceof AbstractSprucePressableButtonWidget widget) {
widget.playDownSound();
@@ -622,8 +627,8 @@ public class MidnightInput {
} else if (focused instanceof MultiplayerServerListWidget list) {
var entry = list.getSelectedOrNull();
if (entry instanceof MultiplayerServerListWidget.LanServerEntry || entry instanceof MultiplayerServerListWidget.ServerEntry) {
((MultiplayerScreen) screen).select(entry);
((MultiplayerScreen) screen).connect();
//((MultiplayerScreen) screen).select(entry);
entry.connect();
}
} else if (focused instanceof SpruceParentWidget) {
var childFocused = ((SpruceParentWidget<?>) focused).getFocused();
@@ -648,7 +653,7 @@ public class MidnightInput {
*/
private boolean handleLeftRight(@NotNull Screen screen, boolean right) {
if (screen instanceof SpruceScreen spruceScreen) {
spruceScreen.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
spruceScreen.onNavigation(new NavigationEvent(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false, false));
this.actionGuiCooldown = 5;
return false;
}
@@ -668,11 +673,11 @@ public class MidnightInput {
case SpruceElement spruceElement -> {
if (spruceElement.requiresCursor())
return true;
return !spruceElement.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
return !spruceElement.onNavigation(new NavigationEvent(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false, false));
}
case SliderWidget slider -> {
if (slider.active) {
slider.keyPressed(right ? 262 : 263, 0, 0);
slider.keyPressed(new KeyInput(right ? 262 : 263, 0, 0));
this.actionGuiCooldown = 2; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
return true;
}
@@ -763,7 +768,7 @@ public class MidnightInput {
if (!isScreenInteractive(screen) && !screen.getClass().getCanonicalName().contains("me.jellysquid.mods.sodium.client.gui")) return false;
try {
if (screen instanceof SpruceScreen spruceScreen) {
if (spruceScreen.onNavigation(direction, false)) {
if (spruceScreen.onNavigation(new NavigationEvent(direction, false, false))) {
this.actionGuiCooldown = 5;
}
return true;
@@ -796,7 +801,7 @@ public class MidnightInput {
.anyMatch(element -> {
if (element.getMessage().getContent() instanceof TranslatableTextContent translatableText) {
if (set.stream().anyMatch(key -> translatableText.getKey().equals(key))) {
element.onPress();
element.onPress(ENTER_KEY_INPUT);
return true;
}
}
@@ -811,9 +816,9 @@ public class MidnightInput {
}
public void pressKeyboardKey(MinecraftClient client, int key) {
client.keyboard.onKey(client.getWindow().getHandle(), key, 0, 1, 0);
((KeyboardAccessor) client.keyboard).midnightcontrols$onKey(client.getWindow().getHandle(), 1, new KeyInput(key, 0, 0));
}
public void pressKeyboardKey(Screen screen, int key) {
screen.keyPressed(key, 0, 1);
screen.keyPressed(new KeyInput(key, 0, 0));
}
}

View File

@@ -125,7 +125,7 @@ public class MidnightReacharound {
if (client.player.isRiding())
return null;
// Temporary pos, do not use
Vec3d playerPosi = client.player.getPos();
Vec3d playerPosi = client.player.getEntityPos();
// Imitates var playerPos = client.player.getBlockPos().down();
Vec3d playerPos = new Vec3d(playerPosi.getX(), playerPosi.getY() - 1.0, playerPosi.getZ());

View File

@@ -4,6 +4,7 @@ import dev.emi.emi.api.EmiApi;
import dev.emi.emi.config.EmiConfig;
import dev.emi.emi.screen.EmiScreenManager;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
@@ -14,8 +15,8 @@ import static eu.midnightdust.midnightcontrols.MidnightControls.id;
public class EMICompat implements CompatHandler {
public static boolean handleEmiPages(boolean direction) {
if (isEMIEnabled() && MidnightControlsClient.input.actionGuiCooldown == 0 && EmiScreenManager.getSearchPanel() != null && EmiScreenManager.getSearchPanel().pageLeft != null && EmiScreenManager.getSearchPanel().pageRight != null) {
if (direction) EmiScreenManager.getSearchPanel().pageRight.onPress();
else EmiScreenManager.getSearchPanel().pageLeft.onPress();
if (direction) EmiScreenManager.getSearchPanel().pageRight.onPress(MidnightInput.ENTER_KEY_INPUT);
else EmiScreenManager.getSearchPanel().pageLeft.onPress(MidnightInput.ENTER_KEY_INPUT);
MidnightControlsClient.input.actionGuiCooldown = 5;
return true;
}

View File

@@ -1,36 +1,52 @@
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import io.github.kosmx.emotes.arch.gui.EmoteMenuImpl;
import io.github.kosmx.emotes.arch.gui.screen.ingame.FastChosseScreen;
import eu.midnightdust.midnightcontrols.client.mixin.MouseAccessor;
import io.github.kosmx.emotes.arch.screen.ingame.FastMenuScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.input.MouseInput;
import org.joml.Vector2i;
import org.lwjgl.glfw.GLFW;
public class EmotecraftCompat {
private static final MinecraftClient client = MinecraftClient.getInstance();
public static void openEmotecraftScreen(Screen parent) {
client.setScreen(new EmoteMenuImpl(parent));
client.setScreen(new FastMenuScreen(parent));
}
public static boolean isEmotecraftScreen(Screen screen) {
return screen instanceof FastChosseScreen;
return screen instanceof FastMenuScreen;
}
static int prevIndex = -1;
public static void handleEmoteSelector(int index) {
if (client.currentScreen instanceof FastChosseScreen) {
try {
if (client.currentScreen instanceof FastMenuScreen) {
boolean stickReleased = index == -1 && prevIndex != -1;
var pos = calcMousePos(stickReleased ? prevIndex : index);
InputManager.queueMousePosition(pos.x, pos.y);
InputManager.INPUT_MANAGER.updateMousePosition(client);
if (stickReleased) {
((MouseAccessor) client.mouse).midnightcontrols$onMouseButton(client.getWindow().getHandle(), new MouseInput(GLFW.GLFW_MOUSE_BUTTON_LEFT, 0), GLFW.GLFW_PRESS);
prevIndex = -1;
}
else prevIndex = index;
} else prevIndex = -1;
} catch (Exception ignored) {}
}
public static Vector2i calcMousePos(int index) {
int x = client.getWindow().getWidth() / 2;
int y = client.getWindow().getHeight() / 2;
switch (index) {
case 0, 3, 5 -> x -= 200;
case 2, 4, 7 -> x += 200;
case 0, 3, 5 -> x -= 275;
case 2, 4, 7 -> x += 275;
}
switch (index) {
case 0, 1, 2 -> y -= 200;
case 5, 6, 7 -> y += 200;
}
InputManager.queueMousePosition(x, y);
InputManager.INPUT_MANAGER.updateMousePosition(client);
case 0, 1, 2 -> y -= 275;
case 5, 6, 7 -> y += 275;
}
return new Vector2i(x, y);
}
}

View File

@@ -5,6 +5,7 @@ import dev.isxander.yacl.gui.OptionListWidget;
import dev.isxander.yacl.gui.YACLScreen;
import dev.isxander.yacl.gui.controllers.ControllerWidget;
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.screen.Screen;
import org.lwjgl.glfw.GLFW;
@@ -13,7 +14,7 @@ public class YACLCompat implements CompatHandler {
public static boolean handleAButton(Screen screen, Element element) {
if (element instanceof AbstractWidget abstractWidget) {
// imitate enter key press
return abstractWidget.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
return abstractWidget.keyPressed(MidnightInput.ENTER_KEY_INPUT);
}
return false;
}

View File

@@ -77,7 +77,7 @@ public class ButtonBinding {
public static final ButtonBinding SCREENSHOT = new Builder("screenshot").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_A)
.action(InputHandlers::handleScreenshot).cooldown().register();
public static final ButtonBinding DEBUG_SCREEN = new Builder("debug_screen").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_B)
.action((client,binding,value,action) -> {if (action == ButtonState.PRESS) client.inGameHud.getDebugHud().toggleDebugHud(); return true;}).cooldown().register();
.action((client,binding,value,action) -> {if (action == ButtonState.PRESS) client.debugHudEntryList.setF3Enabled(!client.debugHudEntryList.isF3Enabled()); return true;}).cooldown().register();
public static final ButtonBinding SLOT_DOWN = new Builder("slot_down").buttons(GLFW_GAMEPAD_BUTTON_DPAD_DOWN)
.action(InputHandlers.handleInventorySlotPad(1)).onlyInInventory().cooldown().register();
public static final ButtonBinding SLOT_LEFT = new Builder("slot_left").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT)
@@ -413,7 +413,7 @@ public class ButtonBinding {
}
static {
MOVEMENT_CATEGORY = InputManager.registerDefaultCategory("key.categories.movement", category -> category.registerAllBindings(
MOVEMENT_CATEGORY = InputManager.registerDefaultCategory("movement", category -> category.registerAllBindings(
ButtonBinding.FORWARD,
ButtonBinding.BACK,
ButtonBinding.LEFT,
@@ -421,12 +421,12 @@ public class ButtonBinding {
ButtonBinding.JUMP,
ButtonBinding.SNEAK,
ButtonBinding.SPRINT));
GAMEPLAY_CATEGORY = InputManager.registerDefaultCategory("key.categories.gameplay", category -> category.registerAllBindings(
GAMEPLAY_CATEGORY = InputManager.registerDefaultCategory("gameplay", category -> category.registerAllBindings(
ButtonBinding.ATTACK,
ButtonBinding.PICK_BLOCK,
ButtonBinding.USE
));
INVENTORY_CATEGORY = InputManager.registerDefaultCategory("key.categories.inventory", category -> category.registerAllBindings(
INVENTORY_CATEGORY = InputManager.registerDefaultCategory("inventory", category -> category.registerAllBindings(
ButtonBinding.EXIT,
ButtonBinding.DROP_ITEM,
ButtonBinding.HOTBAR_LEFT,
@@ -445,9 +445,9 @@ public class ButtonBinding {
ButtonBinding.SLOT_LEFT,
ButtonBinding.SLOT_RIGHT
));
MULTIPLAYER_CATEGORY = InputManager.registerDefaultCategory("key.categories.multiplayer",
MULTIPLAYER_CATEGORY = InputManager.registerDefaultCategory("multiplayer",
category -> category.registerAllBindings(ButtonBinding.CHAT, ButtonBinding.PLAYER_LIST));
MISC_CATEGORY = InputManager.registerDefaultCategory("key.categories.misc", category -> category.registerAllBindings(
MISC_CATEGORY = InputManager.registerDefaultCategory("misc", category -> category.registerAllBindings(
ButtonBinding.SCREENSHOT,
ButtonBinding.TOGGLE_PERSPECTIVE,
ButtonBinding.PAUSE_GAME,

View File

@@ -38,15 +38,6 @@ public class ButtonCategory {
public ButtonCategory(@NotNull Identifier id) {
this(id, 100);
}
@Deprecated
public ButtonCategory(@NotNull org.aperlambda.lambdacommon.Identifier id, int priority) {
this(Identifier.of(id.getNamespace(), id.getName()), priority);
}
@Deprecated
public ButtonCategory(@NotNull org.aperlambda.lambdacommon.Identifier id) {
this(id, 100);
}
public void registerBinding(@NotNull ButtonBinding binding) {
if (this.bindings.contains(binding))
@@ -74,15 +65,12 @@ public class ButtonCategory {
/**
* Gets the translated name of this category.
* <p>
* The translation key should be `modid.identifier_name`.
* The translation key should be `key.category.modid.identifier_name`.
*
* @return the translated name
*/
public @NotNull String getTranslatedName() {
if (this.id.getNamespace().equals("minecraft"))
return I18n.translate(this.id.getPath());
else
return I18n.translate(this.id.getNamespace() + "." + this.id.getPath());
return I18n.translate("key.category.%s.%s".formatted(id.getNamespace(), id.getPath()));
}
/**

View File

@@ -62,9 +62,9 @@ public class InputHandlers {
if (!client.player.isSpectator()) {
var inv = client.player.getInventory();
if (next)
inv.setSelectedSlot(inv.selectedSlot < 8 ? inv.selectedSlot + 1 : inv.selectedSlot - 8);
inv.setSelectedSlot(inv.getSelectedSlot() < 8 ? inv.getSelectedSlot() + 1 : inv.getSelectedSlot() - 8);
else
inv.setSelectedSlot(inv.selectedSlot > 0 ? inv.selectedSlot - 1 : inv.selectedSlot + 8);
inv.setSelectedSlot(inv.getSelectedSlot() > 0 ? inv.getSelectedSlot() - 1 : inv.getSelectedSlot() + 8);
}
else {
if (client.inGameHud.getSpectatorHud().isOpen()) {
@@ -177,7 +177,7 @@ public class InputHandlers {
if (button.getName().equals("take_all")) {
return false;
}
slotId = accessor.midnightcontrols$isClickOutsideBounds(x, y, accessor.getX(), accessor.getY(), GLFW_MOUSE_BUTTON_1) ? -999 : -1;
slotId = accessor.midnightcontrols$isClickOutsideBounds(x, y, accessor.getX(), accessor.getY()) ? -999 : -1;
} else {
slotId = slot.id;
}

View File

@@ -41,7 +41,7 @@ import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.cli
*/
public class InputManager {
public static final InputManager INPUT_MANAGER = new InputManager();
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
private static final List<ButtonBinding> BINDINGS = Collections.synchronizedList(new ArrayList<>());
private static final List<ButtonCategory> CATEGORIES = new ArrayList<>();
public static final Int2ObjectMap<ButtonState> STATES = new Int2ObjectOpenHashMap<>();
public static final Int2FloatMap BUTTON_VALUES = new Int2FloatOpenHashMap();
@@ -76,8 +76,8 @@ public class InputManager {
public void updateMousePosition(@NotNull MinecraftClient client) {
Objects.requireNonNull(client, "Client instance cannot be null.");
if (this.prevTargetMouseX != this.targetMouseX || this.prevTargetMouseY != this.targetMouseY) {
double mouseX = this.prevTargetMouseX + (this.targetMouseX - this.prevTargetMouseX) * client.getRenderTickCounter().getTickDelta(true) + 0.5;
double mouseY = this.prevTargetMouseY + (this.targetMouseY - this.prevTargetMouseY) * client.getRenderTickCounter().getTickDelta(true) + 0.5;
double mouseX = this.prevTargetMouseX + (this.targetMouseX - this.prevTargetMouseX) * client.getRenderTickCounter().getTickProgress(true) + 0.5;
double mouseY = this.prevTargetMouseY + (this.targetMouseY - this.prevTargetMouseY) * client.getRenderTickCounter().getTickProgress(true) + 0.5;
if (!MidnightControlsConfig.virtualMouse)
GLFW.glfwSetCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
((MouseAccessor) client.mouse).midnightcontrols$onCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
@@ -109,8 +109,10 @@ public class InputManager {
* @return true if the binding is registered, else false
*/
public static boolean hasBinding(@NotNull ButtonBinding binding) {
synchronized (BINDINGS) {
return BINDINGS.contains(binding);
}
}
/**
* Returns whether the specified binding is registered or not.
@@ -119,8 +121,10 @@ public class InputManager {
* @return true if the binding is registered, else false
*/
public static boolean hasBinding(@NotNull String name) {
synchronized (BINDINGS) {
return BINDINGS.parallelStream().map(ButtonBinding::getName).anyMatch(binding -> binding.equalsIgnoreCase(name));
}
}
/**
* Returns whether the specified binding is registered or not.
@@ -139,18 +143,22 @@ public class InputManager {
* @return true if the binding is registered, else false
*/
public static ButtonBinding getBinding(@NotNull String name) {
synchronized (BINDINGS) {
if (BINDINGS.parallelStream().map(ButtonBinding::getName).anyMatch(binding -> binding.equalsIgnoreCase(name)))
BINDINGS.forEach(binding -> {
if (binding.getName().equalsIgnoreCase(name)) InputManager.tempBinding = binding;
});
}
return tempBinding;
}
private static List<ButtonBinding> unboundBindings;
public static List<ButtonBinding> getUnboundBindings() {
unboundBindings = new ArrayList<>();
synchronized (BINDINGS) {
BINDINGS.forEach(binding -> {
if (binding.isNotBound() && !MidnightControlsConfig.ignoredUnboundKeys.contains(binding.getTranslationKey())) unboundBindings.add(binding);
});
}
unboundBindings.sort(Comparator.comparing(s -> I18n.translate(s.getTranslationKey())));
return unboundBindings;
}
@@ -162,9 +170,11 @@ public class InputManager {
* @return the registered binding
*/
public static @NotNull ButtonBinding registerBinding(@NotNull ButtonBinding binding) {
if (hasBinding(binding))
synchronized (BINDINGS) {
if (BINDINGS.contains(binding))
throw new IllegalStateException("Cannot register twice a button binding in the registry.");
BINDINGS.add(binding);
}
return binding;
}
@@ -284,8 +294,10 @@ public class InputManager {
* @return true if the button has duplicated bindings, else false
*/
public static boolean hasDuplicatedBindings(int[] button) {
synchronized (BINDINGS) {
return BINDINGS.parallelStream().filter(binding -> areButtonsEquivalent(binding.getButton(), button)).count() > 1;
}
}
/**
* Returns whether the button has duplicated bindings.
@@ -294,8 +306,10 @@ public class InputManager {
* @return true if the button has duplicated bindings, else false
*/
public static boolean hasDuplicatedBindings(ButtonBinding binding) {
synchronized (BINDINGS) {
return BINDINGS.parallelStream().filter(other -> areButtonsEquivalent(other.getButton(), binding.getButton()) && other.filter.equals(binding.filter)).count() > 1;
}
}
/**
* Returns whether the specified buttons are equivalent or not.
@@ -347,6 +361,7 @@ public class InputManager {
record ButtonStateValue(ButtonState state, float value) {
}
var states = new Object2ObjectOpenHashMap<ButtonBinding, ButtonStateValue>();
synchronized (BINDINGS) {
for (var binding : BINDINGS) {
var state = binding.isAvailable() ? getBindingState(binding) : ButtonState.NONE;
if (skipButtons.intStream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
@@ -369,6 +384,7 @@ public class InputManager {
states.put(binding, new ButtonStateValue(state, value));
}
}
states.forEach((binding, state) -> {
if (state.state() != ButtonState.NONE) {
@@ -387,8 +403,10 @@ public class InputManager {
}
public static @NotNull Stream<ButtonBinding> streamBindings() {
synchronized (BINDINGS) {
return BINDINGS.stream();
}
}
public static @NotNull Stream<ButtonCategory> streamCategories() {
return CATEGORIES.stream();
@@ -402,9 +420,9 @@ public class InputManager {
* @param code the code
* @param category the category of the key binding
* @return the key binding
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, String)
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, net.minecraft.client.option.KeyBinding.Category)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull Identifier id, InputUtil.Type type, int code, @NotNull String category) {
public static @NotNull KeyBinding makeKeyBinding(@NotNull Identifier id, InputUtil.Type type, int code, @NotNull KeyBinding.Category category) {
return new KeyBinding(String.format("key.%s.%s", id.getNamespace(), id.getPath()), type, code, category);
}
}

View File

@@ -11,11 +11,14 @@ package eu.midnightdust.midnightcontrols.client.controller;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.mixin.InputAccessor;
import eu.midnightdust.midnightcontrols.client.util.MathUtil;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.util.PlayerInput;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec2f;
import org.jetbrains.annotations.NotNull;
/**
@@ -48,15 +51,12 @@ public final class MovementHandler implements PressAction {
public void applyMovement(@NotNull ClientPlayerEntity player) {
if (!this.shouldOverrideMovement)
return;
// TODO
// player.input.playerInput.pressingForward = this.pressingForward;
// player.input.pressingBack = this.pressingBack;
// player.input.pressingLeft = this.pressingLeft;
// player.input.pressingRight = this.pressingRight;
player.input.playerInput = new PlayerInput(this.pressingForward, this.pressingBack, this.pressingLeft, this.pressingRight,
player.input.playerInput.jump(), player.input.playerInput.sneak(), player.input.playerInput.sprint());
polarUtil.calculate(this.movementSideways, this.movementForward, this.slowdownFactor);
player.input.movementForward = polarUtil.polarY;
player.input.movementSideways = polarUtil.polarX;
Vec2f inputVector = new Vec2f(polarUtil.polarX, polarUtil.polarY);
((InputAccessor)player.input).setMovementVector(inputVector);
this.shouldOverrideMovement = false;
}

View File

@@ -28,7 +28,7 @@ public interface PressAction {
if (action == ButtonState.REPEAT || client.currentScreen != null)
return false;
button.asKeyBinding().ifPresent(binding -> {
if (binding instanceof StickyKeyBinding)
if (binding instanceof StickyKeyBinding && binding != client.options.attackKey) // TODO: Properly fix sticky keys so the attack key doesn't need to be a hardcoded exception
binding.setPressed(button.isPressed());
else
((KeyBindingAccessor) binding).midnightcontrols$handlePressState(button.isPressed());

View File

@@ -9,16 +9,16 @@
package eu.midnightdust.midnightcontrols.client.gui;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.option.SpruceOption;
import dev.lambdaurora.spruceui.option.SpruceSimpleActionOption;
import dev.lambdaurora.spruceui.render.SpruceGuiGraphics;
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
import dev.lambdaurora.spruceui.widget.text.SpruceTextAreaWidget;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.util.math.MatrixStack;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.option.SpruceOption;
import org.thinkingstudio.obsidianui.option.SpruceSimpleActionOption;
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
import org.thinkingstudio.obsidianui.widget.text.SpruceTextAreaWidget;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
@@ -105,9 +105,9 @@ public class MappingsStringInputWidget extends SpruceContainerWidget {
}
@Override
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
public void renderWidget(SpruceGuiGraphics context, int mouseX, int mouseY, float delta) {
super.renderWidget(context, mouseX, mouseY, delta);
context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.multiple_mapping_tip"), this.textArea.getX() + this.textArea.getWidth() / 2, this.textArea.getY() + this.textArea.getHeight() - 12, 0x888888);
context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.current_controller_guid", MidnightControlsConfig.getController().getGuid()), this.textArea.getX() + this.textArea.getWidth() / 2, this.height - 21, 0xFFFFFF);
context.vanilla().drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.multiple_mapping_tip"), this.textArea.getX() + this.textArea.getWidth() / 2, this.textArea.getY() + this.textArea.getHeight() - 12, 0xFF888888);
context.vanilla().drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.current_controller_guid", MidnightControlsConfig.getController().getGuid()), this.textArea.getX() + this.textArea.getWidth() / 2, this.height - 21, 0xFFFFFFFF);
}
}

View File

@@ -10,28 +10,23 @@
package eu.midnightdust.midnightcontrols.client.gui;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import net.minecraft.client.render.RenderTickCounter;
import org.thinkingstudio.obsidianui.hud.Hud;
import org.joml.Matrix3x2fStack;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
/**
* Represents the midnightcontrols HUD.
*
@@ -39,8 +34,8 @@ import static eu.midnightdust.midnightcontrols.MidnightControls.id;
* @version 1.7.0
* @since 1.0.0
*/
public class MidnightControlsHud extends Hud {
private final MinecraftClient client = MinecraftClient.getInstance();
public class MidnightControlsHud {
private MinecraftClient client = MinecraftClient.getInstance();
private int attackWidth = 0;
private int attackButtonWidth = 0;
private int dropItemWidth = 0;
@@ -56,14 +51,15 @@ public class MidnightControlsHud extends Hud {
private String placeAction = "";
private int ticksDisplayedCrosshair = 0;
private static boolean isCrammed = false;
public static boolean isVisible = false;
private static final MidnightControlsHud INSTANCE = new MidnightControlsHud();
public MidnightControlsHud() {
super(id("hud/button_indicator"));
public static MidnightControlsHud getInstance() {
return INSTANCE;
}
@Override
public void init(@NotNull MinecraftClient client, int screenWidth, int screenHeight) {
super.init(client, screenWidth, screenHeight);
public void init() {
this.client = MidnightControlsClient.client;
this.inventoryWidth = this.width(ButtonBinding.INVENTORY);
this.inventoryButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.INVENTORY);
this.swapHandsWidth = this.width(ButtonBinding.SWAP_HANDS);
@@ -78,19 +74,18 @@ public class MidnightControlsHud extends Hud {
/**
* Renders the MidnightControls HUD.
*/
@Override
public void render(DrawContext context, RenderTickCounter tickCounter) {
if (this.client == null) return;
if (this.client == null || !isVisible) return;
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && this.client.currentScreen == null) {
isCrammed = client.getWindow().getScaledWidth() < 520;
int y = bottom(2);
MatrixStack matrices = context.getMatrices();
matrices.push();
Matrix3x2fStack matrices = context.getMatrices();
matrices.pushMatrix();
this.renderFirstIcons(context, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderSecondIcons(context, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderFirstSection(context, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderSecondSection(context, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
matrices.pop();
matrices.popMatrix();
}
if (MidnightControlsClient.reacharound.isLastReacharoundVertical()) {
@@ -98,12 +93,12 @@ public class MidnightControlsHud extends Hud {
var window = this.client.getWindow();
var text = "[ ]";
float scale = Math.min(5, this.ticksDisplayedCrosshair + tickCounter.getTickDelta(true)) / 5F;
float scale = Math.min(5, this.ticksDisplayedCrosshair + tickCounter.getTickProgress(true)) / 5F;
scale *= scale;
int opacity = ((int) (255 * scale)) << 24;
context.drawText(client.textRenderer, text, (int) (window.getScaledWidth() / 2.f - this.client.textRenderer.getWidth(text) / 2.f),
(int) (window.getScaledHeight() / 2.f - 4), 0xCCCCCC | opacity, false);
(int) (window.getScaledHeight() / 2.f - 4), 0xFFCCCCCC | opacity, false);
}
}
@@ -196,10 +191,8 @@ public class MidnightControlsHud extends Hud {
if (!ButtonBinding.ATTACK.isNotBound()) this.drawTip(context, currentX, y, this.attackAction, this.attackWidth != 0);
}
@Override
public void tick() {
if (this.client == null) return;
super.tick();
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
if (this.client.crosshairTarget == null)
return;
@@ -256,7 +249,7 @@ public class MidnightControlsHud extends Hud {
placeAction = customUseAction;
this.placeAction = placeAction;
this.showSwapHandsAction = !this.client.player.getMainHandStack().isEmpty() || !this.client.player.getOffHandStack().isEmpty();
this.showSwapHandsAction = this.client.player != null && (!this.client.player.getMainHandStack().isEmpty() || !this.client.player.getOffHandStack().isEmpty());
// Cache the "Use" tip width.
if (this.placeAction.isEmpty())
@@ -266,11 +259,6 @@ public class MidnightControlsHud extends Hud {
}
}
@Override
public boolean hasTicks() {
return true;
}
private int bottom(int y) {
return (this.client.getWindow().getScaledHeight() - y - MidnightControlsRenderer.ICON_SIZE);
}
@@ -299,6 +287,6 @@ public class MidnightControlsHud extends Hud {
return;
var translatedAction = I18n.translate(action);
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - this.client.textRenderer.fontHeight / 2) + 1;
context.drawText(this.client.textRenderer, translatedAction, x, (y + textY), 14737632, false);
context.drawText(this.client.textRenderer, translatedAction, x, (y + textY), 0xFFFFFFFF, false);
}
}

View File

@@ -9,34 +9,18 @@
package eu.midnightdust.midnightcontrols.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.enums.ControllerType;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.enums.VirtualMouseSkin;
import eu.midnightdust.midnightcontrols.client.mixin.DrawContextAccessor;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.*;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.texture.Sprite;
import net.minecraft.screen.slot.Slot;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW;
import java.util.function.Function;
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
/**
* Represents the midnightcontrols renderer.
*
@@ -169,17 +153,14 @@ public class MidnightControlsRenderer {
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 200 -> buttonOffset = 10 * 15;
}
RenderSystem.disableDepthTest();
int assetSize = axis || (button >= 15 && button <= 18) ? AXIS_SIZE : BUTTON_SIZE;
RenderSystem.setShaderColor(1.f, second ? 0.f : 1.f, 1.f, 1.f);
context.drawTexture(RenderLayer::getGuiTextured, axis ? MidnightControlsClient.CONTROLLER_AXIS : button >= 15 && button <= 19 ? MidnightControlsClient.CONTROLLER_EXPANDED :MidnightControlsClient.CONTROLLER_BUTTONS
//RenderSystem.setShaderColor(1.f, second ? 0.f : 1.f, 1.f, 1.f);
context.drawTexture(RenderPipelines.GUI_TEXTURED, axis ? MidnightControlsClient.CONTROLLER_AXIS : button >= 15 && button <= 19 ? MidnightControlsClient.CONTROLLER_EXPANDED :MidnightControlsClient.CONTROLLER_BUTTONS
, x + (ICON_SIZE / 2 - assetSize / 2), y + (ICON_SIZE / 2 - assetSize / 2),
(float) buttonOffset, (float) (controllerType * assetSize),
assetSize, assetSize,
256, 256);
RenderSystem.enableDepthTest();
return ICON_SIZE;
}
@@ -195,7 +176,8 @@ public class MidnightControlsRenderer {
var translatedAction = I18n.translate(action);
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - client.textRenderer.fontHeight / 2) + 1;
return context.drawTextWithShadow(client.textRenderer, translatedAction, (x + buttonWidth + 2), (y + textY), 14737632);
context.drawTextWithShadow(client.textRenderer, translatedAction, (x + buttonWidth + 2), (y + textY), 0xFFFFFFFF);
return (x + buttonWidth + 2) + client.textRenderer.getWidth(translatedAction);
}
return -10;
@@ -204,76 +186,6 @@ public class MidnightControlsRenderer {
private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer) {
return 15 + 5 + textRenderer.getWidth(action);
}
public static void renderWaylandCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (MidnightControlsConfig.virtualMouse || client.currentScreen == null || MidnightControlsConfig.controlsMode != ControlsMode.CONTROLLER) return;
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
try {
Identifier spritePath = MidnightControlsClient.WAYLAND_CURSOR_TEXTURE_LIGHT;
if (MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.DEFAULT_DARK || MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.SECOND_DARK)
spritePath = MidnightControlsClient.WAYLAND_CURSOR_TEXTURE_DARK;
Sprite sprite = client.getGuiAtlasManager().getSprite(spritePath);
drawUnalignedTexturedQuad(RenderLayer::getGuiTextured, sprite.getAtlasId(), context, mouseX, mouseX + 8, mouseY, mouseY + 8, 999, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
} catch (IllegalStateException ignored) {}
}
public static void renderVirtualCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null
|| MidnightInput.isScreenInteractive(client.currentScreen)))
return;
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
boolean hoverSlot = false;
if (client.currentScreen instanceof HandledScreenAccessor inventoryScreen) {
int guiLeft = inventoryScreen.getX();
int guiTop = inventoryScreen.getY();
Slot slot = inventoryScreen.midnightcontrols$getSlotAt(mouseX, mouseY);
if (slot != null) {
mouseX = guiLeft + slot.x;
mouseY = guiTop + slot.y;
hoverSlot = true;
}
}
if (!hoverSlot && client.currentScreen != null) {
var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, (int) mouseX, (int) mouseY);
if (slot != null) {
mouseX = slot.x();
mouseY = slot.y();
hoverSlot = true;
}
}
if (!hoverSlot) {
mouseX -= 8;
mouseY -= 8;
}
try {
Sprite sprite = client.getGuiAtlasManager().getSprite(id(MidnightControlsConfig.virtualMouseSkin.getSpritePath() + (hoverSlot ? "_slot" : "")));
drawUnalignedTexturedQuad(RenderLayer::getGuiTextured, sprite.getAtlasId(), context, mouseX, mouseX + 16, mouseY, mouseY + 16, 999, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
} catch (IllegalStateException ignored) {}
}
private static void drawUnalignedTexturedQuad(Function<Identifier, RenderLayer> renderLayers, Identifier texture, DrawContext context, float x1, float x2, float y1, float y2, float z, float u1, float u2, float v1, float v2) {
RenderLayer renderLayer = renderLayers.apply(texture);
//RenderSystem.setShaderTexture(0, texture);
Matrix4f matrix4f = context.getMatrices().peek().getPositionMatrix();
//BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE);
VertexConsumer vertexConsumer = ((DrawContextAccessor)context).getVertexConsumers().getBuffer(renderLayer);
vertexConsumer.vertex(matrix4f, x1, y1, z).texture(u1, v1).color(ColorHelper.getWhite(1.0f));
vertexConsumer.vertex(matrix4f, x1, y2, z).texture(u1, v2).color(ColorHelper.getWhite(1.0f));
vertexConsumer.vertex(matrix4f, x2, y2, z).texture(u2, v2).color(ColorHelper.getWhite(1.0f));
vertexConsumer.vertex(matrix4f, x2, y1, z).texture(u2, v1).color(ColorHelper.getWhite(1.0f));
context.draw();
}
public record ButtonSize(int length, int height) {
}

View File

@@ -9,43 +9,40 @@
package eu.midnightdust.midnightcontrols.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.SpruceTexts;
import dev.lambdaurora.spruceui.background.Background;
import dev.lambdaurora.spruceui.option.*;
import dev.lambdaurora.spruceui.render.SpruceGuiGraphics;
import dev.lambdaurora.spruceui.screen.SpruceScreen;
import dev.lambdaurora.spruceui.tooltip.TooltipData;
import dev.lambdaurora.spruceui.widget.AbstractSpruceWidget;
import dev.lambdaurora.spruceui.widget.SpruceLabelWidget;
import dev.lambdaurora.spruceui.widget.SpruceWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceOptionListWidget;
import dev.lambdaurora.spruceui.widget.container.tabbed.SpruceTabbedWidget;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil;
import org.thinkingstudio.obsidianui.background.Background;
import org.thinkingstudio.obsidianui.widget.SpruceWidget;
import eu.midnightdust.lib.util.MidnightColorUtil;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.KeyboardLayoutManager;
import net.minecraft.util.math.ColorHelper;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.option.*;
import org.thinkingstudio.obsidianui.screen.SpruceScreen;
import org.thinkingstudio.obsidianui.widget.AbstractSpruceWidget;
import org.thinkingstudio.obsidianui.widget.SpruceLabelWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceOptionListWidget;
import org.thinkingstudio.obsidianui.widget.container.tabbed.SpruceTabbedWidget;
import eu.midnightdust.midnightcontrols.packet.ControlsModePayload;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.render.*;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Util;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
/**
* Represents the midnightcontrols settings screen.
*/
@@ -63,12 +60,11 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
private final SpruceOption eyeTrackingAsMouseOption;
private final SpruceOption eyeTrackingDeadzone;
private final SpruceOption virtualMouseOption;
private final SpruceOption virtualKeyboardOption;
private final SpruceOption hideCursorOption;
private final SpruceOption resetOption;
private final SpruceOption advancedConfigOption;
// Gameplay options
private final SpruceOption analogMovementOption;
private final SpruceOption doubleTapToSprintOption;
private final SpruceOption autoJumpOption;
private final SpruceOption controllerToggleSneakOption;
private final SpruceOption controllerToggleSprintOption;
@@ -104,7 +100,7 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.GOLD));
else
return option.getDisplayText(Text.literal(controllerName));
}, null);
}, TooltipData.EMPTY);
private final SpruceOption secondControllerOption = new SpruceCyclingOption("midnightcontrols.menu.controller2",
amount -> {
int id = MidnightControlsConfig.getSecondController().map(Controller::id).orElse(-1);
@@ -123,7 +119,7 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
else
return option.getDisplayText(Text.literal(controllerName));
}).orElse(option.getDisplayText(SpruceTexts.OPTIONS_OFF.copyContentOnly().formatted(Formatting.RED))),
Text.translatable("midnightcontrols.menu.controller2.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.controller2.tooltip")).build());
private final SpruceOption unfocusedInputOption;
private final SpruceOption invertsRightXAxis;
private final SpruceOption invertsRightYAxis;
@@ -137,13 +133,22 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
maxAnalogValueOption("midnightcontrols.menu.max_right_x_value", GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X),
maxAnalogValueOption("midnightcontrols.menu.max_right_y_value", GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y)
};
// Controller options
public final static SpruceOption virtualKeyboardLayoutOption =
new SpruceCyclingOption("midnightcontrols.menu.virtual_keyboard_layout",
amount -> {
MidnightControlsConfig.keyboardLayout = KeyboardLayoutManager.getNext(KeyboardLayoutManager.getById(MidnightControlsConfig.keyboardLayout)).getId();
},
option -> {
return option.getDisplayText(Text.translatable(KeyboardLayoutManager.getById(MidnightControlsConfig.keyboardLayout).getTranslationKey()));
}, TooltipData.EMPTY);
private static SpruceOption maxAnalogValueOption(String key, int axis) {
return new SpruceDoubleOption(key, .25f, 1.f, 0.05f,
() -> MidnightControlsConfig.getAxisMaxValue(axis),
newValue -> MidnightControlsConfig.setAxisMaxValue(axis, newValue),
option -> option.getDisplayText(Text.literal(String.format("%.2f", option.get()))),
Text.translatable(key.concat(".tooltip"))
TooltipData.builder().text(Text.translatable(key.concat(".tooltip"))).build()
);
}
// Touch options
@@ -158,7 +163,7 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
.append(Text.literal(GAMEPAD_TOOL_URL).formatted(Formatting.GOLD))
.append("),");
private static int searchNextAvailableController(int newId, boolean allowNone) {
public static int searchNextAvailableController(int newId, boolean allowNone) {
if ((allowNone && newId == -1) || newId == 0) return newId;
Controller candidate = Controller.byId(newId);
@@ -189,80 +194,76 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
NetworkUtil.sendPayloadC2S(new ControlsModePayload(next.getName()));
}
}, option -> option.getDisplayText(Text.translatable(MidnightControlsConfig.controlsMode.getTranslationKey())),
Text.translatable("midnightcontrols.menu.controls_mode.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.controls_mode.tooltip")).build());
this.autoSwitchModeOption = new SpruceToggleBooleanOption("midnightcontrols.menu.auto_switch_mode", () -> MidnightControlsConfig.autoSwitchMode,
value -> MidnightControlsConfig.autoSwitchMode = value, Text.translatable("midnightcontrols.menu.auto_switch_mode.tooltip"));
value -> MidnightControlsConfig.autoSwitchMode = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.auto_switch_mode.tooltip")).build());
this.rotationSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.rotation_speed", 0.0, 100.0, .5f,
() -> MidnightControlsConfig.rotationSpeed,
value -> MidnightControlsConfig.rotationSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.rotation_speed.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.rotation_speed.tooltip")).build());
this.yAxisRotationSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.y_axis_rotation_speed", 0.0, 100.0, .5f,
() -> MidnightControlsConfig.yAxisRotationSpeed,
value -> MidnightControlsConfig.yAxisRotationSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.y_axis_rotation_speed.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.y_axis_rotation_speed.tooltip")).build());
this.mouseSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.mouse_speed", 0.0, 150.0, .5f,
() -> MidnightControlsConfig.mouseSpeed,
value -> MidnightControlsConfig.mouseSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.mouse_speed.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.mouse_speed.tooltip")).build());
this.joystickAsMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.joystick_as_mouse",
() -> MidnightControlsConfig.joystickAsMouse, value -> MidnightControlsConfig.joystickAsMouse = value,
Text.translatable("midnightcontrols.menu.joystick_as_mouse.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.joystick_as_mouse.tooltip")).build());
this.eyeTrackingAsMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.eye_tracker_as_mouse",
() -> MidnightControlsConfig.eyeTrackerAsMouse, value -> MidnightControlsConfig.eyeTrackerAsMouse = value,
Text.translatable("midnightcontrols.menu.eye_tracker_as_mouse.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.eye_tracker_as_mouse.tooltip")).build());
this.eyeTrackingDeadzone = new SpruceDoubleInputOption("midnightcontrols.menu.eye_tracker_deadzone",
() -> MidnightControlsConfig.eyeTrackerDeadzone, value -> MidnightControlsConfig.eyeTrackerDeadzone = value,
Text.translatable("midnightcontrols.menu.eye_tracker_deadzone.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.eye_tracker_deadzone.tooltip")).build());
this.resetOption = SpruceSimpleActionOption.reset(btn -> {
MidnightControlsConfig.reset();
var client = MinecraftClient.getInstance();
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
});
this.advancedConfigOption = SpruceSimpleActionOption.of("midnightcontrols.midnightconfig.title", button -> client.setScreen(MidnightControlsConfig.getScreen(this, MidnightControlsConstants.NAMESPACE)));
// Gameplay options
this.analogMovementOption = new SpruceToggleBooleanOption("midnightcontrols.menu.analog_movement",
() -> MidnightControlsConfig.analogMovement, value -> MidnightControlsConfig.analogMovement = value,
Text.translatable("midnightcontrols.menu.analog_movement.tooltip"));
this.doubleTapToSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.double_tap_to_sprint",
() -> MidnightControlsConfig.doubleTapToSprint, value -> MidnightControlsConfig.doubleTapToSprint = value,
Text.translatable("midnightcontrols.menu.double_tap_to_sprint.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.analog_movement.tooltip")).build());
this.autoJumpOption = new SpruceToggleBooleanOption("options.autoJump",
() -> this.client.options.getAutoJump().getValue(),
newValue -> this.client.options.getAutoJump().setValue(newValue),
null);
TooltipData.EMPTY);
this.controllerToggleSneakOption = new SpruceToggleBooleanOption("midnightcontrols.menu.controller_toggle_sneak",
() -> MidnightControlsConfig.controllerToggleSneak, value -> MidnightControlsConfig.controllerToggleSneak = value,
null);
TooltipData.EMPTY);
this.controllerToggleSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.controller_toggle_sprint",
() -> MidnightControlsConfig.controllerToggleSprint, value -> MidnightControlsConfig.controllerToggleSprint = value,
null);
TooltipData.EMPTY);
this.fastBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fast_block_placing", () -> MidnightControlsConfig.fastBlockPlacing,
value -> MidnightControlsConfig.fastBlockPlacing = value, Text.translatable("midnightcontrols.menu.fast_block_placing.tooltip"));
value -> MidnightControlsConfig.fastBlockPlacing = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.fast_block_placing.tooltip")).build());
this.frontBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.horizontal", () -> MidnightControlsConfig.horizontalReacharound,
value -> MidnightControlsConfig.horizontalReacharound = value, Text.translatable("midnightcontrols.menu.reacharound.horizontal.tooltip"));
value -> MidnightControlsConfig.horizontalReacharound = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.reacharound.horizontal.tooltip")).build());
this.verticalReacharoundOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.vertical", () -> MidnightControlsConfig.verticalReacharound,
value -> MidnightControlsConfig.verticalReacharound = value, Text.translatable("midnightcontrols.menu.reacharound.vertical.tooltip"));
value -> MidnightControlsConfig.verticalReacharound = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.reacharound.vertical.tooltip")).build());
this.flyDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting", () -> MidnightControlsConfig.flyDrifting,
value -> MidnightControlsConfig.flyDrifting = value, Text.translatable("midnightcontrols.menu.fly_drifting.tooltip"));
value -> MidnightControlsConfig.flyDrifting = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.fly_drifting.tooltip")).build());
this.flyVerticalDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting_vertical", () -> MidnightControlsConfig.verticalFlyDrifting,
value -> MidnightControlsConfig.verticalFlyDrifting = value, Text.translatable("midnightcontrols.menu.fly_drifting_vertical.tooltip"));
value -> MidnightControlsConfig.verticalFlyDrifting = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.fly_drifting_vertical.tooltip")).build());
// Appearance options
this.controllerTypeOption = new SpruceCyclingOption("midnightcontrols.menu.controller_type",
amount -> MidnightControlsConfig.controllerType = MidnightControlsConfig.controllerType.next(),
option -> option.getDisplayText(MidnightControlsConfig.controllerType.getTranslatedText()),
Text.translatable("midnightcontrols.menu.controller_type.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.controller_type.tooltip")).build());
this.virtualMouseSkinOption = new SpruceCyclingOption("midnightcontrols.menu.virtual_mouse.skin",
amount -> MidnightControlsConfig.virtualMouseSkin = MidnightControlsConfig.virtualMouseSkin.next(),
option -> option.getDisplayText(MidnightControlsConfig.virtualMouseSkin.getTranslatedText()),
null);
TooltipData.EMPTY);
this.hudEnableOption = new SpruceToggleBooleanOption("midnightcontrols.menu.hud_enable", () -> MidnightControlsConfig.hudEnable,
MidnightControlsClient::setHudEnabled, Text.translatable("midnightcontrols.menu.hud_enable.tooltip"));
MidnightControlsClient::setHudEnabled, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.hud_enable.tooltip")).build());
this.hudSideOption = new SpruceCyclingOption("midnightcontrols.menu.hud_side",
amount -> MidnightControlsConfig.hudSide = MidnightControlsConfig.hudSide.next(),
option -> option.getDisplayText(MidnightControlsConfig.hudSide.getTranslatedText()),
Text.translatable("midnightcontrols.menu.hud_side.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.hud_side.tooltip")).build());
this.moveChatOption = new SpruceToggleBooleanOption("midnightcontrols.menu.move_chat", () -> MidnightControlsConfig.moveChat,
value -> MidnightControlsConfig.moveChat = value, Text.translatable("midnightcontrols.menu.move_chat.tooltip"));
value -> MidnightControlsConfig.moveChat = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.move_chat.tooltip")).build());
// Controller options
this.toggleControllerProfileOption = new SpruceToggleBooleanOption("midnightcontrols.menu.separate_controller_profile", () -> MidnightControlsConfig.controllerBindingProfiles.containsKey(MidnightControlsConfig.getController().getGuid()), value -> {
if (value) {
@@ -273,54 +274,56 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
MidnightControlsConfig.updateBindingsForController(MidnightControlsConfig.getController());
}
}, Text.empty());
}, TooltipData.EMPTY);
this.cameraModeOption = new SpruceCyclingOption("midnightcontrols.menu.camera_mode",
amount -> MidnightControlsConfig.cameraMode = MidnightControlsConfig.cameraMode.next(),
option -> option.getDisplayText(MidnightControlsConfig.cameraMode.getTranslatedText()),
Text.translatable("midnightcontrols.menu.camera_mode.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.camera_mode.tooltip")).build());
this.rightDeadZoneOption = new SpruceDoubleOption("midnightcontrols.menu.right_dead_zone", 0.05, 1.0, .05f,
() -> MidnightControlsConfig.rightDeadZone,
value -> MidnightControlsConfig.rightDeadZone = value, option -> {
var value = String.valueOf(option.get());
return option.getDisplayText(Text.literal(value.substring(0, Math.min(value.length(), 5))));
}, Text.translatable("midnightcontrols.menu.right_dead_zone.tooltip"));
}, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.right_dead_zone.tooltip")).build());
this.leftDeadZoneOption = new SpruceDoubleOption("midnightcontrols.menu.left_dead_zone", 0.05, 1.0, .05f,
() -> MidnightControlsConfig.leftDeadZone,
value -> MidnightControlsConfig.leftDeadZone = value, option -> {
var value = String.valueOf(option.get());
return option.getDisplayText(Text.literal(value.substring(0, Math.min(value.length(), 5))));
}, Text.translatable("midnightcontrols.menu.left_dead_zone.tooltip"));
}, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.left_dead_zone.tooltip")).build());
this.invertsRightXAxis = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_right_x_axis", () -> MidnightControlsConfig.invertRightXAxis,
value -> MidnightControlsConfig.invertRightXAxis = value, null);
value -> MidnightControlsConfig.invertRightXAxis = value, TooltipData.EMPTY);
this.invertsRightYAxis = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_right_y_axis", () -> MidnightControlsConfig.invertRightYAxis,
value -> MidnightControlsConfig.invertRightYAxis = value, null);
value -> MidnightControlsConfig.invertRightYAxis = value, TooltipData.EMPTY);
this.unfocusedInputOption = new SpruceToggleBooleanOption("midnightcontrols.menu.unfocused_input", () -> MidnightControlsConfig.unfocusedInput,
value -> MidnightControlsConfig.unfocusedInput = value, Text.translatable("midnightcontrols.menu.unfocused_input.tooltip"));
value -> MidnightControlsConfig.unfocusedInput = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.unfocused_input.tooltip")).build());
this.virtualMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.virtual_mouse", () -> MidnightControlsConfig.virtualMouse,
value -> MidnightControlsConfig.virtualMouse = value, Text.translatable("midnightcontrols.menu.virtual_mouse.tooltip"));
value -> MidnightControlsConfig.virtualMouse = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.virtual_mouse.tooltip")).build());
this.virtualKeyboardOption = new SpruceToggleBooleanOption("midnightcontrols.menu.virtual_keyboard", () -> MidnightControlsConfig.virtualMouse,
value -> MidnightControlsConfig.virtualKeyboard = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.virtual_keyboard.tooltip")).build());
this.hideCursorOption = new SpruceToggleBooleanOption("midnightcontrols.menu.hide_cursor", () -> MidnightControlsConfig.hideNormalMouse,
value -> MidnightControlsConfig.hideNormalMouse = value, Text.translatable("midnightcontrols.menu.hide_cursor.tooltip"));
value -> MidnightControlsConfig.hideNormalMouse = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.hide_cursor.tooltip")).build());
// Touch options
this.touchModeOption = new SpruceCyclingOption("midnightcontrols.menu.touch_mode",
amount -> MidnightControlsConfig.touchMode = MidnightControlsConfig.touchMode.next(),
option -> option.getDisplayText(MidnightControlsConfig.touchMode.getTranslatedText()),
Text.translatable("midnightcontrols.menu.touch_mode.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.touch_mode.tooltip")).build());
this.touchWithControllerOption = new SpruceToggleBooleanOption("midnightcontrols.menu.touch_with_controller", () -> MidnightControlsConfig.touchInControllerMode,
value -> MidnightControlsConfig.touchInControllerMode = value, Text.translatable("midnightcontrols.menu.touch_with_controller.tooltip"));
value -> MidnightControlsConfig.touchInControllerMode = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.touch_with_controller.tooltip")).build());
this.touchSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.touch_speed", 0.0, 150.0, .5f,
() -> MidnightControlsConfig.touchSpeed,
value -> MidnightControlsConfig.touchSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.touch_speed.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.touch_speed.tooltip")).build());
this.touchBreakDelayOption = new SpruceDoubleOption("midnightcontrols.menu.touch_break_delay", 50, 500, 1f,
() -> (double) MidnightControlsConfig.touchBreakDelay,
value -> MidnightControlsConfig.touchBreakDelay = value.intValue(), option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip")).build());
this.touchTransparencyOption = new SpruceDoubleOption("midnightcontrols.menu.touch_transparency", 0, 100, 1f,
() -> (double) MidnightControlsConfig.touchTransparency,
value -> MidnightControlsConfig.touchTransparency = value.intValue(), option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip"));
TooltipData.builder().text(Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip")).build());
this.invertTouchOption = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_touch", () -> MidnightControlsConfig.invertTouch,
value -> MidnightControlsConfig.invertTouch = value, Text.translatable("midnightcontrols.menu.invert_touch.tooltip"));
value -> MidnightControlsConfig.invertTouch = value, TooltipData.builder().text(Text.translatable("midnightcontrols.menu.invert_touch.tooltip")).build());
}
@Override
@@ -389,10 +392,11 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
list.addSingleOptionEntry(this.yAxisRotationSpeedOption);
list.addSingleOptionEntry(this.mouseSpeedOption);
list.addSingleOptionEntry(this.virtualMouseOption);
list.addSingleOptionEntry(this.virtualKeyboardOption);
list.addSingleOptionEntry(this.virtualKeyboardLayoutOption);
list.addSingleOptionEntry(this.hideCursorOption);
list.addSingleOptionEntry(this.joystickAsMouseOption);
list.addSingleOptionEntry(this.eyeTrackingAsMouseOption);
list.addSingleOptionEntry(this.advancedConfigOption);
return list;
}
@@ -400,7 +404,6 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
var list = new SpruceOptionListWidget(Position.origin(), width, height);
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.analogMovementOption);
list.addSingleOptionEntry(this.doubleTapToSprintOption);
list.addSingleOptionEntry(this.controllerToggleSneakOption);
list.addSingleOptionEntry(this.controllerToggleSprintOption);
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.fastBlockPlacingOption);
@@ -417,7 +420,7 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.controllerTypeOption);
list.addSingleOptionEntry(this.virtualMouseSkinOption);
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, null));
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, TooltipData.EMPTY));
list.addSingleOptionEntry(this.hudEnableOption);
list.addSingleOptionEntry(this.hudSideOption);
list.addSingleOptionEntry(this.moveChatOption);
@@ -433,17 +436,17 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
var aboutMappings1 = new SpruceLabelWidget(Position.of(0, 2),
Text.translatable("midnightcontrols.controller.mappings.1", SDL2_GAMEPAD_TOOL),
width, true);
width);
var gamepadToolUrlLabel = new SpruceLabelWidget(Position.of(0, aboutMappings1.getHeight() + 4),
this.controllerMappingsUrlText, width,
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true);
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL));
gamepadToolUrlLabel.setTooltip(Text.translatable("chat.link.open"));
var aboutMappings3 = new SpruceLabelWidget(Position.of(0,
aboutMappings1.getHeight() + gamepadToolUrlLabel.getHeight() + 6),
Text.translatable("midnightcontrols.controller.mappings.3", Formatting.GREEN.toString(), Formatting.RESET.toString()),
width, true);
width);
int listHeight = height - 8 - aboutMappings1.getHeight() - aboutMappings3.getHeight() - gamepadToolUrlLabel.getHeight();
var labels = new SpruceContainerWidget(Position.of(0,
@@ -477,7 +480,7 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
list.addSingleOptionEntry(this.touchSpeedOption);
list.addSingleOptionEntry(this.touchWithControllerOption);
list.addSingleOptionEntry(this.invertTouchOption);
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, null));
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, TooltipData.EMPTY));
list.addSingleOptionEntry(this.touchModeOption);
list.addSingleOptionEntry(this.touchBreakDelayOption);
list.addSingleOptionEntry(this.touchTransparencyOption);
@@ -489,39 +492,20 @@ public class MidnightControlsSettingsScreen extends SpruceScreen {
}
@Override
public void renderTitle(DrawContext context, int mouseX, int mouseY, float delta) {
context.drawCenteredTextWithShadow(this.textRenderer, I18n.translate("midnightcontrols.menu.title"), this.width / 2, 8, 16777215);
public void render(SpruceGuiGraphics context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
context.vanilla().drawCenteredTextWithShadow(this.textRenderer, I18n.translate("midnightcontrols.menu.title"), this.width / 2, 8, 0xFFFFFFFF);
}
public static class MidnightControlsBackground implements Background {
private static int transparency = 160;
private int transparency = 160;
public MidnightControlsBackground() {}
public MidnightControlsBackground(int transparency) {
MidnightControlsBackground.transparency = transparency;
this.transparency = transparency;
}
@Override
public void render(DrawContext context, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) {
fill(context.getMatrices(), widget.getX(), widget.getY(), widget.getX() + widget.getWidth(), widget.getY() + widget.getHeight(), MidnightColorUtil.hex2Rgb("#000000"));
}
private static void fill(MatrixStack matrixStack, int x2, int y2, int x1, int y1, Color color) {
matrixStack.push();
Matrix4f matrix = matrixStack.peek().getPositionMatrix();
float r = (float)(color.getRed()) / 255.0F;
float g = (float)(color.getGreen()) / 255.0F;
float b = (float)(color.getBlue()) / 255.0F;
float t = (float)(transparency) / 255.0F;
BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
//RenderSystem.setShader(GameRenderer::getPositionColorProgram);
bufferBuilder.vertex(matrix, (float)x1, (float)y2, 0.0F).color(r, g, b, t);
bufferBuilder.vertex(matrix, (float)x2, (float)y2, 0.0F).color(r, g, b, t);
bufferBuilder.vertex(matrix, (float)x2, (float)y1, 0.0F).color(r, g, b, t);
bufferBuilder.vertex(matrix, (float)x1, (float)y1, 0.0F).color(r, g, b, t);
BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
RenderSystem.disableBlend();
matrixStack.pop();
public void render(SpruceGuiGraphics context, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) {
context.fill(widget.getX(), widget.getY(), widget.getX() + widget.getWidth(), widget.getY() + widget.getHeight(), ColorHelper.getArgb(transparency, 0, 0, 0));
}
}
}

View File

@@ -9,9 +9,10 @@
package eu.midnightdust.midnightcontrols.client.gui;
import dev.lambdaurora.spruceui.option.SpruceSimpleActionOption;
import dev.lambdaurora.spruceui.tooltip.TooltipData;
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import org.thinkingstudio.obsidianui.option.SpruceSimpleActionOption;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
@@ -35,6 +36,6 @@ public class ReloadControllerMappingsOption {
client.currentScreen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION,
Text.translatable("midnightcontrols.controller.mappings.updated"), Text.empty()));
}, Text.translatable("midnightcontrols.tooltip.reload_controller_mappings"));
}, TooltipData.builder().text(Text.translatable("midnightcontrols.tooltip.reload_controller_mappings")).build());
}
}

View File

@@ -12,6 +12,7 @@ package eu.midnightdust.midnightcontrols.client.gui;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.ring.RingButtonMode;
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
import net.minecraft.client.gui.Click;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
@@ -79,8 +80,8 @@ public class RingScreen extends Screen {
// }
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
if (ring.getCurrentPage().onClick(width, height, (int) mouseX, (int) mouseY)) {
public boolean mouseReleased(Click click) {
if (ring.getCurrentPage().onClick(width, height, (int) click.x(), (int) click.y())) {
this.close();
return true;
}

View File

@@ -0,0 +1,160 @@
package eu.midnightdust.midnightcontrols.client.gui.config;
import com.google.common.collect.Lists;
import dev.lambdaurora.spruceui.SpruceTexts;
import eu.midnightdust.lib.config.EntryInfo;
import eu.midnightdust.lib.config.MidnightConfigListWidget;
import eu.midnightdust.lib.config.MidnightConfigScreen;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.client.input.AbstractInput;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class ControllerBindingButton extends ButtonWidget implements ControlsInput {
private static final int[] UNBOUND = new int[]{-1};
static boolean waiting = false;
static List<Integer> currentButtons = new ArrayList<>();
private int iconWidth;
public static void add(ButtonBinding binding, MidnightConfigListWidget list, MidnightConfigScreen screen) {
ControllerBindingButton editButton = new ControllerBindingButton(screen.width - 185 + 22, 0, 128, 20, binding);
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton());
MidnightControlsClient.input.beginControlsInput(null);
editButton.updateMessage(false);
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
resetButton.setPosition(screen.width - 205 + 150 + 25, 0);
editButton.resetButton = resetButton;
editButton.updateMessage(false);
EntryInfo info = new EntryInfo(null, screen.modid);
TextIconButtonWidget unbindButton = TextIconButtonWidget.builder(Text.translatable("midnightcontrols.narrator.unbound", binding.getText()), (button -> {
MidnightControlsConfig.setButtonBinding(binding, UNBOUND);
MidnightControlsClient.input.beginControlsInput(null);
editButton.updateMessage(false);
}), true).texture(Identifier.of("midnightcontrols","icon/unbind"), 12, 12).dimension(20, 20).build();
unbindButton.setPosition(screen.width - 205 + 20, 0);
unbindButton.setTooltip(Tooltip.of(SpruceTexts.GUI_UNBIND));
unbindButton.active = !binding.isNotBound();
editButton.unbindButton = unbindButton;
list.addButton(Lists.newArrayList(editButton, resetButton, unbindButton), Text.translatable(binding.getTranslationKey()), info);
}
private final ButtonBinding binding;
private @Nullable ClickableWidget resetButton;
private @Nullable ClickableWidget unbindButton;
public ControllerBindingButton(int x, int y, int width, int height, ButtonBinding binding) {
super(x, y, width, height, binding.getText(), (button) -> {},
(textSupplier) -> binding.isNotBound() ? Text.translatable("narrator.controls.unbound", binding.getTranslationKey()) : Text.translatable("narrator.controls.bound", binding.getTranslationKey(), textSupplier.get()));
this.binding = binding;
updateMessage(false);
}
@Override
public void onPress(AbstractInput input) {
MidnightControlsClient.input.beginControlsInput(this);
this.updateMessage(true);
}
public void updateMessage(boolean focused) {
AtomicBoolean hasConflicts = new AtomicBoolean(false);
MutableText conflictingBindings = Text.empty();
if (focused) this.setMessage(Text.literal("> ").append(getTranslatedButtons().copy().formatted(Formatting.WHITE, Formatting.UNDERLINE)).append(" <").formatted(Formatting.YELLOW));
else {
this.setMessage(getTranslatedButtons());
if (!this.binding.isNotBound()) {
InputManager.streamBindings().forEach(keyBinding -> {
if (keyBinding != this.binding && this.binding.equals(keyBinding)) {
if (hasConflicts.get()) conflictingBindings.append(", ");
hasConflicts.set(true);
conflictingBindings.append(Text.translatable(keyBinding.getTranslationKey()));
}
});
}
}
if (this.resetButton != null) this.resetButton.active = !this.binding.isDefault();
if (this.unbindButton != null) this.unbindButton.active = !binding.isNotBound();
if (hasConflicts.get()) {
this.setMessage(Text.literal("[ ").append(this.getMessage().copy().formatted(Formatting.WHITE)).append(" ]").formatted(Formatting.RED));
this.setTooltip(Tooltip.of(Text.translatable("controls.keybinds.duplicateKeybinds", conflictingBindings)));
} else {
this.setTooltip(null);
}
}
private Text getTranslatedButtons() {
return this.binding.isNotBound() ? SpruceTexts.NOT_BOUND.copy() :
(binding.getButton().length > 0 ? ButtonBinding.getLocalizedButtonName(binding.getButton()[0]) : Text.literal("..."));
}
@Override
public void drawMessage(DrawContext context, TextRenderer textRenderer, int color) {
if (this.binding.getButton().length < 2) super.drawMessage(context, textRenderer, color);
}
@Override
protected void renderWidget(DrawContext context, int mouseX, int mouseY, float deltaTicks) {
super.renderWidget(context, mouseX, mouseY, deltaTicks);
int x = this.getX();
if (this.binding.getButton().length > 1) {
x += (this.width / 2 - iconWidth / 2) - 4;
}
var size = MidnightControlsRenderer.drawButton(context, x, this.getY(), this.binding, MinecraftClient.getInstance());
iconWidth = size.length();
}
@Override
public void finishBindingEdit(int... buttons) {
MidnightControlsConfig.setButtonBinding(binding, buttons);
updateMessage(false);
}
@Override
public void update() {
this.updateMessage(true);
}
@Override
public void setWaiting(boolean value) {
waiting = value;
}
@Override
public boolean isWaiting() {
return waiting;
}
@Override
public List<Integer> getCurrentButtons() {
return currentButtons;
}
@Override
public ButtonBinding getFocusedBinding() {
return this.binding;
}
}

View File

@@ -0,0 +1,64 @@
package eu.midnightdust.midnightcontrols.client.gui.config;
import dev.lambdaurora.spruceui.SpruceTexts;
import eu.midnightdust.lib.config.EntryInfo;
import eu.midnightdust.lib.config.MidnightConfigListWidget;
import eu.midnightdust.lib.config.MidnightConfigScreen;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import org.lwjgl.glfw.GLFW;
import java.util.List;
import static eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen.searchNextAvailableController;
public class ControllerSelectionButton {
public static void add(MidnightConfigListWidget list, MidnightConfigScreen screen, boolean second) {
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
if (second) MidnightControlsConfig.secondControllerID = -1;
else MidnightControlsConfig.controllerID = 0;
screen.updateList();
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
resetButton.setPosition(screen.width - 205 + 150 + 25, 0);
ButtonWidget editButton = ButtonWidget.builder(getControllerName(second),
button -> {
int id = second ? MidnightControlsConfig.getSecondController().map(Controller::id).orElse(-1) : MidnightControlsConfig.getController().id();
id += 1;
if (id > GLFW.GLFW_JOYSTICK_LAST)
id = GLFW.GLFW_JOYSTICK_1;
id = searchNextAvailableController(id, second);
if (second) {
MidnightControlsConfig.setSecondController(Controller.byId(id));
} else {
MidnightControlsConfig.setController(Controller.byId(id));
}
if (MidnightControlsConfig.debug && id != -1) System.out.println(Controller.byId(id).getName() + "'s Controller GUID: " + Controller.byId(id).getGuid());
resetButton.active = second ? MidnightControlsConfig.getSecondController().isPresent() : false;
button.setMessage(getControllerName(second));
}).dimensions(screen.width - 185, 0, 150, 20).build();
resetButton.active = second ? MidnightControlsConfig.getSecondController().isPresent() : false;
if (second) editButton.setTooltip(Tooltip.of(Text.translatable("midnightcontrols.menu.controller2.tooltip")));
list.addButton(List.of(editButton, resetButton), Text.translatable(second ? "midnightcontrols.menu.controller2" : "midnightcontrols.menu.controller"), new EntryInfo(null, screen.modid));
}
private static Text getControllerName(boolean second) {
if (second && MidnightControlsConfig.getSecondController().isEmpty()) return SpruceTexts.OPTIONS_OFF.copyContentOnly().formatted(Formatting.RED);
var controller = second ? MidnightControlsConfig.getSecondController().get() : MidnightControlsConfig.getController();
var controllerName = controller.getName();
if (!controller.isConnected())
return Text.literal(controllerName).formatted(Formatting.RED);
else if (!controller.isGamepad())
return Text.literal(controllerName).formatted(Formatting.GOLD);
else
return Text.literal(controllerName);
}
}

View File

@@ -0,0 +1,18 @@
package eu.midnightdust.midnightcontrols.client.gui.config;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import java.util.List;
public interface ControlsInput {
void setWaiting(boolean value);
boolean isWaiting();
List<Integer> getCurrentButtons();
ButtonBinding getFocusedBinding();
void finishBindingEdit(int[] buttons);
default void update() {};
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui.config;
import com.google.common.collect.Lists;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.widget.text.SpruceTextAreaWidget;
import eu.midnightdust.lib.config.EntryInfo;
import eu.midnightdust.lib.config.MidnightConfigListWidget;
import eu.midnightdust.lib.config.MidnightConfigScreen;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.client.gui.widget.*;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import java.util.List;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
/**
* Represents the controller mappings file editor screen.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.4.3
*/
public class MappingsStringInputWidget {
public static void add(EntryInfo centered, MidnightConfigListWidget list, MidnightConfigScreen screen) {
//SpruceTextAreaWidget editButton = new SpruceTextAreaWidget(Position.of(0, 0), 20, 20, Text.empty());
MultilineTextFieldWidget editButton = new MultilineTextFieldWidget(screen.getTextRenderer(), screen.width / 2 - 128, 0, 256, 60, Text.of("TESTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\nnew line!"));
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
screen.updateList();
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 60).build();
resetButton.setPosition(screen.width / 2 - 128 + 256 + 4, 0);
editButton.setChangedListener(string -> {
resetButton.active = !string.isEmpty();
});
list.addButton(List.of(), Text.translatable("midnightcontrols.menu.title.mappings.string"), centered);
//screen.addDrawableChild(editButton);
list.addButton(Lists.newArrayList(editButton, resetButton), Text.empty(), centered);
list.addButton(List.of(), Text.empty(), centered);
list.addButton(List.of(), Text.empty(), centered);
list.addButton(List.of(), Text.translatable("midnightcontrols.menu.multiple_mapping_tip"), centered);
//list.addButton();
ButtonWidget copyButton = TextIconButtonWidget.builder(Text.of("Copy GUID"), widget -> {
client.keyboard.setClipboard(MidnightControlsConfig.getController().getGuid());
}).dimensions(screen.width - 185, 0, 150, 20).build();
list.addButton(List.of(copyButton), Text.translatable("midnightcontrols.menu.current_controller_guid", MidnightControlsConfig.getController().getGuid()), new EntryInfo(null, screen.modid));
}
}

View File

@@ -0,0 +1,13 @@
package eu.midnightdust.midnightcontrols.client.gui.config;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.widget.ScrollableTextWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
public class MultilineTextFieldWidget extends TextFieldWidget {
public MultilineTextFieldWidget(TextRenderer textRenderer, int x, int y, int width, int height, Text text) {
super(textRenderer, x, y, width, height, text);
}
// TODO
}

View File

@@ -0,0 +1,24 @@
package eu.midnightdust.midnightcontrols.client.gui.cursor;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import eu.midnightdust.midnightcontrols.client.mixin.DrawContextAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.cursor.Cursor;
import net.minecraft.client.texture.TextureSetup;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix3x2f;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
public abstract class CursorRenderer {
public static Cursor currentCursorStyle = Cursor.DEFAULT;
public abstract void renderCursor(@NotNull DrawContext context, @NotNull MinecraftClient client);
public static void drawUnalignedTexturedQuad(RenderPipeline pipeline, Identifier texture, DrawContext context, float x1, float x2, float y1, float y2, float u1, float u2, float v1, float v2) {
DrawContextAccessor accessor = (DrawContextAccessor) context;
accessor.getState().addSimpleElement(new UnalignedTexturedQuadGuiElementRenderState(pipeline, TextureSetup.withoutGlTexture(client.getTextureManager().getTexture(texture).getGlTextureView()), new Matrix3x2f(context.getMatrices()), x1, y1, x2, y2, u1, u2, v1, v2, 0xffffffff, accessor.getScissorStack().peekLast()));
}
}

View File

@@ -0,0 +1,32 @@
package eu.midnightdust.midnightcontrols.client.gui.cursor;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.ScreenRect;
import net.minecraft.client.gui.render.state.SimpleGuiElementRenderState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.texture.TextureSetup;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3x2f;
@Environment(EnvType.CLIENT)
public record UnalignedTexturedQuadGuiElementRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x1, float y1, float x2, float y2, float u1, float u2, float v1, float v2, int color, @Nullable ScreenRect scissorArea, @Nullable ScreenRect bounds) implements SimpleGuiElementRenderState {
public UnalignedTexturedQuadGuiElementRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x1, float y1, float x2, float y2, float u1, float u2, float v1, float v2, int color, @Nullable ScreenRect scissorArea) {
this(pipeline, textureSetup, pose, x1, y1, x2, y2, u1, u2, v1, v2, color, scissorArea, createBounds(x1, y1, x2, y2, pose, scissorArea));
}
@Override
public void setupVertices(VertexConsumer vertices) {
vertices.vertex(pose(), x1(), y1()).texture(u1(), v1()).color(color());
vertices.vertex(pose(), x1(), y2()).texture(u1(), v2()).color(color());
vertices.vertex(pose(), x2(), y2()).texture(u2(), v2()).color(color());
vertices.vertex(pose(), x2(), y1()).texture(u2(), v1()).color(color());
}
@Nullable
private static ScreenRect createBounds(float x1, float y1, float x2, float y2, Matrix3x2f pose, @Nullable ScreenRect scissorArea) {
ScreenRect screenRect = (new ScreenRect((int) x1, (int) y1, (int) (x2 - x1), (int) (y2 - y1))).transformEachVertex(pose);
return scissorArea != null ? scissorArea.intersection(screenRect) : screenRect;
}
}

View File

@@ -0,0 +1,67 @@
package eu.midnightdust.midnightcontrols.client.gui.cursor;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.texture.Sprite;
import net.minecraft.screen.slot.Slot;
import net.minecraft.util.Atlases;
import org.jetbrains.annotations.NotNull;
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
public class VirtualCursorRenderer extends CursorRenderer {
private static final VirtualCursorRenderer INSTANCE = new VirtualCursorRenderer();
public static VirtualCursorRenderer getInstance() {
return INSTANCE;
}
public void renderCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null
|| MidnightInput.isScreenInteractive(client.currentScreen)))
return;
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
boolean hoverSlot = false;
if (client.currentScreen instanceof HandledScreenAccessor inventoryScreen) {
int guiLeft = inventoryScreen.getX();
int guiTop = inventoryScreen.getY();
Slot slot = inventoryScreen.midnightcontrols$getSlotAt(mouseX, mouseY);
if (slot != null) {
mouseX = guiLeft + slot.x;
mouseY = guiTop + slot.y;
hoverSlot = true;
}
}
if (!hoverSlot && client.currentScreen != null) {
var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, (int) mouseX, (int) mouseY);
if (slot != null) {
mouseX = slot.x();
mouseY = slot.y();
hoverSlot = true;
}
}
if (!hoverSlot) {
mouseX -= 8;
mouseY -= 8;
}
try {
Sprite sprite = client.getAtlasManager().getAtlasTexture(Atlases.GUI).getSprite(id(MidnightControlsConfig.virtualMouseSkin.getSpritePath() + (hoverSlot ? "_slot" : "")));
drawUnalignedTexturedQuad(RenderPipelines.GUI_TEXTURED, sprite.getAtlasId(), context, mouseX, mouseX + 16, mouseY, mouseY + 16, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
} catch (IllegalStateException ignored) {}
}
}

View File

@@ -0,0 +1,61 @@
package eu.midnightdust.midnightcontrols.client.gui.cursor;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.enums.VirtualMouseSkin;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.cursor.StandardCursors;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.Atlases;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
public class WaylandCursorRenderer extends CursorRenderer {
private static final WaylandCursorRenderer INSTANCE = new WaylandCursorRenderer();
public static final Identifier WAYLAND_CURSOR_ARROW_LIGHT = id("cursor/light/mouse_arrow");
public static final Identifier WAYLAND_CURSOR_ARROW_DARK = id("cursor/dark/mouse_arrow");
public static final Identifier WAYLAND_CURSOR_POINTING_LIGHT = id("cursor/light/mouse_pointing_hand");
public static final Identifier WAYLAND_CURSOR_POINTING_DARK = id("cursor/dark/mouse_pointing_hand");
public static final Identifier WAYLAND_CURSOR_IBEAM_LIGHT = id("cursor/light/mouse_ibeam");
public static final Identifier WAYLAND_CURSOR_IBEAM_DARK = id("cursor/dark/mouse_ibeam");
public static final Identifier WAYLAND_CURSOR_RESIZE_VERTICAL_LIGHT = id("cursor/light/mouse_resize_vertical");
public static final Identifier WAYLAND_CURSOR_REZIZE_VERTICAL_DARK = id("cursor/dark/mouse_resize_vertical");
public static final Identifier WAYLAND_CURSOR_RESIZE_HORIZONTAL_LIGHT = id("cursor/light/mouse_resize_horizontal");
public static final Identifier WAYLAND_CURSOR_REZIZE_HORIZONTAL_DARK = id("cursor/dark/mouse_resize_horizontal");
public static final Identifier WAYLAND_CURSOR_NOT_ALLOWED_LIGHT = id("cursor/light/mouse_not_allowed");
public static final Identifier WAYLAND_CURSOR_NOT_ALLOWED_DARK = id("cursor/dark/mouse_not_allowed");
public static WaylandCursorRenderer getInstance() {
return INSTANCE;
}
public void renderCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (MidnightControlsConfig.virtualMouse || client.currentScreen == null || MidnightControlsConfig.controlsMode != ControlsMode.CONTROLLER) return;
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
try {
Sprite sprite = getSprite(client);
drawUnalignedTexturedQuad(RenderPipelines.GUI_TEXTURED, sprite.getAtlasId(), context, mouseX - 2, mouseX + 6, mouseY - 2, mouseY + 6, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
} catch (IllegalStateException ignored) {}
}
private static Sprite getSprite(@NotNull MinecraftClient client) {
boolean isDark = MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.DEFAULT_DARK || MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.SECOND_DARK;
Identifier spritePath;
if (CursorRenderer.currentCursorStyle == StandardCursors.POINTING_HAND) spritePath = isDark ? WAYLAND_CURSOR_POINTING_DARK : WAYLAND_CURSOR_POINTING_LIGHT;
else if (CursorRenderer.currentCursorStyle == StandardCursors.IBEAM) spritePath = isDark ? WAYLAND_CURSOR_IBEAM_DARK : WAYLAND_CURSOR_IBEAM_LIGHT;
else if (CursorRenderer.currentCursorStyle == StandardCursors.RESIZE_NS) spritePath = isDark ? WAYLAND_CURSOR_REZIZE_VERTICAL_DARK : WAYLAND_CURSOR_RESIZE_VERTICAL_LIGHT;
else if (CursorRenderer.currentCursorStyle == StandardCursors.RESIZE_EW) spritePath = isDark ? WAYLAND_CURSOR_REZIZE_HORIZONTAL_DARK : WAYLAND_CURSOR_RESIZE_HORIZONTAL_LIGHT;
else if (CursorRenderer.currentCursorStyle == StandardCursors.NOT_ALLOWED) spritePath = isDark ? WAYLAND_CURSOR_NOT_ALLOWED_DARK : WAYLAND_CURSOR_NOT_ALLOWED_LIGHT;
else spritePath = isDark ? WAYLAND_CURSOR_ARROW_DARK : WAYLAND_CURSOR_ARROW_LIGHT;
return client.getAtlasManager().getAtlasTexture(Atlases.GUI).getSprite(spritePath);
}
}

View File

@@ -9,11 +9,12 @@
package eu.midnightdust.midnightcontrols.client.gui.widget;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.SpruceTexts;
import dev.lambdaurora.spruceui.render.SpruceGuiGraphics;
import dev.lambdaurora.spruceui.widget.AbstractSpruceIconButtonWidget;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.widget.AbstractSpruceIconButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.text.Text;
@@ -45,12 +46,12 @@ public class ControllerButtonWidget extends AbstractSpruceIconButtonWidget {
}
@Override
protected int renderIcon(DrawContext context, int mouseX, int mouseY, float delta) {
protected int renderIcon(SpruceGuiGraphics spruceGuiGraphics, int mouseX, int mouseY, float delta) {
int x = this.getX();
if (this.binding.getButton().length > 1) {
x += (this.width / 2 - this.iconWidth / 2) - 4;
}
var size = MidnightControlsRenderer.drawButton(context, x, this.getY(), this.binding, MinecraftClient.getInstance());
var size = MidnightControlsRenderer.drawButton(spruceGuiGraphics.vanilla(), x, this.getY(), this.binding, MinecraftClient.getInstance());
this.iconWidth = size.length();
return size.height();
}

View File

@@ -9,13 +9,15 @@
package eu.midnightdust.midnightcontrols.client.gui.widget;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.SpruceTexts;
import dev.lambdaurora.spruceui.render.SpruceGuiGraphics;
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
import eu.midnightdust.midnightcontrols.client.gui.config.ControlsInput;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
@@ -29,11 +31,11 @@ import java.util.stream.Collectors;
/**
* Represents the controls screen.
*/
public class ControllerControlsWidget extends SpruceContainerWidget {
public class ControllerControlsWidget extends SpruceContainerWidget implements ControlsInput {
private SpruceButtonWidget resetButton;
public ButtonBinding focusedBinding;
public boolean waiting = false;
public List<Integer> currentButtons = new ArrayList<>();
ButtonBinding focusedBinding = null;
boolean waiting = false;
List<Integer> currentButtons = new ArrayList<>();
public ControllerControlsWidget(Position position, int width, int height) {
super(position, width, height);
@@ -54,9 +56,9 @@ public class ControllerControlsWidget extends SpruceContainerWidget {
}
@Override
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.title.controller_controls"),
this.getX() + this.width / 2, this.getY() + 4, 16777215);
public void renderWidget(SpruceGuiGraphics context, int mouseX, int mouseY, float delta) {
context.vanilla().drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.title.controller_controls"),
this.getX() + this.width / 2, this.getY() + 4, 0xFFFFFFFF);
this.resetButton.setActive(InputManager.streamBindings().anyMatch(Predicates.not(ButtonBinding::isDefault)));
super.renderWidget(context, mouseX, mouseY, delta);
}
@@ -66,4 +68,24 @@ public class ControllerControlsWidget extends SpruceContainerWidget {
MidnightControlsConfig.setButtonBinding(this.focusedBinding, buttons);
this.focusedBinding = null;
}
@Override
public void setWaiting(boolean value) {
this.waiting = value;
}
@Override
public boolean isWaiting() {
return this.waiting;
}
@Override
public List<Integer> getCurrentButtons() {
return currentButtons;
}
@Override
public ButtonBinding getFocusedBinding() {
return focusedBinding;
}
}

View File

@@ -9,20 +9,29 @@
package eu.midnightdust.midnightcontrols.client.gui.widget;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.SpruceTexts;
import dev.lambdaurora.spruceui.navigation.NavigationEvent;
import dev.lambdaurora.spruceui.navigation.NavigationUtils;
import dev.lambdaurora.spruceui.render.SpruceGuiGraphics;
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
import dev.lambdaurora.spruceui.widget.SpruceIconButtonWidget;
import dev.lambdaurora.spruceui.widget.SpruceSeparatorWidget;
import dev.lambdaurora.spruceui.widget.SpruceWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceEntryListWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceParentWidget;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.navigation.NavigationDirection;
import org.thinkingstudio.obsidianui.navigation.NavigationUtils;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import org.thinkingstudio.obsidianui.widget.SpruceSeparatorWidget;
import org.thinkingstudio.obsidianui.widget.SpruceWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceEntryListWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceParentWidget;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.Click;
import net.minecraft.client.gui.navigation.NavigationAxis;
import net.minecraft.client.gui.navigation.NavigationDirection;
import net.minecraft.client.input.KeyInput;
import net.minecraft.client.input.MouseInput;
import net.minecraft.util.Identifier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.DrawContext;
@@ -86,7 +95,7 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
super(parent);
this.binding = binding;
this.bindingName = I18n.translate(this.binding.getTranslationKey());
this.editButton = new ControllerButtonWidget(Position.of(this, parent.getWidth() / 2 - 8, 0), 110, this.binding, btn -> {
this.editButton = new ControllerButtonWidget(Position.of(this, parent.getWidth() / 2 - 8, 0), 120, this.binding, btn -> {
gui.focusedBinding = binding;
MidnightControlsClient.input.beginControlsInput(gui);
}) {
@@ -96,13 +105,24 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
}
};
this.children.add(editButton);
this.resetButton = new SpruceButtonWidget(Position.of(this,
this.resetButton = new SpruceIconButtonWidget(Position.of(this,
this.editButton.getPosition().getRelativeX() + this.editButton.getWidth() + 2, 0),
44, 20, Text.translatable("controls.reset"),
37, 20, Text.empty(),
btn -> MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton())) {
protected Text getNarrationMessage() {
return Text.translatable("narrator.controls.reset", bindingName);
}
private final Identifier resetTexture = Identifier.of("midnightlib","icon/reset");
@Override
protected int renderIcon(SpruceGuiGraphics context, int mouseX, int mouseY, float delta) {
int size = 12;
int x = this.getX() + this.getWidth() / 2 - size / 2;
int y = this.getY() + this.getHeight() / 2 - size / 2;
context.vanilla().drawGuiTexture(RenderPipelines.GUI_TEXTURED, resetTexture, x, y, size, size);
return 1;
}
};
this.children.add(this.resetButton);
this.unbindButton = new SpruceButtonWidget(Position.of(this,
@@ -150,7 +170,7 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
/* Input */
@Override
protected boolean onMouseClick(double mouseX, double mouseY, int button) {
protected boolean onMouseClick(Click click, boolean doubleClick) {
var it = this.children().iterator();
SpruceWidget element;
@@ -160,30 +180,30 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
}
element = it.next();
} while (!element.mouseClicked(mouseX, mouseY, button));
} while (!element.mouseClicked(click, false));
this.setFocused(element);
if (button == GLFW.GLFW_MOUSE_BUTTON_1)
if (click.button() == GLFW.GLFW_MOUSE_BUTTON_1)
this.dragging = true;
return true;
}
@Override
protected boolean onMouseRelease(double mouseX, double mouseY, int button) {
protected boolean onMouseRelease(Click click) {
this.dragging = false;
return this.hoveredElement(mouseX, mouseY).filter(element -> element.mouseReleased(mouseX, mouseY, button)).isPresent();
return this.hoveredElement(click.x(), click.y()).filter(element -> element.mouseReleased(click)).isPresent();
}
@Override
protected boolean onMouseDrag(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
return this.getFocused() != null && this.dragging && button == GLFW.GLFW_MOUSE_BUTTON_1
&& this.getFocused().mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
protected boolean onMouseDrag(@NotNull Click click, double deltaX, double deltaY) {
return this.getFocused() != null && this.dragging && click.button() == GLFW.GLFW_MOUSE_BUTTON_1
&& this.getFocused().mouseDragged(click, deltaX, deltaY);
}
@Override
protected boolean onKeyPress(int keyCode, int scanCode, int modifiers) {
return this.focused != null && this.focused.keyPressed(keyCode, scanCode, modifiers);
protected boolean onKeyPress(@NotNull KeyInput input) {
return this.focused != null && this.focused.keyPressed(input);
}
/* Navigation */
@@ -197,9 +217,9 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
}
@Override
public boolean onNavigation(@NotNull NavigationDirection direction, boolean tab) {
public boolean onNavigation(NavigationEvent event) {
if (this.requiresCursor()) return false;
if (!tab && direction.isVertical()) {
if (!event.tab() && event.direction().getAxis() == NavigationAxis.VERTICAL) {
if (this.isFocused()) {
this.setFocused(null);
return false;
@@ -207,16 +227,16 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
int lastIndex = this.parent.lastIndex;
if (lastIndex >= this.children.size())
lastIndex = this.children.size() - 1;
if (!this.children.get(lastIndex).onNavigation(direction, tab))
if (!this.children.get(lastIndex).onNavigation(event))
return false;
this.setFocused(this.children.get(lastIndex));
return true;
}
boolean result = NavigationUtils.tryNavigate(direction, tab, this.children, this.focused, this::setFocused, true);
boolean result = NavigationUtils.tryNavigate(event, this.children, this.focused, this::setFocused, true);
if (result) {
this.setFocused(true);
if (direction.isHorizontal() && this.getFocused() != null) {
if (event.direction().getAxis() == NavigationAxis.HORIZONTAL && this.getFocused() != null) {
this.parent.lastIndex = this.children.indexOf(this.getFocused());
}
}
@@ -226,14 +246,14 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
/* Rendering */
@Override
protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
protected void renderWidget(SpruceGuiGraphics context, int mouseX, int mouseY, float delta) {
boolean focused = gui.focusedBinding == this.binding;
var textRenderer = ControlsListWidget.this.client.textRenderer;
int height = this.getHeight();
//float textX = (float) (this.getX() + 70 - ControlsListWidget.this.maxTextLength);
int textY = this.getY() + height / 2;
context.drawText(textRenderer, this.bindingName, this.getX(), (textY - 9 / 2), 16777215, true);
context.drawText(textRenderer, this.bindingName, this.getX(), (textY - 9 / 2), 0xFFFFFFFF, true);
this.resetButton.setVisible(!focused);
this.unbindButton.setVisible(focused);
@@ -282,14 +302,14 @@ public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget
/* Navigation */
@Override
public boolean onNavigation(@NotNull NavigationDirection direction, boolean tab) {
return this.separatorWidget.onNavigation(direction, tab);
public boolean onNavigation(NavigationEvent event) {
return this.separatorWidget.onNavigation(event);
}
/* Rendering */
@Override
protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
protected void renderWidget(SpruceGuiGraphics context, int mouseX, int mouseY, float delta) {
this.separatorWidget.render(context, mouseX, mouseY, delta);
}

View File

@@ -0,0 +1,14 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(AbstractBlock.class)
public interface AbstractBlockAccessor {
@Invoker("canPlaceAt")
boolean midnightcontrols$canPlaceAt(BlockState state, WorldView world, BlockPos pos);
}

View File

@@ -0,0 +1,34 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.util.AbstractSignEditScreenAccessor;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.block.entity.SignText;
import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(AbstractSignEditScreen.class)
public class AbstractSignEditScreenMixin implements AbstractSignEditScreenAccessor {
@Shadow @Final private String[] messages;
@Shadow private SignText text;
@Shadow @Final protected SignBlockEntity blockEntity;
@Shadow @Final private boolean front;
@Override
public String[] midnightcontrols$getMessages() {
return messages;
}
@Override
public void midnightcontrols$setMessage(int line, String text) {
this.messages[line] = text;
this.text = this.text.withMessage(line, Text.literal(text));
}
@Override
public void midnightcontrols$writeToBlockEntity() {
this.blockEntity.setText(this.text, this.front);
}
}

View File

@@ -0,0 +1,12 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.gui.screen.ingame.BookEditScreen;
import net.minecraft.client.gui.widget.EditBoxWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(BookEditScreen.class)
public interface BookEditScreenAccessor {
@Accessor("editBox")
EditBoxWidget midnightcontrols$getEditBox();
}

View File

@@ -0,0 +1,12 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.gui.screen.ingame.BookSigningScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(BookSigningScreen.class)
public interface BookSigningScreenAccessor {
@Accessor("bookTitleTextField")
TextFieldWidget midnightcontrols$getBookTitleTextField();
}

View File

@@ -26,10 +26,10 @@ public abstract class ChatScreenMixin extends Screen {
}
@Inject(method = "render", at = @At("HEAD"))
private void midnightcontrols$moveInputFieldBackground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, -this.height + 16, 0f);
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, -this.height + 16);
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;render(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.BEFORE))
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;fill(IIIII)V", shift = At.Shift.AFTER))
private void midnightcontrols$dontMoveOtherStuff(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, this.height - 16, 0f);
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, this.height - 16);
}
}

View File

@@ -56,12 +56,9 @@ public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
@Shadow
protected abstract boolean isCamera();
@Shadow protected int ticksLeftToDoubleTapSprint;
@Inject(method = "move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V"))
public void onMove(MovementType type, Vec3d movement, CallbackInfo ci) {
if (!MidnightControlsConfig.doubleTapToSprint) ticksLeftToDoubleTapSprint = 0;
if (!MidnightControls.isExtrasLoaded) return;
if (type == MovementType.SELF) {
if (this.getAbilities().flying && (!MidnightControlsConfig.flyDrifting || !MidnightControlsConfig.verticalFlyDrifting)) {
@@ -77,7 +74,7 @@ public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
}
}
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(ZF)V", shift = At.Shift.AFTER))
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick()V", shift = At.Shift.AFTER))
public void onInputUpdate(CallbackInfo ci) {
MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this);
}

View File

@@ -58,4 +58,10 @@ public interface CreativeInventoryScreenAccessor {
*/
@Invoker("hasScrollbar")
boolean midnightcontrols$hasScrollbar();
/**
* Triggers searching the creative inventory from the current value of the internal {@link net.minecraft.client.gui.widget.TextFieldWidget}
*/
@Invoker("search")
void midnightcontrols$search();
}

View File

@@ -0,0 +1,17 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.gui.cursor.CursorRenderer;
import net.minecraft.client.gui.cursor.Cursor;
import net.minecraft.client.util.Window;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Cursor.class)
public abstract class CursorMixin {
@Inject(method = "applyTo", at = @At("TAIL"))
public void midnightcontrols$applyCursorStyle(Window window, CallbackInfo ci) {
CursorRenderer.currentCursorStyle = ((Cursor) (Object) this);
}
}

View File

@@ -10,12 +10,15 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.gui.render.state.GuiRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(DrawContext.class)
public interface DrawContextAccessor {
@Accessor("vertexConsumers")
VertexConsumerProvider.Immediate getVertexConsumers();
@Accessor("state")
GuiRenderState getState();
@Accessor("scissorStack")
DrawContext.ScissorStack getScissorStack();
}

View File

@@ -9,6 +9,7 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
@@ -33,17 +34,24 @@ import static eu.midnightdust.midnightcontrols.MidnightControls.id;
@Mixin(GameOptionsScreen.class)
public abstract class GameOptionsScreenMixin extends Screen {
@Shadow @Nullable protected OptionListWidget body;
@Unique TextIconButtonWidget midnightcontrols$button = TextIconButtonWidget.builder(Text.translatable("midnightcontrols.menu.title.controller"), (button -> this.client.setScreen(new MidnightControlsSettingsScreen(this, false))), true)
@Unique TextIconButtonWidget midnightcontrols$button = TextIconButtonWidget.builder(Text.translatable("midnightcontrols.menu.title.controller"),
(button -> this.client.setScreen(MidnightControlsConfig.getScreen(this, "midnightcontrols"))), true)
.dimension(20,20).texture(id("icon/controller"), 20, 20).build();
protected GameOptionsScreenMixin(Text title) {
super(title);
}
@Inject(method = "init", at = @At("TAIL"))
@Inject(method = "initBody", at = @At("TAIL"))
public void midnightcontrols$addMCButton(CallbackInfo ci) {
if (this.getClass().toString().equals(ControlsOptionsScreen.class.toString())) {
this.midnightcontrols$setButtonPos();
this.addSelectableChild(midnightcontrols$button);
}
}
@Inject(method = "init", at = @At("TAIL"))
public void midnightcontrols$drawMCButton(CallbackInfo ci) {
if (this.getClass().toString().equals(ControlsOptionsScreen.class.toString())) {
this.addDrawableChild(midnightcontrols$button);
}
}

View File

@@ -14,7 +14,8 @@ import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
import eu.midnightdust.midnightcontrols.client.gui.cursor.VirtualCursorRenderer;
import eu.midnightdust.midnightcontrols.client.gui.cursor.WaylandCursorRenderer;
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
@@ -30,23 +31,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public abstract class GameRendererMixin {
@Shadow @Final MinecraftClient client;
@Shadow @Final private MinecraftClient client;
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Mouse;getX()D", shift = At.Shift.BEFORE))
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Mouse;getScaledX(Lnet/minecraft/client/util/Window;)D", shift = At.Shift.BEFORE))
private void midnightcontrols$onRender(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) {
if (this.client.currentScreen != null && MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER)
MidnightControlsClient.input.onPreRenderScreen(this.client.currentScreen);
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;draw()V", shift = At.Shift.BEFORE))
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.AFTER))
private void midnightcontrols$renderVirtualCursor(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci, @Local DrawContext drawContext) {
MidnightControlsRenderer.renderVirtualCursor(drawContext, client);
if (MidnightControlsClient.isWayland) MidnightControlsRenderer.renderWaylandCursor(drawContext, client);
drawContext.draw();
VirtualCursorRenderer.getInstance().renderCursor(drawContext, client);
if (MidnightControlsClient.isWayland) WaylandCursorRenderer.getInstance().renderCursor(drawContext, client);
}
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/GameRenderer;renderHand:Z"), method = "renderWorld")
private void midnigtcontrols$captureMatrices(RenderTickCounter tickCounter, CallbackInfo ci, @Local(ordinal = 1) Matrix4f matrices) {
TouchUtils.lastProjMat.set(RenderSystem.getProjectionMatrix());
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;renderHand(FZLorg/joml/Matrix4f;)V"), method = "renderWorld")
private void midnigtcontrols$captureMatrices(RenderTickCounter tickCounter, CallbackInfo ci, @Local(ordinal = 0) Matrix4f projectionMatrix, @Local(ordinal = 1) Matrix4f worldSpaceMatrix) {
TouchUtils.lastProjMat.set(projectionMatrix);
TouchUtils.lastModMat.set(RenderSystem.getModelViewMatrix());
TouchUtils.lastWorldSpaceMatrix.set(matrices);
TouchUtils.lastWorldSpaceMatrix.set(worldSpaceMatrix);
}
}

View File

@@ -46,7 +46,7 @@ public abstract class HandledScreenMixin implements HandledScreenAccessor {
public abstract Slot midnightcontrols$getSlotAt(double posX, double posY);
@Invoker("isClickOutsideBounds")
public abstract boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button);
public abstract boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int left, int top);
@Invoker("onMouseClick")

View File

@@ -0,0 +1,30 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsHud;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderTickCounter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(InGameHud.class)
public class InGameHudMixin {
@Inject(method = "<init>", at = @At("TAIL"))
private static void midnightcontrols$initHud(MinecraftClient client, CallbackInfo ci) {
MidnightControlsHud.getInstance().init();
}
@Inject(method = "renderMainHud", at = @At("HEAD"))
public void midnightcontrols$renderHud(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) {
MidnightControlsHud.getInstance().render(context, tickCounter);
}
@Inject(method = "tick()V", at = @At("HEAD"))
public void midnightcontrols$tickHud(CallbackInfo ci) {
MidnightControlsHud.getInstance().tick();
}
}

View File

@@ -0,0 +1,12 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.input.Input;
import net.minecraft.util.math.Vec2f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Input.class)
public interface InputAccessor {
@Accessor
void setMovementVector(Vec2f input);
}

View File

@@ -0,0 +1,12 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.Keyboard;
import net.minecraft.client.input.KeyInput;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(Keyboard.class)
public interface KeyboardAccessor {
@Invoker("onKey")
void midnightcontrols$onKey(long window, int action, KeyInput input);
}

View File

@@ -28,6 +28,7 @@ import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.profiler.Profiler;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Final;
@@ -38,6 +39,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.reacharound;
@Mixin(MinecraftClient.class)
@@ -54,6 +56,10 @@ public abstract class MinecraftClientMixin {
@Shadow public abstract void setScreen(Screen screen);
@Shadow protected int attackCooldown;
@Shadow protected abstract void handleInputEvents();
@Unique private BlockPos midnightcontrols$lastTargetPos;
@Unique private Vec3d midnightcontrols$lastPos;
@Unique private Direction midnightcontrols$lastTargetSide;
@@ -71,7 +77,7 @@ public abstract class MinecraftClientMixin {
if (!MidnightControlsFeature.FAST_BLOCK_PLACING.isAvailable())
return;
if (this.midnightcontrols$lastPos == null)
this.midnightcontrols$lastPos = this.player.getPos();
this.midnightcontrols$lastPos = this.player.getEntityPos();
int cooldown = this.itemUseCooldown;
BlockHitResult hitResult;
@@ -81,7 +87,7 @@ public abstract class MinecraftClientMixin {
var side = hitResult.getSide();
boolean sidewaysBlockPlacing = this.midnightcontrols$lastTargetPos == null || !targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide));
boolean backwardsBlockPlacing = this.player.input.movementForward < 0.0f && (this.midnightcontrols$lastTargetPos == null || targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide)));
boolean backwardsBlockPlacing = this.player.input.getMovementInput().y < 0.0f && (this.midnightcontrols$lastTargetPos == null || targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide)));
if (cooldown > 1
&& !targetPos.equals(this.midnightcontrols$lastTargetPos)
@@ -100,7 +106,7 @@ public abstract class MinecraftClientMixin {
// this.itemUseCooldown = 0;
// }
// }
this.midnightcontrols$lastPos = this.player.getPos();
this.midnightcontrols$lastPos = this.player.getEntityPos();
}
@Inject(at = @At("TAIL"), method = "setScreen")
@@ -129,7 +135,7 @@ public abstract class MinecraftClientMixin {
if (result.isAccepted()) {
//if (result.shouldSwingHand()) {
this.player.swingHand(hand);
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || this.interactionManager.hasCreativeInventory())) {
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || this.player.isInCreativeMode())) {
this.gameRenderer.firstPersonRenderer.resetEquipProgress(hand);
}
//}
@@ -144,10 +150,21 @@ public abstract class MinecraftClientMixin {
}
}
}
// This is always supposed to be located at before the line 'this.profiler.swap("Keybindings");'
// @Redirect(method = "tick", at = @At(value = "FIELD",target = "Lnet/minecraft/client/MinecraftClient;currentScreen:Lnet/minecraft/client/gui/screen/Screen;", ordinal = 6))
// private Screen midnightcontrols$ignoreTouchOverlay(MinecraftClient instance) {
// if (instance.currentScreen instanceof TouchscreenOverlay) return null;
// return instance.currentScreen;
// }
// TODO: Replace this with MixinExtras' Expressions once that's officially released
@Inject(method = "tick", at = @At(value = "INVOKE",target = "Lnet/minecraft/client/gui/hud/DebugHud;shouldShowDebugHud()Z"))
private void midnightcontrols$handleKeybindsWithTouchOverlay(CallbackInfo ci, @Local Profiler profiler) {
if (client.currentScreen instanceof TouchscreenOverlay) {
profiler.swap("Keybindings");
this.handleInputEvents();
if (this.attackCooldown > 0) {
--this.attackCooldown;
}
}
}
// Needed, as it will cause item actions not to work in touchscreen mode otherwise with the above method
@Inject(method = "handleInputEvents", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isUsingItem()Z"), cancellable = true)
private void midnightcontrols$dontHandleItemAndBlockInteractions(CallbackInfo ci) {
if (client.currentScreen instanceof TouchscreenOverlay) ci.cancel();
}
}

View File

@@ -1,6 +1,7 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.Mouse;
import net.minecraft.client.input.MouseInput;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@@ -9,5 +10,5 @@ public interface MouseAccessor {
@Invoker("onCursorPos")
void midnightcontrols$onCursorPos(long window, double x, double y);
@Invoker("onMouseButton")
void midnightcontrols$onMouseButton(long window, int button, int action, int mods);
void midnightcontrols$onMouseButton(long window, MouseInput input, int action);
}

View File

@@ -17,6 +17,8 @@ import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.Mouse;
import net.minecraft.client.gui.Click;
import net.minecraft.client.input.MouseInput;
import net.minecraft.client.util.GlfwUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ThrowablePotionItem;
@@ -64,12 +66,12 @@ public abstract class MouseMixin implements MouseAccessor {
@Shadow private boolean leftButtonClicked;
@Inject(method = "onMouseButton", at = @At(value = "HEAD"), cancellable = true)
private void midnightcontrols$onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
private void midnightcontrols$onMouseButton(long window, MouseInput input, int action, CallbackInfo ci) {
if (window != this.client.getWindow().getHandle()) return;
if (action == 1 && button == GLFW.GLFW_MOUSE_BUTTON_4 && client.currentScreen != null) {
if (action == 1 && input.button() == GLFW.GLFW_MOUSE_BUTTON_4 && client.currentScreen != null) {
MidnightControlsClient.input.tryGoBack(client.currentScreen);
}
else if ((client.currentScreen == null && doMixedInput() || client.currentScreen instanceof TouchscreenOverlay) && client.player != null && button == GLFW_MOUSE_BUTTON_1) {
else if ((client.currentScreen == null && doMixedInput() || client.currentScreen instanceof TouchscreenOverlay) && client.player != null && input.button() == GLFW_MOUSE_BUTTON_1) {
double mouseX = x / client.getWindow().getScaleFactor();
double mouseY = y / client.getWindow().getScaleFactor();
int centerX = client.getWindow().getScaledWidth() / 2;
@@ -77,7 +79,8 @@ public abstract class MouseMixin implements MouseAccessor {
for (int slot = 0; slot < 9; ++slot) {
int slotX = centerX - 90 + slot * 20 + 2;
if (mouseX >= (double) slotX && mouseX <= (double) (slotX + 20)) {
client.player.getInventory().selectedSlot = slot;
client.player.getInventory().setSelectedSlot(slot);
TouchInput.clickStartTime = -1;
ci.cancel();
return;
}
@@ -86,11 +89,11 @@ public abstract class MouseMixin implements MouseAccessor {
if (action == 1) {
TouchInput.clickStartTime = System.currentTimeMillis();
boolean bl = false;
if (client.currentScreen instanceof TouchscreenOverlay overlay) bl = overlay.mouseClicked(mouseX, mouseY, button);
if (client.currentScreen instanceof TouchscreenOverlay overlay) bl = overlay.mouseClicked(new Click(mouseX, mouseY, input), false);
if (!bl) TouchInput.firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
if (client.currentScreen == null) ci.cancel();
}
else if (TouchInput.mouseReleased(mouseX, mouseY, button)) ci.cancel();
else if (TouchInput.mouseReleased(mouseX, mouseY, input.button())) ci.cancel();
}
}
@@ -139,7 +142,7 @@ public abstract class MouseMixin implements MouseAccessor {
stack.getUseAction() == UseAction.SPEAR || stack.getItem() instanceof ThrowablePotionItem));
}
@Inject(method = "lockCursor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/InputUtil;setCursorParameters(JIDD)V",shift = At.Shift.BEFORE), cancellable = true)
@Inject(method = "lockCursor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/InputUtil;setCursorParameters(Lnet/minecraft/client/util/Window;IDD)V",shift = At.Shift.BEFORE), cancellable = true)
private void midnightcontrols$lockCursor(CallbackInfo ci) {
if ((doMixedInput() || MidnightControlsConfig.eyeTrackerAsMouse)) {
//In eye tracking mode, we cannot have the cursor locked to the center.

View File

@@ -1,6 +1,6 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import org.thinkingstudio.obsidianui.Position;
import dev.lambdaurora.spruceui.Position;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;

View File

@@ -18,6 +18,7 @@ import eu.midnightdust.midnightcontrols.client.util.RainbowColor;
import net.minecraft.block.ShapeContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.render.state.WorldRenderState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.BlockItem;
@@ -33,7 +34,6 @@ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.awt.*;
@@ -54,31 +54,21 @@ public abstract class WorldRendererMixin {
@Shadow
private ClientWorld world;
@Shadow
@Final
private BufferBuilderStorage bufferBuilders;
@Redirect(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/BlockHitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"))
private HitResult.Type dontRenderOutline(BlockHitResult instance) {
if (MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN && MidnightControlsConfig.touchMode == TouchMode.FINGER_POS) {
return HitResult.Type.MISS;
}
return instance.getType();
}
@Inject(
method = "renderTargetBlockOutline",
at = @At("TAIL")
at = @At("HEAD"),
cancellable = true
)
private void onOutlineRender(Camera camera, VertexConsumerProvider.Immediate vertexConsumers, MatrixStack matrices, boolean translucent, CallbackInfo ci) {
private void onOutlineRender(VertexConsumerProvider.Immediate immediate, MatrixStack matrices, boolean renderBlockOutline, WorldRenderState renderStates, CallbackInfo ci) {
if (((MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.touchInControllerMode) || MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN)
&& MidnightControlsConfig.touchMode == TouchMode.FINGER_POS) {
this.midnightcontrols$renderFingerOutline(matrices, camera);
this.midnightcontrols$renderFingerOutline(immediate, matrices, client.gameRenderer.getCamera());
ci.cancel();
}
this.midnightcontrols$renderReacharoundOutline(matrices, camera);
this.midnightcontrols$renderReacharoundOutline(immediate, matrices, client.gameRenderer.getCamera());
}
@Unique
private void midnightcontrols$renderFingerOutline(MatrixStack matrices, Camera camera) {
private void midnightcontrols$renderFingerOutline(VertexConsumerProvider.Immediate immediate, MatrixStack matrices, Camera camera) {
if (TouchInput.firstHitResult == null || TouchInput.firstHitResult.getType() != HitResult.Type.BLOCK)
return;
BlockHitResult result = (BlockHitResult) TouchInput.firstHitResult;
@@ -89,14 +79,14 @@ public abstract class WorldRendererMixin {
if (MidnightControlsConfig.touchOutlineColorHex.isEmpty()) rgb = RainbowColor.radialRainbow(1,1);
var pos = camera.getPos();
matrices.push();
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
var vertexConsumer = immediate.getBuffer(RenderLayer.getLines());
VertexRendering.drawOutline(matrices, vertexConsumer, outlineShape, blockPos.getX() - pos.getX(), blockPos.getY() - pos.getY(), blockPos.getZ() - pos.getZ(),
ColorHelper.withAlpha(MidnightControlsConfig.touchOutlineColorAlpha, rgb.getRGB()));
matrices.pop();
}
}
@Unique
private void midnightcontrols$renderReacharoundOutline(MatrixStack matrices, Camera camera) {
private void midnightcontrols$renderReacharoundOutline(VertexConsumerProvider.Immediate immediate, MatrixStack matrices, Camera camera) {
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !MidnightControlsConfig.shouldRenderReacharoundOutline)
return;
var result = reacharound.getLastReacharoundResult();
@@ -121,7 +111,7 @@ public abstract class WorldRendererMixin {
Color rgb = MidnightColorUtil.hex2Rgb(MidnightControlsConfig.reacharoundOutlineColorHex);
if (MidnightControlsConfig.reacharoundOutlineColorHex.isEmpty()) rgb = RainbowColor.radialRainbow(1,1);
matrices.push();
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
var vertexConsumer = immediate.getBuffer(RenderLayer.getLines());
VertexRendering.drawOutline(matrices, vertexConsumer, outlineShape, blockPos.getX() - pos.getX(), blockPos.getY() - pos.getY(), blockPos.getZ() - pos.getZ(),
ColorHelper.withAlpha(MidnightControlsConfig.touchOutlineColorAlpha, rgb.getRGB()));
matrices.pop();

View File

@@ -60,7 +60,7 @@ public class EyeTrackerHandler {
// The player entity's needs their facing rotated.
double invertY = 1.0;
double moveMagnitude = Math.sqrt(normalizedX*normalizedX + normalizedY*normalizedY);
if (client.options.getInvertYMouse().getValue()) {
if (client.options.getInvertMouseY().getValue()) {
invertY = -1.0;
}
boolean notInDeadzone = (moveMagnitude > MidnightControlsConfig.eyeTrackerDeadzone) && !usingLongRangedTool;

View File

@@ -4,6 +4,9 @@ import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Click;
import net.minecraft.client.input.MouseInput;
import net.minecraft.client.particle.BlockDustParticle;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
@@ -46,7 +49,7 @@ public class TouchInput {
if (result instanceof BlockHitResult blockHit && firstHitResult instanceof BlockHitResult firstBlock && blockHit.getBlockPos().equals(firstBlock.getBlockPos())) {
if (MidnightControlsConfig.debug) System.out.println(blockHit.getBlockPos().toString());
if (client.interactionManager.updateBlockBreakingProgress(blockHit.getBlockPos(), blockHit.getSide())) {
client.particleManager.addBlockBreakingParticles(blockHit.getBlockPos(), blockHit.getSide());
//client.particleManager.addBlockBreakingParticles(blockHit.getBlockPos(), blockHit.getSide()); // TODO Re-implement block breaking particles!!!
client.player.swingHand(Hand.MAIN_HAND);
} else client.interactionManager.cancelBlockBreaking();
firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
@@ -62,7 +65,7 @@ public class TouchInput {
isDragging = false;
firstHitResult = null;
if (client.interactionManager != null) client.interactionManager.cancelBlockBreaking();
if ((client.currentScreen == null || !client.currentScreen.mouseReleased(mouseX, mouseY, button)) && System.currentTimeMillis() - clickStartTime < MidnightControlsConfig.touchBreakDelay) {
if ((client.currentScreen == null || !client.currentScreen.mouseReleased(new Click(mouseX, mouseY, new MouseInput(button, 0)))) && System.currentTimeMillis() - clickStartTime < MidnightControlsConfig.touchBreakDelay) {
assert client.player != null;
assert client.world != null;
assert client.interactionManager != null;
@@ -87,7 +90,7 @@ public class TouchInput {
if (interaction.isAccepted()) {
//if (interaction.shouldSwingHand()) {
client.player.swingHand(client.player.preferredHand);
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || client.interactionManager.hasCreativeInventory())) {
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || client.player.isInCreativeMode())) {
client.gameRenderer.firstPersonRenderer.resetEquipProgress(client.player.preferredHand);
}
//}

View File

@@ -34,13 +34,13 @@ public class TouchUtils {
Vec3d far = screenSpaceToWorldSpace(mouseX, mouseY, 1);
float playerRange = getPlayerRange(client);
EntityHitResult entityCast = ProjectileUtil.raycast(client.player, near, far, Box.from(client.player.getPos()).expand(playerRange), entity -> (!entity.isSpectator() && entity.isAttackable()), playerRange * playerRange);
EntityHitResult entityCast = ProjectileUtil.raycast(client.player, near, far, Box.from(client.player.getEntityPos()).expand(playerRange), entity -> (!entity.isSpectator() && entity.isAttackable()), playerRange * playerRange);
if (entityCast != null && entityCast.getType() == HitResult.Type.ENTITY) return entityCast;
BlockHitResult result = client.world.raycast(new RaycastContext(near, far, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.ANY, client.player));
BlockHitResult result = client.world.raycast(new RaycastContext(near, far, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, client.player));
if (client.player.getPos().distanceTo(result.getPos()) > playerRange) return null;
if (client.player.getEntityPos().distanceTo(result.getPos()) > playerRange) return null;
return result;
}

View File

@@ -1,11 +1,11 @@
package eu.midnightdust.midnightcontrols.client.touch.gui;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.item.consume.UseAction;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.item.ArmorItem;
import net.minecraft.text.Text;
public class ItemUseButtonWidget extends SpruceButtonWidget {
@@ -30,7 +30,7 @@ public class ItemUseButtonWidget extends SpruceButtonWidget {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.eat"));
} else if (action == UseAction.DRINK) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.drink"));
} else if (client.player.getMainHandStack().getItem() instanceof ArmorItem) {
} else if (client.player.getMainHandStack().getComponents().contains(DataComponentTypes.EQUIPPABLE)) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.equip"));
} else if (!action.equals(UseAction.NONE)) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.use"));

View File

@@ -1,7 +1,8 @@
package eu.midnightdust.midnightcontrols.client.touch.gui;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.widget.SpruceTexturedButtonWidget;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.widget.SpruceTexturedButtonWidget;
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
@@ -34,6 +35,7 @@ public class SilentTexturedButtonWidget extends SpruceTexturedButtonWidget {
public void onClick(double mouseX, double mouseY) {
this.setActive(true);
super.onClick(mouseX, mouseY);
TouchInput.clickStartTime = -1;
this.setActive(false);
}
}

View File

@@ -9,18 +9,20 @@
package eu.midnightdust.midnightcontrols.client.touch.gui;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import eu.midnightdust.midnightcontrols.client.util.storage.AxisStorage;
import net.minecraft.client.gui.Click;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.GameMenuScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.item.ArmorItem;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.item.consume.UseAction;
import net.minecraft.util.Arm;
import net.minecraft.util.Atlases;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
@@ -34,10 +36,7 @@ import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.texture.MissingSprite;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.InputUtil;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
@@ -92,7 +91,7 @@ public class TouchscreenOverlay extends Screen {
assert this.client != null;
this.client.setScreen(new GameMenuScreen(true));
if (this.client.isIntegratedServerRunning() && !Objects.requireNonNull(this.client.getServer()).isRemote()) {
this.client.getSoundManager().pauseAll();
this.client.getSoundManager().pauseAllExcept();
}
}
@@ -144,7 +143,7 @@ public class TouchscreenOverlay extends Screen {
*/
private void handleJump(SpruceButtonWidget btn) {
assert this.client != null;
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(btn.isActive());
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(btn.isInteractable());
}
/**
* Handles the jump button.
@@ -172,7 +171,7 @@ public class TouchscreenOverlay extends Screen {
this.addDrawableChild(emoteButton);
}
TextIconButtonWidget chatButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.client.setScreen(new ChatScreen("")), true).width(20).texture(id("touch/chat"), 20, 20).build();
TextIconButtonWidget chatButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.client.setScreen(new ChatScreen("", true)), true).width(20).texture(id("touch/chat"), 20, 20).build();
chatButton.setPosition(scaledWidth / 2 - 20 + emoteOffset, 0);
this.addDrawableChild(chatButton);
TextIconButtonWidget pauseButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.pauseGame(), true).width(20).texture(id("touch/pause"), 20, 20).build();
@@ -209,7 +208,7 @@ public class TouchscreenOverlay extends Screen {
// Swap items hand.
this.addDrawableChild(this.swapHandsButton = new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY), 20, 20, Text.empty(),
button -> {
if (button.isActive()) {
if (button.isInteractable()) {
if (!this.client.player.isSpectator()) {
Objects.requireNonNull(this.client.getNetworkHandler()).sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN));
}
@@ -217,7 +216,7 @@ public class TouchscreenOverlay extends Screen {
},0, 160, 20, WIDGETS_LOCATION));
// Drop
this.addDrawableChild(this.dropButton = new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY + 5 + 20), 20, 20, Text.empty(), btn -> {
if (btn.isActive() && !client.player.isSpectator() && client.player.dropSelectedItem(false)) {
if (btn.isInteractable() && !client.player.isSpectator() && client.player.dropSelectedItem(false)) {
client.player.swingHand(Hand.MAIN_HAND);
}
}, 20, 160, 20, WIDGETS_LOCATION));
@@ -234,12 +233,12 @@ public class TouchscreenOverlay extends Screen {
this::handleJump, 40, 40, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.flyDownButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY + 20 + 5), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(btn.isActive()), 60, 40, 20, WIDGETS_LOCATION
btn -> ((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(btn.isInteractable()), 60, 40, 20, WIDGETS_LOCATION
));
this.updateJumpButtons();
// Movements keys
this.addDrawableChild((this.startSneakButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY), 20, 20, Text.empty(), btn -> {
if (btn.isActive()) {
if (btn.isInteractable()) {
((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(true);
this.startSneakButton.setVisible(false);
this.endSneakButton.setVisible(true);
@@ -247,40 +246,40 @@ public class TouchscreenOverlay extends Screen {
}, 0, 120, 20, WIDGETS_LOCATION))
);
this.addDrawableChild((this.endSneakButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY), 20, 20, Text.empty(), btn -> {
if (btn.isActive()) {
if (btn.isInteractable()) {
((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(false);
this.endSneakButton.setVisible(false);
this.startSneakButton.setVisible(true);
}
}, 20, 120, 20, WIDGETS_LOCATION)));
this.addDrawableChild(this.forwardLeftButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX - 20 - 5, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isActive());
this.updateForwardButtonsState(btn.isActive());
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isInteractable());
((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isInteractable());
this.updateForwardButtonsState(btn.isInteractable());
}, 80, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.forwardButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
this.updateForwardButtonsState(btn.isActive());
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isInteractable());
this.updateForwardButtonsState(btn.isInteractable());
this.forwardLeftButton.setVisible(true);
this.forwardRightButton.setVisible(true);
}, 0, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.forwardRightButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX + 20 + 5, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isActive());
this.updateForwardButtonsState(btn.isActive());
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isInteractable());
((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isInteractable());
this.updateForwardButtonsState(btn.isInteractable());
}, 100, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.rightButton =new SilentTexturedButtonWidget(Position.of(sneakButtonX + 20 + 5, sneakButtonY), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isActive()), 20, 80, 20, WIDGETS_LOCATION
btn -> ((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isInteractable()), 20, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.backButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY + 20 + 5), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.backKey).midnightcontrols$handlePressState(btn.isActive()), 40, 80, 20, WIDGETS_LOCATION
btn -> ((KeyBindingAccessor) this.client.options.backKey).midnightcontrols$handlePressState(btn.isInteractable()), 40, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.leftButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX - 20 - 5, sneakButtonY), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isActive()), 60, 80, 20, WIDGETS_LOCATION
btn -> ((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isInteractable()), 60, 80, 20, WIDGETS_LOCATION
));
initCustomButtons(true);
initCustomButtons(false);
@@ -291,14 +290,20 @@ public class TouchscreenOverlay extends Screen {
assert client != null;
Identifier emptySprite = id("touch/empty");
List<String> list = left ? MidnightControlsConfig.leftTouchBinds : MidnightControlsConfig.rightTouchBinds;
Sprite missingSprite = client.getGuiAtlasManager().getSprite(MissingSprite.getMissingSpriteId());
Sprite missingSprite = client.getAtlasManager().getAtlasTexture(Atlases.GUI).getMissingSprite();
for (int i = 0; i < list.size(); i++) {
String bindName = list.get(i);
ButtonBinding binding = InputManager.getBinding(bindName);
if (binding == null) continue;
boolean hasTexture = client.getGuiAtlasManager().getSprite(id("binding/"+bindName)) != missingSprite;
boolean hasTexture = client.getAtlasManager().getAtlasTexture(Atlases.GUI).getSprite(id("binding/"+bindName)) != missingSprite;
if (MidnightControlsConfig.debug) System.out.println(left +" "+id("binding/"+bindName)+" "+ hasTexture);
var button = TextIconButtonWidget.builder(Text.translatable(binding.getTranslationKey()), b -> binding.handle(client, 1, ButtonState.PRESS), hasTexture)
var button = TextIconButtonWidget.builder(Text.translatable(binding.getTranslationKey()), b -> {
binding.handle(client, 1.0f, ButtonState.PRESS);
if (binding.asKeyBinding().isPresent()) {
binding.asKeyBinding().get().setPressed(true);
((KeyBindingAccessor)binding.asKeyBinding().get()).midnightcontrols$press();
}
}, hasTexture)
.texture(hasTexture ? id("binding/"+bindName) : emptySprite, 20, 20).dimension(20, 20).build();
button.setPosition(left ? (3+(i*23)) : this.width-(23+(i*23)), 3);
button.setAlpha(MidnightControlsConfig.touchTransparency / 100f);
@@ -340,13 +345,13 @@ public class TouchscreenOverlay extends Screen {
this.forwardLeftButton.setVisible(false);
this.forwardRightButton.setVisible(false);
}
this.useButton.setVisible(client.player.getMainHandStack() != null && (client.player.getMainHandStack().getUseAction() != UseAction.NONE || client.player.getMainHandStack().getItem() instanceof ArmorItem) && !TouchUtils.hasInWorldUseAction(client.player.getMainHandStack()));
this.useButton.setVisible(client.player.getMainHandStack() != null && (client.player.getMainHandStack().getUseAction() != UseAction.NONE || client.player.getMainHandStack().getComponents().contains(DataComponentTypes.EQUIPPABLE)) && !TouchUtils.hasInWorldUseAction(client.player.getMainHandStack()));
this.updateJumpButtons();
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
if (button == GLFW.GLFW_MOUSE_BUTTON_1 && this.client != null) {
public boolean mouseDragged(Click click, double deltaX, double deltaY) {
if (click.button() == GLFW.GLFW_MOUSE_BUTTON_1 && this.client != null) {
if (TouchInput.isDragging) {
if (!MidnightControlsConfig.invertTouch) {
deltaX = -deltaX;
@@ -357,12 +362,6 @@ public class TouchscreenOverlay extends Screen {
}
else TouchInput.isDragging = true;
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
KeyBinding.onKeyPressed(InputUtil.fromKeyCode(keyCode, scanCode));
super.keyPressed(keyCode,scanCode,modifiers);
return true;
return super.mouseDragged(click, deltaX, deltaY);
}
}

View File

@@ -0,0 +1,14 @@
package eu.midnightdust.midnightcontrols.client.util;
import org.spongepowered.asm.mixin.Unique;
public interface AbstractSignEditScreenAccessor {
@Unique
String[] midnightcontrols$getMessages();
@Unique
void midnightcontrols$setMessage(int line, String text);
@Unique
void midnightcontrols$writeToBlockEntity();
}

View File

@@ -40,7 +40,7 @@ public interface HandledScreenAccessor {
*/
Slot midnightcontrols$getSlotAt(double posX, double posY);
boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button);
boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int left, int top);
/**
* Handles a mouse click on the specified slot.

View File

@@ -91,7 +91,7 @@ public class InventoryUtil {
if (closestSlot.isPresent() && client.player != null) {
var slot = closestSlot.get().getLeft();
if (slot.hasStack() || !client.player.getInventory().getMainHandStack().isEmpty()) {
if (slot.hasStack() || !client.player.getInventory().getSelectedStack().isEmpty()) {
int slotCenterXScaled = guiLeft + slot.x + 8;
int slotCenterYScaled = guiTop + slot.y + 8;
int slotCenterX = (int) (slotCenterXScaled / ((double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()));

View File

@@ -1,6 +1,7 @@
package eu.midnightdust.midnightcontrols.client.util.platform;
import dev.architectury.injectables.annotations.ExpectPlatform;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
@@ -11,6 +12,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
import static eu.midnightdust.midnightcontrols.client.MidnightInput.ENTER_KEY_INPUT;
public class ItemGroupUtil {
@ExpectPlatform
public static List<ItemGroup> getVisibleGroups(CreativeInventoryScreen screen) {
@@ -24,10 +27,10 @@ public class ItemGroupUtil {
.filter(element -> element.getMessage() != null && element.getMessage().getContent() != null)
.anyMatch(element -> {
if (next && element.getMessage().getString().equals(">")) {
element.onPress();
element.onPress(ENTER_KEY_INPUT);
return true;
} else if (element.getMessage().getString().equals("<")) {
element.onPress();
element.onPress(ENTER_KEY_INPUT);
return true;
}
return false;

View File

@@ -0,0 +1,102 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class KeyboardLayout {
public static KeyboardLayout QWERTY = new KeyboardLayout("en_US:qwerty", createQwertyLetterLayout(), createSymbolLayout());
private final String id;
private final List<List<String>> letters;
private final List<List<String>> symbols;
private KeyboardLayout(String id, List<List<String>> letters, List<List<String>> symbols) {
this.id = id;
this.letters = letters;
this.symbols = symbols;
}
public static KeyboardLayout fromJson(JsonObject json) {
try {
return new KeyboardLayout(json.get("id").getAsString(), getFromJson(json, true), getFromJson(json, false));
} catch (Exception e) {
throw new RuntimeException("Error loading keyboard definition: %s".formatted(e));
}
}
private static List<List<String>> getFromJson(JsonObject json, boolean letters) {
String type = letters ? "letters" : "symbols";
List<List<String>> arr = new ArrayList<>();
if (json.has(type)) {
JsonObject lettersJson = json.get(type).getAsJsonObject();
for (int i = 0; ; i++) {
if (!lettersJson.has("row"+i)) break;
var rowJson = lettersJson.get("row%s".formatted(i)).getAsJsonArray();
List<String> row = new ArrayList<>();
for (int j = 0; j < rowJson.size(); j++) {
row.add(rowJson.get(j).getAsString());
}
arr.add(row);
}
return arr;
}
else {
return letters ? createQwertyLetterLayout() : createSymbolLayout();
}
}
public String getId() {
return id;
}
public String getTranslationKey() {
String[] identifier = id.split(":");
if (identifier.length != 2) return "Invalid Keyboard ID: %s".formatted(id);
return "midnightcontrols.virtual_keyboard.layout.%s.%s".formatted(identifier[0], identifier[1]);
}
public List<List<String>> getLetters() {
return letters;
}
public List<List<String>> getSymbols() {
return symbols;
}
private static List<List<String>> createQwertyLetterLayout() {
List<List<String>> letters = new ArrayList<>();
letters.add(Arrays.asList(
"q", "w", "e", "r", "t",
"y", "u", "i", "o", "p"
));
letters.add(Arrays.asList(
"a", "s", "d", "f", "g",
"h", "j", "k", "l"
));
letters.add(Arrays.asList(
"z", "x", "c", "v",
"b", "n", "m"
));
return letters;
}
private static List<List<String>> createSymbolLayout() {
List<List<String>> symbols = new ArrayList<>();
symbols.add(Arrays.asList(
"1", "2", "3", "4", "5",
"6", "7", "8", "9", "0"
));
symbols.add(Arrays.asList(
"@", "#", "$", "%", "&",
"*", "-", "+", "(", ")"
));
symbols.add(Arrays.asList(
"!", "\"", "'", ":", ";",
",", ".", "?", "/"
));
return symbols;
}
}

View File

@@ -0,0 +1,36 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.resource.Resource;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class KeyboardLayoutManager {
private static final Map<String, KeyboardLayout> KEYBOARD_LAYOUTS = new HashMap<>();
public static void loadLayout(Identifier id, Resource resource) {
try {
JsonObject json = JsonParser.parseReader(resource.getReader()).getAsJsonObject();
KeyboardLayout layout = KeyboardLayout.fromJson(json);
KEYBOARD_LAYOUTS.put(layout.getId(), layout);
if (MidnightControlsConfig.debug) System.out.printf("Loaded keyboard layout: %s\n", layout.getId());
} catch (IOException e) { throw new RuntimeException(e); }
}
public static KeyboardLayout getById(String id) {
return KEYBOARD_LAYOUTS.get(id) == null ? KeyboardLayout.QWERTY : KEYBOARD_LAYOUTS.get(id);
}
public static KeyboardLayout getNext(KeyboardLayout current) {
KeyboardLayout[] layouts = KEYBOARD_LAYOUTS.values().toArray(KeyboardLayout[]::new);
int currentIndex = -1;
for (int i = 0; i < layouts.length; i++) {
if (layouts[i] == current) currentIndex = i;
}
currentIndex = currentIndex+1 >= layouts.length ? 0 : currentIndex + 1;
return layouts[currentIndex];
}
}

View File

@@ -0,0 +1,39 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.AbstractScreenClickHandler;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.BookEditScreenClickHandler;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.DefaultScreenClickHandler;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.SignEditScreenClickHandler;
import net.minecraft.client.gui.Click;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.BookEditScreen;
import net.minecraft.client.gui.screen.ingame.BookSigningScreen;
import net.minecraft.client.gui.screen.ingame.SignEditScreen;
import java.util.HashMap;
import java.util.Map;
public class MouseClickInterceptor {
private final Map<Class<?>, AbstractScreenClickHandler<?>> clickHandlers;
public MouseClickInterceptor() {
this.clickHandlers = new HashMap<>();
this.clickHandlers.put(BookSigningScreen.class, new BookEditScreenClickHandler.Signing());
this.clickHandlers.put(BookEditScreen.class, new BookEditScreenClickHandler());
this.clickHandlers.put(SignEditScreen.class, new SignEditScreenClickHandler());
this.clickHandlers.put(Screen.class, new DefaultScreenClickHandler());
}
@SuppressWarnings("unchecked")
public <T extends Screen> void intercept(T screen, Click click) {
AbstractScreenClickHandler<T> handler = (AbstractScreenClickHandler<T>) clickHandlers.get(screen.getClass());
if (handler == null) {
handler = (AbstractScreenClickHandler<T>) clickHandlers.get(Screen.class);
}
handler.handle(screen, click.x(), click.y());
}
}

View File

@@ -0,0 +1,7 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler;
import net.minecraft.client.gui.screen.Screen;
public abstract class AbstractScreenClickHandler<T extends Screen> {
public abstract void handle(T screen, double mouseX, double mouseY);
}

View File

@@ -0,0 +1,47 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler;
import eu.midnightdust.midnightcontrols.client.mixin.BookEditScreenAccessor;
import eu.midnightdust.midnightcontrols.client.mixin.BookSigningScreenAccessor;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.gui.VirtualKeyboardScreen;
import net.minecraft.client.gui.screen.ingame.BookEditScreen;
import net.minecraft.client.gui.screen.ingame.BookSigningScreen;
import net.minecraft.client.gui.widget.EditBoxWidget;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
public class BookEditScreenClickHandler extends AbstractScreenClickHandler<BookEditScreen> {
@Override
public void handle(BookEditScreen screen, double mouseX, double mouseY) {
// don't open the keyboard if a UI element was clicked
if(screen.hoveredElement(mouseX, mouseY).isPresent() && !(screen.hoveredElement(mouseX, mouseY).get() instanceof EditBoxWidget)) {
return;
}
var accessor = (BookEditScreenAccessor) screen;
VirtualKeyboardScreen virtualKeyboardScreen = new VirtualKeyboardScreen(accessor.midnightcontrols$getEditBox().getText(), (text) -> {
client.setScreen(screen);
accessor.midnightcontrols$getEditBox().setText(text);
}, true);
client.setScreen(virtualKeyboardScreen);
}
public static class Signing extends AbstractScreenClickHandler<BookSigningScreen> {
@Override
public void handle(BookSigningScreen screen, double mouseX, double mouseY) {
// don't open the keyboard if a UI element was clicked
if(screen.hoveredElement(mouseX, mouseY).isPresent()) {
return;
}
var accessor = (BookSigningScreenAccessor) screen;
VirtualKeyboardScreen virtualKeyboardScreen = new VirtualKeyboardScreen(accessor.midnightcontrols$getBookTitleTextField().getText(), (text) -> {
client.setScreen(screen);
accessor.midnightcontrols$getBookTitleTextField().setText(text);
}, false);
client.setScreen(virtualKeyboardScreen);
}
}
}

View File

@@ -0,0 +1,145 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler;
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.gui.VirtualKeyboardScreen;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.ParentElement;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import org.lwjgl.glfw.GLFW;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
import static eu.midnightdust.midnightcontrols.client.MidnightInput.ENTER_KEY_INPUT;
public class DefaultScreenClickHandler extends AbstractScreenClickHandler<Screen> {
private Screen parentScreen;
private List<Integer> textFieldElementPath;
@Override
public void handle(Screen screen, double mouseX, double mouseY) {
var textField = findClickedTextField(screen.children(), mouseX, mouseY);
if (textField == null) {
return;
}
this.parentScreen = screen;
this.textFieldElementPath = calculatePathToElement(screen, textField.asElement());
var virtualKeyboardScreen = new VirtualKeyboardScreen(textField.getText(), this::handleKeyboardClose, false);
client.setScreen(virtualKeyboardScreen);
}
private void handleKeyboardClose(String newText) {
if (this.parentScreen == null || this.textFieldElementPath == null) {
return;
}
client.setScreen(this.parentScreen);
TextFieldWrapper textField = findTextFieldByPath(this.parentScreen, this.textFieldElementPath);
if (textField == null) {
return;
}
textField.setText(newText);
switch (this.parentScreen) {
case CreativeInventoryScreen creativeInventoryScreen -> {
var accessor = (CreativeInventoryScreenAccessor) creativeInventoryScreen;
accessor.midnightcontrols$search();
}
case ChatScreen chatScreen -> {
// send the chat message
chatScreen.keyPressed(ENTER_KEY_INPUT);
}
default -> {
}
}
}
private TextFieldWrapper findClickedTextField(List<? extends Element> elements, double mouseX, double mouseY) {
for (Element element : elements) {
if (TextFieldWrapper.isValidTextField(element)) {
TextFieldWrapper textField = new TextFieldWrapper(element);
if (textField.isMouseOver(mouseX, mouseY) && textField.isFocused()) {
return textField;
}
}
if (element instanceof ParentElement parentElement) {
TextFieldWrapper found = findClickedTextField(parentElement.children(), mouseX, mouseY);
if (found != null) {
return found;
}
}
}
return null;
}
/**
* Calculates the path between a parent and a target in the UI hierarchy
*/
protected List<Integer> calculatePathToElement(Element parent, Element target) {
if (!(parent instanceof ParentElement parentElement)) {
return null;
}
List<? extends Element> children = parentElement.children();
for (int i = 0; i < children.size(); i++) {
Element child = children.get(i);
if (child == target) {
return Collections.singletonList(i);
}
if (child instanceof ParentElement) {
List<Integer> subPath = calculatePathToElement(child, target);
if (subPath != null) {
List<Integer> fullPath = new ArrayList<>(subPath.size() + 1);
fullPath.add(i);
fullPath.addAll(subPath);
return fullPath;
}
}
}
return null;
}
protected TextFieldWrapper findTextFieldByPath(Element parent, List<Integer> path) {
if (path == null || path.isEmpty()) {
return null;
}
if (!(parent instanceof ParentElement parentElement)) {
return null;
}
List<? extends Element> children = parentElement.children();
int index = path.get(0);
if (index < 0 || index >= children.size()) {
return null;
}
Element child = children.get(index);
if (path.size() == 1) {
return TextFieldWrapper.isValidTextField(child) ? new TextFieldWrapper(child) : null;
}
if (child instanceof ParentElement) {
return findTextFieldByPath(child, path.subList(1, path.size()));
}
return null;
}
}

View File

@@ -0,0 +1,36 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler;
import eu.midnightdust.midnightcontrols.client.util.AbstractSignEditScreenAccessor;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.gui.VirtualKeyboardScreen;
import net.minecraft.client.gui.screen.ingame.SignEditScreen;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
public class SignEditScreenClickHandler extends AbstractScreenClickHandler<SignEditScreen> {
@Override
public void handle(SignEditScreen screen, double mouseX, double mouseY) {
// don't open the keyboard if a UI element was clicked
if(screen.hoveredElement(mouseX, mouseY).isPresent()) {
return;
}
var accessor = (AbstractSignEditScreenAccessor) screen;
StringBuilder linesToString = new StringBuilder();
String[] messages = accessor.midnightcontrols$getMessages();
for (int i = 0; i < Math.min(4, messages.length); i++) {
String line = messages[i];
linesToString.append(line);
if (!line.isEmpty() && i < 3) linesToString.append("\n");
}
VirtualKeyboardScreen virtualKeyboardScreen = new VirtualKeyboardScreen(linesToString.toString(), (text) -> {
client.setScreen(screen);
String[] lines = text.split("\n");
for (int i = 0; i < 4; i++) accessor.midnightcontrols$setMessage(i, lines.length > i ? lines[i] : "");
accessor.midnightcontrols$writeToBlockEntity();
}, true);
client.setScreen(virtualKeyboardScreen);
}
}

View File

@@ -0,0 +1,77 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler;
import dev.lambdaurora.spruceui.widget.text.SpruceTextFieldWidget;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.widget.TextFieldWidget;
public record TextFieldWrapper(Object textField) {
public TextFieldWrapper {
if (!isValidTextField(textField)) {
throw new IllegalArgumentException("Type " + textField.getClass() + " is not marked as a valid text field");
}
}
Element asElement() {
return (Element) textField;
}
String getText() {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
return spruceTextField.getText();
}
case TextFieldWidget vanillaTextField -> {
return vanillaTextField.getText();
}
default -> {
return null;
}
}
}
void setText(String text) {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
spruceTextField.setText(text);
}
case TextFieldWidget vanillaTextField -> {
vanillaTextField.setText(text);
}
default -> {
}
}
}
boolean isMouseOver(double mouseX, double mouseY) {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
return spruceTextField.isMouseOver(mouseX, mouseY);
}
case TextFieldWidget vanillaTextField -> {
return vanillaTextField.isMouseOver(mouseX, mouseY);
}
default -> {
return false;
}
}
}
boolean isFocused() {
switch (textField) {
case SpruceTextFieldWidget spruceTextField -> {
return spruceTextField.isFocused();
}
case TextFieldWidget vanillaTextField -> {
return vanillaTextField.isFocused();
}
default -> {
return false;
}
}
}
static boolean isValidTextField(Object textField) {
return textField instanceof TextFieldWidget || textField instanceof SpruceTextFieldWidget;
}
}

View File

@@ -0,0 +1,295 @@
package eu.midnightdust.midnightcontrols.client.virtualkeyboard.gui;
import dev.lambdaurora.spruceui.Position;
import dev.lambdaurora.spruceui.SpruceTexts;
import dev.lambdaurora.spruceui.screen.SpruceScreen;
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
import dev.lambdaurora.spruceui.widget.text.SpruceTextAreaWidget;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.KeyboardLayout;
import eu.midnightdust.midnightcontrols.client.virtualkeyboard.KeyboardLayoutManager;
import net.minecraft.text.Text;
import java.util.List;
public class VirtualKeyboardScreen extends SpruceScreen {
@FunctionalInterface
public interface CloseCallback {
void onClose(String text);
}
private static final int STANDARD_KEY_WIDTH = 20;
private static final int SPECIAL_KEY_WIDTH = (int) (STANDARD_KEY_WIDTH * 1.5);
private static final int KEY_HEIGHT = 20;
private static final int HORIZONTAL_SPACING = 2;
private static final int VERTICAL_SPACING = 4;
private static final int CONTAINER_PADDING = 10;
// Key symbols
private static final String BACKSPACE_SYMBOL = "\b";
private static final String NEWLINE_SYMBOL = "\n";
private static final String SPACE_SYMBOL = " ";
private final StringBuilder buffer;
private final CloseCallback closeCallback;
private final KeyboardLayout layout;
private final boolean newLineSupport;
private boolean capsMode;
private boolean symbolMode;
private SpruceTextAreaWidget bufferDisplayArea;
private SpruceContainerWidget keyboardContainer;
public VirtualKeyboardScreen(String initialText, CloseCallback closeCallback, boolean newLineSupport) {
super(Text.translatable("midnightcontrols.virtual_keyboard.screen"));
this.buffer = new StringBuilder(initialText);
this.closeCallback = closeCallback;
this.layout = KeyboardLayoutManager.getById(MidnightControlsConfig.keyboardLayout);
this.capsMode = false;
this.symbolMode = false;
this.newLineSupport = newLineSupport;
}
@Override
protected void init() {
super.init();
this.bufferDisplayArea = createBufferDisplayArea();
this.addDrawableChild(this.bufferDisplayArea);
rebuildKeyboard();
int doneButtonY = this.keyboardContainer.getY() + this.keyboardContainer.getHeight() + VERTICAL_SPACING * 2;
this.addDrawableChild(
new SpruceButtonWidget(
Position.of(this, this.width / 2 - 50, doneButtonY),
100,
20,
SpruceTexts.GUI_DONE,
btn -> this.close()
)
);
}
@Override
public boolean shouldPause() {
return false;
}
@Override
public void close() {
super.close();
if (this.closeCallback != null) {
this.closeCallback.onClose(this.buffer.toString());
}
}
private void rebuildKeyboard() {
if (this.keyboardContainer != null) {
this.remove(this.keyboardContainer);
}
var layoutKeys = getActiveKeyLayout();
var keyboardContainer = createKeyboardContainer(layoutKeys);
addLayoutRows(keyboardContainer, layoutKeys);
addFunctionKeys(keyboardContainer);
addBottomRow(keyboardContainer);
this.keyboardContainer = keyboardContainer;
this.addDrawableChild(this.keyboardContainer);
}
private SpruceContainerWidget createKeyboardContainer(List<List<String>> layoutKeys) {
int containerWidth = this.width;
int totalKeyboardHeight = calculateKeyboardHeight(layoutKeys);
int keyboardY = this.bufferDisplayArea.getY() + this.bufferDisplayArea.getHeight() + VERTICAL_SPACING * 2;
return new SpruceContainerWidget(
Position.of(0, keyboardY),
containerWidth,
totalKeyboardHeight
);
}
private SpruceTextAreaWidget createBufferDisplayArea() {
int lineCount = this.newLineSupport ? 4 : 1;
int bufferX = this.width / 2 - 100;
int bufferY = this.height / 4 - VERTICAL_SPACING * 5 - 5;
int bufferWidth = 200;
int desiredHeight = (this.textRenderer.fontHeight + 2) * lineCount + 6;
var bufferDisplay = new SpruceTextAreaWidget(
Position.of(bufferX, bufferY),
bufferWidth,
desiredHeight,
Text.literal("Buffer Display")
);
bufferDisplay.setText(this.buffer.toString());
bufferDisplay.setEditable(false);
bufferDisplay.setUneditableColor(0xFFFFFFFF);
bufferDisplay.setDisplayedLines(lineCount);
bufferDisplay.setCursorToEnd();
return bufferDisplay;
}
private int calculateKeyboardHeight(List<List<String>> keyRows) {
return keyRows.size() * (KEY_HEIGHT + VERTICAL_SPACING) +
(KEY_HEIGHT + VERTICAL_SPACING) + // space for bottom row
CONTAINER_PADDING * 2; // top and bottom padding
}
private void addLayoutRows(SpruceContainerWidget container, List<List<String>> keyLayoutRows) {
int currentY = CONTAINER_PADDING;
for (List<String> row : keyLayoutRows) {
int rowWidth = calculateRowWidth(row);
// center row
int currentX = (container.getWidth() - rowWidth) / 2;
for (String key : row) {
String displayText = (this.capsMode && !this.symbolMode) ? key.toUpperCase() : key;
container.addChild(
new SpruceButtonWidget(
Position.of(currentX, currentY),
STANDARD_KEY_WIDTH,
KEY_HEIGHT,
Text.literal(displayText),
btn -> handleKeyPress(displayText)
)
);
currentX += STANDARD_KEY_WIDTH + HORIZONTAL_SPACING;
}
currentY += KEY_HEIGHT + VERTICAL_SPACING;
}
}
private int calculateRowWidth(List<String> row) {
int rowWidth = 0;
for (int i = 0; i < row.size(); i++) {
rowWidth += STANDARD_KEY_WIDTH;
// padding
if (i < row.size() - 1) {
rowWidth += HORIZONTAL_SPACING;
}
}
return rowWidth;
}
private void addFunctionKeys(SpruceContainerWidget container) {
List<String> firstRow = getActiveKeyLayout().getFirst();
int firstRowWidth = calculateRowWidth(firstRow);
// position backspace at the right of the first row
int backspaceWidth = (int) (STANDARD_KEY_WIDTH * 1.5);
int backspaceX = (container.getWidth() + firstRowWidth) / 2 + HORIZONTAL_SPACING;
container.addChild(
new SpruceButtonWidget(
Position.of(backspaceX, CONTAINER_PADDING),
backspaceWidth,
KEY_HEIGHT,
Text.literal(""),
btn -> handleKeyPress(BACKSPACE_SYMBOL)
)
);
if (this.newLineSupport) {
// position newline at the right of the second row
List<String> secondRow = getActiveKeyLayout().get(1);
int newlineWidth = (int) (STANDARD_KEY_WIDTH * 1.5);
int secondRowWidth = calculateRowWidth(secondRow);
int newlineX = (container.getWidth() + secondRowWidth) / 2 + HORIZONTAL_SPACING;
int newlineY = CONTAINER_PADDING + (KEY_HEIGHT + VERTICAL_SPACING);
container.addChild(
new SpruceButtonWidget(
Position.of(newlineX, newlineY),
newlineWidth,
KEY_HEIGHT,
Text.literal(""),
btn -> handleKeyPress(NEWLINE_SYMBOL)
)
);
}
}
private void addBottomRow(SpruceContainerWidget container) {
// calculate positions for bottom row
int rowY = CONTAINER_PADDING + getActiveKeyLayout().size() * (KEY_HEIGHT + VERTICAL_SPACING);
// space bar - wide key in the middle
double spaceWidthFactor = 5.0;
int spaceKeyWidth = (int) (STANDARD_KEY_WIDTH * spaceWidthFactor);
int spaceX = (container.getWidth() - spaceKeyWidth) / 2;
container.addChild(
new SpruceButtonWidget(
Position.of(spaceX, rowY),
spaceKeyWidth,
KEY_HEIGHT,
Text.translatable("midnightcontrols.virtual_keyboard.keyboard.space"),
btn -> handleKeyPress(SPACE_SYMBOL)
)
);
// caps key - left of space
if (!this.symbolMode) {
int capsX = spaceX - SPECIAL_KEY_WIDTH - HORIZONTAL_SPACING * 2;
var capsModeButton = new SpruceButtonWidget(
Position.of(capsX, rowY),
SPECIAL_KEY_WIDTH,
KEY_HEIGHT,
Text.literal(this.capsMode ? "caps" : "CAPS"),
btn -> toggleCapsMode());
container.addChild(capsModeButton);
}
// symbols key - right of space
int symbolsX = spaceX + spaceKeyWidth + HORIZONTAL_SPACING * 2;
var symbolModeButton = new SpruceButtonWidget(
Position.of(symbolsX, rowY),
SPECIAL_KEY_WIDTH,
KEY_HEIGHT,
Text.literal(this.symbolMode ? "ABC" : "123?!"),
btn -> toggleSymbolMode()
);
container.addChild(symbolModeButton);
}
private void handleKeyPress(String key) {
if (key.equals(BACKSPACE_SYMBOL)) {
if (!this.buffer.isEmpty()) {
this.buffer.deleteCharAt(buffer.length() - 1);
}
} else {
this.buffer.append(key);
}
if (this.bufferDisplayArea != null) {
this.bufferDisplayArea.setText(this.buffer.toString());
this.bufferDisplayArea.setCursorToEnd();
}
}
private List<List<String>> getActiveKeyLayout() {
return this.symbolMode ? this.layout.getSymbols() : this.layout.getLetters();
}
private void toggleCapsMode() {
this.capsMode = !this.capsMode;
rebuildKeyboard();
}
private void toggleSymbolMode() {
this.symbolMode = !this.symbolMode;
rebuildKeyboard();
}
}

View File

@@ -0,0 +1,14 @@
{
"id": "de_DE:qwertz",
"letters": {
"row0": ["q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ü"],
"row1": ["a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä"],
"row2": ["y", "x", "c", "v", "b", "n", "m", "ß"]
},
"symbols": {
"row0": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
"row1": ["@", "#", "$", "%", "&", "*", "-", "+", "(", ")"],
"row2": ["!", "\"", "'", ":", ";", ",", ".", "?", "/"]
}
}

View File

@@ -0,0 +1,14 @@
{
"id": "en_US:qwerty",
"letters": {
"row0": ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
"row1": ["a", "s", "d", "f", "g", "h", "j", "k", "l"],
"row2": ["z", "x", "c", "v", "b", "n", "m"]
},
"symbols": {
"row0": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
"row1": ["@", "#", "$", "%", "&", "*", "-", "+", "(", ")"],
"row2": ["!", "\"", "'", ":", ";", ",", ".", "?", "/"]
}
}

View File

@@ -0,0 +1,239 @@
{
"midnightcontrols.midnightconfig.title": "Pokročilá konfigurace MidnightControls",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "Výchozí světlá",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "Výchozí tmavá",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "Druhá světlá",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_DARK": "Druhá tmavá",
"midnightcontrols.midnightconfig.enum.ControllerType.DEFAULT": "Výchozí",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSHOCK": "DualShock",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSENSE": "DualSense",
"midnightcontrols.midnightconfig.enum.ControllerType.SWITCH": "Switch/Wii ovladač",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX": "Xbox One/Series ovladač",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX_360": "Xbox 360 Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_CONTROLLER": "Steam Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_DECK": "Steam Deck",
"midnightcontrols.midnightconfig.enum.ControllerType.OUYA": "OUYA ovladač",
"midnightcontrols.midnightconfig.enum.ControllerType.NUMBERED": "Ovladač s číslicemi",
"midnightcontrols.midnightconfig.enum.ControlsMode.DEFAULT": "Klávesnice/Myš",
"midnightcontrols.midnightconfig.enum.ControlsMode.CONTROLLER": "Ovladač",
"midnightcontrols.midnightconfig.enum.ControlsMode.TOUCHSCREEN": "Dotyk (Beta)",
"midnightcontrols.midnightconfig.enum.HudSide.LEFT": "Levá",
"midnightcontrols.midnightconfig.enum.HudSide.RIGHT": "Pravá",
"midnightcontrols.midnightconfig.enum.TouchMode.CROSSHAIR": "Crosshair režim",
"midnightcontrols.midnightconfig.enum.TouchMode.FINGER_POS": "Pozice prstu",
"midnightcontrols.midnightconfig.enum.CameraMode.FLAT": "Plochá",
"midnightcontrols.midnightconfig.enum.CameraMode.ADAPTIVE": "Adaptivní",
"key.categories.midnightcontrols": "MidnightControls",
"key.midnightcontrols.look_down": "Podívat se dolů",
"key.midnightcontrols.look_left": "Podívat se doleva",
"key.midnightcontrols.look_right": "Podívat se doprava",
"key.midnightcontrols.look_up": "Podívat se nahoru",
"key.midnightcontrols.ring": "Otevřít ovládací kroužek",
"midnightcontrols.action.attack": "Útok",
"midnightcontrols.action.back": "Zpět",
"midnightcontrols.action.chat": "Otevřít chat",
"midnightcontrols.action.controls_ring": "Otevřít ovládací kroužek",
"midnightcontrols.action.debug_screen": "Otevřít debugovací HUD (F3)",
"midnightcontrols.action.drop_item": "Odhodit věc",
"midnightcontrols.action.drink": "Napít se",
"midnightcontrols.action.eat": "Najíst se",
"midnightcontrols.action.equip": "Vybavit se",
"midnightcontrols.action.exit": "Ukončit",
"midnightcontrols.action.forward": "Dopředu",
"midnightcontrols.action.hit": "Útok",
"midnightcontrols.action.hotbar_left": "Hotbar doleva",
"midnightcontrols.action.hotbar_right": "Hotbar doprava",
"midnightcontrols.action.inventory": "Inventář",
"midnightcontrols.action.jump": "Skok",
"midnightcontrols.action.left": "Doleva",
"midnightcontrols.action.pause_game": "Pozastavit hru",
"midnightcontrols.action.pick_block": "Vybrat blok",
"midnightcontrols.action.pickup": "Vzít",
"midnightcontrols.action.pickup_all": "Vzít vše",
"midnightcontrols.action.place": "Položit",
"midnightcontrols.action.player_list": "Seznam hráčů",
"midnightcontrols.action.quick_move": "Rychlý přesun",
"midnightcontrols.action.right": "Doprava",
"midnightcontrols.action.screenshot": "Vyfotit snímek obrazovky",
"midnightcontrols.action.slot_up": "Přesunout slot nahoru",
"midnightcontrols.action.slot_down": "Přesunout slot dolů",
"midnightcontrols.action.slot_left": "Přesunout slot doleva",
"midnightcontrols.action.slot_right": "Přesunout slot doprava",
"midnightcontrols.action.sneak": "Plížit se",
"midnightcontrols.action.sprint": "Sprintovat",
"midnightcontrols.action.swap_hands": "Vyměnit si ruce",
"midnightcontrols.action.toggle_perspective": "Změnit perspektivu",
"midnightcontrols.action.toggle_smooth_camera": "Nastavit kinematografickou kameru",
"midnightcontrols.action.page_back": "Předchozí stránka",
"midnightcontrols.action.page_next": "Následující stránka",
"midnightcontrols.action.tab_back": "Předchozí karta",
"midnightcontrols.action.tab_next": "Následující karta",
"midnightcontrols.action.take": "Vzít věc",
"midnightcontrols.action.take_all": "Vzít stack",
"midnightcontrols.action.use": "Použít",
"midnightcontrols.action.zoom": "Přiblížit",
"midnightcontrols.action.zoom_in": "Zvýšit přiblížení",
"midnightcontrols.action.zoom_out": "Snížit příblížení",
"midnightcontrols.action.zoom_reset": "Resetovat Přiblížení",
"midnightcontrols.action.emi_page_left": "Předchozí stránka",
"midnightcontrols.action.emi_page_right": "Následující stránka",
"midnightcontrols.category.emi": "EMI",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Levý bumper",
"midnightcontrols.button.right_bumper": "Pravý bumper",
"midnightcontrols.button.back": "Zpět",
"midnightcontrols.button.start": "Start",
"midnightcontrols.button.guide": "Guide",
"midnightcontrols.button.left_thumb": "Levá páčka",
"midnightcontrols.button.right_thumb": "Pravá páčka",
"midnightcontrols.button.dpad_up": "DPAD nahoru",
"midnightcontrols.button.dpad_right": "DPAD doprava",
"midnightcontrols.button.dpad_down": "DPAD dolů",
"midnightcontrols.button.dpad_left": "DPAD doleva",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Levý X+",
"midnightcontrols.axis.left_y+": "Levý Y+",
"midnightcontrols.axis.right_x+": "Pravý X+",
"midnightcontrols.axis.right_y+": "Pravý Y+",
"midnightcontrols.axis.left_trigger": "Levý trigger",
"midnightcontrols.axis.right_trigger": "Pravý trigger",
"midnightcontrols.axis.left_x-": "Levý X-",
"midnightcontrols.axis.left_y-": "Levý Y-",
"midnightcontrols.axis.right_x-": "Pravý X-",
"midnightcontrols.axis.right_y-": "Pravý Y-",
"midnightcontrols.button.unknown": "Neznámý (%d)",
"midnightcontrols.controller.tutorial.title": "Hrajte hru s ovladačem!",
"midnightcontrols.controller.tutorial.description": "jděte do %s -> %s -> %s",
"midnightcontrols.controller.connected": "Ovladač %d připojen.",
"midnightcontrols.controller.disconnected": "Ovladač %d odpojen.",
"midnightcontrols.controller.mappings.1": "Chcete-li nakonfigurovat mapování ovladače, použijte %s",
"midnightcontrols.controller.mappings.3": "a vložte mapování do editoru souboru mapování.",
"midnightcontrols.controller.mappings.error": "Chyba při načítání mapování.",
"midnightcontrols.controller.mappings.error.write": "Chyba při zápisu mapování do souboru.",
"midnightcontrols.controller.mappings.updated": "Aktualizované mapování!",
"midnightcontrols.controller_type.default": "Výchozí",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Switch/Wii ovladač",
"midnightcontrols.controller_type.xbox": "Xbox One/Series ovladač",
"midnightcontrols.controller_type.xbox_360": "Xbox 360 ovladač",
"midnightcontrols.controller_type.steam_controller": "Steam ovladač",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "OUYA ovladač",
"midnightcontrols.controller_type.numbered": "Ovladač s číslicemi",
"midnightcontrols.controls_mode.default": "Klávesnice/Myš",
"midnightcontrols.controls_mode.controller": "Ovladač",
"midnightcontrols.controls_mode.touchscreen": "Dotyk (Beta)",
"midnightcontrols.hud_side.LEFT": "Levý",
"midnightcontrols.hud_side.RIGHT": "Pravý",
"midnightcontrols.menu.analog_movement": "Analogový pohyb",
"midnightcontrols.menu.analog_movement.tooltip": "Pokud je to možné, umožňuje analogový pohyb.",
"midnightcontrols.menu.auto_switch_mode": "Režim automatického přepínání",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Zda se má režim ovládání automaticky přepnout na ovladač, pokud je připojen.",
"midnightcontrols.menu.camera_mode": "Režim kamery",
"midnightcontrols.menu.controller": "Ovladač",
"midnightcontrols.menu.controller2": "Druhý ovladač",
"midnightcontrols.menu.controller2.tooltip": "Použije druhý ovladač, který umožňuje (například) podporu Joy-Conů.",
"midnightcontrols.menu.controller_toggle_sneak": "Přepnutí plížení na ovladači",
"midnightcontrols.menu.controller_toggle_sprint": "Přepnutí sprintování na ovladačí",
"midnightcontrols.menu.controller_type": "Typ ovladače",
"midnightcontrols.menu.controller_type.tooltip": "Typ používaného ovladače (potřebné pro zobrazení správných tlačítek).",
"midnightcontrols.menu.controls_mode": "Režim",
"midnightcontrols.menu.controls_mode.tooltip": "Režim ovládání.",
"midnightcontrols.menu.copy_controller_guid": "Zkopírovat GUID",
"midnightcontrols.menu.current_controller_guid": "GUID aktuálního ovladače: %s",
"midnightcontrols.menu.double_tap_to_sprint": "Dvojité klepnutí na sprintování",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "Přepíná, zda klávesa chůze vpřed při dvojitém rychlém poklepání umožní hráči sprintovat.",
"midnightcontrols.menu.fast_block_placing": "Rychlé pokládání bloků",
"midnightcontrols.menu.fast_block_placing.tooltip": "Při letu v kreativním režimu umožňuje rychlé pokládání bloků v závislosti na vaší rychlosti. §cNa některých serverech to může být považováno za podvádění§r.",
"midnightcontrols.menu.fly_drifting": "Fly Drifting",
"midnightcontrols.menu.fly_drifting.tooltip": "Za letu umožňuje Vanilla driftování/setrvačnost.",
"midnightcontrols.menu.fly_drifting_vertical": "Vertikální Fly Drifting",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Za letu umožňuje Vanilla vertikální driftování/setrvačnost.",
"midnightcontrols.menu.hud_enable": "Zapnout HUD",
"midnightcontrols.menu.hud_enable.tooltip": "Přepíná indikátor tlačítek ovladače na obrazovce.",
"midnightcontrols.menu.hud_side": "Strana HUD",
"midnightcontrols.menu.hud_side.tooltip": "Pozice HUD.",
"midnightcontrols.menu.invert_right_x_axis": "Invertovat pravý X",
"midnightcontrols.menu.invert_right_y_axis": "Invertovat pravý Y",
"midnightcontrols.menu.joystick_as_mouse": "Vždy použít levou páčku jako myš",
"midnightcontrols.menu.joystick_as_mouse.tooltip": "V každém menu se páčka chová jako myš.",
"midnightcontrols.menu.eye_tracker_as_mouse": "Použití eye trackeru jako myši"
"midnightcontrols.menu.eye_tracker_as_mouse.tooltip": "Nahraďte myš zařízením pro sledování očí (například Tobii 5).",
"midnightcontrols.menu.eye_tracker_deadzone": "Velikost mrtvé zóny eye trackeru",
"midnightcontrols.menu.eye_tracker_deadzone.tooltip": "Zastaví pohyb kamery při pohledu do blízkosti crosshair",
"midnightcontrols.menu.keyboard_controls": "Ovládání klávesnice...",
"midnightcontrols.menu.left_dead_zone": "Mrtvá zóna levé páčky",
"midnightcontrols.menu.left_dead_zone.tooltip": "Mrtvá zóna levé analogové páčky ovladače.",
"midnightcontrols.menu.mappings.open_input_str": "Otevřít editor souborů mapování",
"midnightcontrols.menu.max_left_x_value": "Maximální hodnota levé osy X",
"midnightcontrols.menu.max_left_x_value.tooltip": "Změní hodnotu, kterou mod považuje za nejvyšší pro levou osu X. Užitečné, pokud osa nevyužívá celý rozsah a zdá se pomalá.",
"midnightcontrols.menu.max_left_y_value": "Maximální hodnota levé osy Y",
"midnightcontrols.menu.max_left_y_value.tooltip": "Změní hodnotu, kterou mod považuje za nejvyšší pro levou osu Y. Užitečné, pokud osa nevyužívá celý rozsah a zdá se pomalá.",
"midnightcontrols.menu.max_right_x_value": "Maximální hodnota pravé osy X",
"midnightcontrols.menu.max_right_x_value.tooltip": "Změní hodnotu, kterou mod považuje za nejvyšší pro pravou osu X. Užitečné, pokud osa nevyužívá celý rozsah a zdá se pomalá.",
"midnightcontrols.menu.max_right_y_value": "Maximální hodnota pravé osy Y",
"midnightcontrols.menu.max_right_y_value.tooltip": "Změní hodnotu, kterou mod považuje za nejvyšší pro pravou osu Y. Užitečné, pokud osa nevyužívá celý rozsah a zdá se pomalá.",
"midnightcontrols.menu.mouse_speed": "Rychlost myši",
"midnightcontrols.menu.mouse_speed.tooltip": "Rychlost emulované myši ovladače.",
"midnightcontrols.menu.move_chat": "Přesunutí vstupního pole chatu nahoru",
"midnightcontrols.menu.move_chat.tooltip": "Přesune pole pro zadávání chatu nahoru, aby se lépe zadávalo na zařízeních s klávesnicí na obrazovce.",
"midnightcontrols.menu.multiple_mapping_tip": "(Tip: Můžete také vložit více mapování najednou.)",
"midnightcontrols.menu.reacharound.horizontal": "Pokládání předního bloku",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Umožňuje umístění předního bloku, §ccož může být na některých serverech považováno za podvod.§r",
"midnightcontrols.menu.reacharound.vertical": "Vertikální dosah",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Zapiná vertikální dosah, §ccož může být na některých serverech považováno za podvod.§r",
"midnightcontrols.menu.reload_controller_mappings": "Opětovné načíst mapování ovladače",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Znovu načte soubor mapování ovladače.",
"midnightcontrols.menu.right_dead_zone": "Mrtvá zóna pravé páčky",
"midnightcontrols.menu.right_dead_zone.tooltip": "Mrtvá zóna pravé analogové páčky ovladače.",
"midnightcontrols.menu.rotation_speed": "Rychlost otáčení osy X",
"midnightcontrols.menu.rotation_speed.tooltip": "Rychlost otáčení osy X v režimu ovladače.",
"midnightcontrols.menu.y_axis_rotation_speed": "Rychlost otáčení osy Y",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Rychlost otáčení osy Y v režimu ovladače.",
"midnightcontrols.menu.separate_controller_profile": "Samostatný profil ovladače",
"midnightcontrols.menu.separator.controller": "Ovladač",
"midnightcontrols.menu.separator.general": "Obecné",
"midnightcontrols.menu.title": "MidnightControls - Nastavení",
"midnightcontrols.menu.title.controller": "Nastavení ovladače",
"midnightcontrols.menu.title.controller_controls": "Vazby ovladače",
"midnightcontrols.menu.title.gameplay": "Herní možnosti",
"midnightcontrols.menu.title.general": "Obecné možnosti",
"midnightcontrols.menu.title.hud": "Možnosti HUD",
"midnightcontrols.menu.title.mappings.string": "Editor souborů mapování",
"midnightcontrols.menu.title.touch": "Možnosti dotyku",
"midnightcontrols.menu.title.visual": "Možnosti vzhledu",
"midnightcontrols.menu.touch_break_delay": "Zpoždění přerušení dotyku",
"midnightcontrols.menu.touch_speed": "Rychlost dotyku",
"midnightcontrols.menu.invert_touch": "Invertování směru dotyku",
"midnightcontrols.menu.touch_mode": "Režim dotykové interakce",
"midnightcontrols.menu.touch_transparency": "Průhlednost dotykového HUD",
"midnightcontrols.menu.touch_with_controller": "Dotyk v režimu ovladače",
"midnightcontrols.menu.unfocused_input": "Nezaostřený vstup",
"midnightcontrols.menu.unfocused_input.tooltip": "Umožňuje vstup do ovladače, když okno není zaostřené.",
"midnightcontrols.menu.virtual_mouse": "Virtuální myš",
"midnightcontrols.menu.virtual_mouse.tooltip": "Povoluje virtuální myš, která je užitečná při rozdělené obrazovce.",
"midnightcontrols.menu.virtual_mouse.skin": "Skin virtuální myši",
"midnightcontrols.menu.hide_cursor": "Skrytí normálního kurzoru myši",
"midnightcontrols.menu.hide_cursor.tooltip": "Skryje normální kurzor myši a ponechá viditelný pouze virtuální kurzor myši.",
"midnightcontrols.narrator.unbound": "Zrušení vazby na tlačítko %s",
"midnightcontrols.not_bound": "Není vázano",
"midnightcontrols.virtual_mouse.skin.default_light": "Výchozí světlá",
"midnightcontrols.virtual_mouse.skin.default_dark": "Výchozí tmavá",
"midnightcontrols.virtual_mouse.skin.second_light": "Druhá světlá",
"midnightcontrols.virtual_mouse.skin.second_dark": "Druhá tmavá",
"midnightcontrols.midnightconfig.category.controller": "Ovladač",
"midnightcontrols.midnightconfig.category.misc": "Různé",
"midnightcontrols.midnightconfig.category.screens": "Obrazovky",
"midnightcontrols.midnightconfig.category.gameplay": "Hra",
"midnightcontrols.midnightconfig.category.touch": "Dotyk",
"midnightcontrols.midnightconfig.category.visual": "Vizuál",
"modmenu.descriptionTranslation.midnightcontrols": "Přidává podporu ovladačů a celkově vylepšuje ovládání.\nPřevzato z LambdaControls, jehož vývoj byl ukončen."
}

View File

@@ -146,6 +146,10 @@
"midnightcontrols.menu.title.visual": "Visuelle Optionen",
"midnightcontrols.menu.unfocused_input": "Unfokussierte Eingabe",
"midnightcontrols.menu.unfocused_input.tooltip": "Erlaube Controllereingabe auch wenn das Fenster nicht fokussiert ist.",
"midnightcontrols.virtual_keyboard.screen": "Virtuelle Tastatur",
"midnightcontrols.virtual_keyboard.keyboard.space": "Leertaste",
"midnightcontrols.virtual_keyboard.layout.en_US.qwerty": "Englisch (Qwerty)",
"midnightcontrols.virtual_keyboard.layout.de_DE.qwertz": "Deutsch (Qwertz)",
"midnightcontrols.menu.virtual_mouse": "Virtuelle Maus",
"midnightcontrols.menu.virtual_mouse.tooltip": "Aktiviere die virtuelle Maus.",
"midnightcontrols.menu.virtual_mouse.skin": "Aussehen der Virtuellen Maus",

View File

@@ -1,5 +1,5 @@
{
"midnightcontrols.midnightconfig.title": "MidnightControls Advanced Config",
"midnightcontrols.midnightconfig.title": "MidnightControls Config",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "Default Light",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "Default Dark",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "Second Light",
@@ -23,7 +23,7 @@
"midnightcontrols.midnightconfig.enum.TouchMode.FINGER_POS": "Finger Position",
"midnightcontrols.midnightconfig.enum.CameraMode.FLAT": "Flat",
"midnightcontrols.midnightconfig.enum.CameraMode.ADAPTIVE": "Adaptive",
"key.categories.midnightcontrols": "MidnightControls",
"key.category.midnightcontrols.keybinds": "MidnightControls",
"key.midnightcontrols.look_down": "Look Down",
"key.midnightcontrols.look_left": "Look Left",
"key.midnightcontrols.look_right": "Look Right",
@@ -217,6 +217,10 @@
"midnightcontrols.menu.touch_with_controller": "Touch in Controller mode",
"midnightcontrols.menu.unfocused_input": "Unfocused Input",
"midnightcontrols.menu.unfocused_input.tooltip": "Allows controller input when the window is not focused.",
"midnightcontrols.menu.virtual_keyboard": "Virtual Keyboard",
"midnightcontrols.menu.virtual_keyboard.tooltip": "Enables a virtual on-screen keyboard",
"midnightcontrols.menu.virtual_keyboard_layout": "Virtual Keyboard Layout",
"midnightcontrols.menu.virtual_keyboard_layout.tooltip": "Defines which layout the on-screen keyboard will follow.",
"midnightcontrols.menu.virtual_mouse": "Virtual Mouse",
"midnightcontrols.menu.virtual_mouse.tooltip": "Enables the virtual mouse, which is useful during splitscreen.",
"midnightcontrols.menu.virtual_mouse.skin": "Virtual Mouse Skin",
@@ -224,11 +228,17 @@
"midnightcontrols.menu.hide_cursor.tooltip": "Hides the normal mouse cursor, leaving only the virtual mouse visible.",
"midnightcontrols.narrator.unbound": "Unbound %s",
"midnightcontrols.not_bound": "Not bound",
"midnightcontrols.virtual_keyboard.screen": "Virtual Keyboard",
"midnightcontrols.virtual_keyboard.keyboard.space": "Space",
"midnightcontrols.virtual_keyboard.layout.en_US.qwerty": "English (Qwerty)",
"midnightcontrols.virtual_keyboard.layout.de_DE.qwertz": "German (Qwertz)",
"midnightcontrols.virtual_mouse.skin.default_light": "Default Light",
"midnightcontrols.virtual_mouse.skin.default_dark": "Default Dark",
"midnightcontrols.virtual_mouse.skin.second_light": "Second Light",
"midnightcontrols.virtual_mouse.skin.second_dark": "Second Dark",
"midnightcontrols.midnightconfig.category.controller": "Controller",
"midnightcontrols.midnightconfig.category.buttons": "Buttons",
"midnightcontrols.midnightconfig.category.mapping": "Mapping",
"midnightcontrols.midnightconfig.category.misc": "Miscellaneous",
"midnightcontrols.midnightconfig.category.screens": "Screens",
"midnightcontrols.midnightconfig.category.gameplay": "Gameplay",

View File

@@ -0,0 +1,238 @@
{
"midnightcontrols.midnightconfig.title": "Configurações avançadas do MidnightControls",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "Padrão claro",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "Padrão escuro",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "Segundo claro",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_DARK": "Segundo escuro",
"midnightcontrols.midnightconfig.enum.ControllerType.DEFAULT": "Padrão",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSHOCK": "DualShock",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSENSE": "DualSense",
"midnightcontrols.midnightconfig.enum.ControllerType.SWITCH": "Controle de Switch/Wii",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX": "Controle de Xbox One/Series",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX_360": "Controle de Xbox 360",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_CONTROLLER": "Controle da Steam",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_DECK": "Steam Deck",
"midnightcontrols.midnightconfig.enum.ControllerType.OUYA": "Controle de OUYA",
"midnightcontrols.midnightconfig.enum.ControllerType.NUMBERED": "Controle númerado",
"midnightcontrols.midnightconfig.enum.ControlsMode.DEFAULT": "Teclado/Mouse",
"midnightcontrols.midnightconfig.enum.ControlsMode.CONTROLLER": "Controle",
"midnightcontrols.midnightconfig.enum.ControlsMode.TOUCHSCREEN": "Tela sensível ao toque (Beta)",
"midnightcontrols.midnightconfig.enum.HudSide.LEFT": "Esquerda",
"midnightcontrols.midnightconfig.enum.HudSide.RIGHT": "Direita",
"midnightcontrols.midnightconfig.enum.TouchMode.CROSSHAIR": "Na mira",
"midnightcontrols.midnightconfig.enum.TouchMode.FINGER_POS": "Posição do dedo",
"midnightcontrols.midnightconfig.enum.CameraMode.FLAT": "Plano",
"midnightcontrols.midnightconfig.enum.CameraMode.ADAPTIVE": "Adaptativo",
"key.categories.midnightcontrols": "MidnightControls",
"key.midnightcontrols.look_down": "Olhar para baixo",
"key.midnightcontrols.look_left": "Olhar para a esquerda",
"key.midnightcontrols.look_right": "Olhar para a direita",
"key.midnightcontrols.look_up": "Olhar para cima",
"key.midnightcontrols.ring": "Abrir anel de teclas não vinculadas",
"midnightcontrols.action.attack": "Atacar",
"midnightcontrols.action.back": "Voltar",
"midnightcontrols.action.chat": "Abrir bate-papo",
"midnightcontrols.action.controls_ring": "Abrir anel de teclas não vinculadas",
"midnightcontrols.action.debug_screen": "Abrir HUD de depuração (F3)",
"midnightcontrols.action.drop_item": "Soltar item",
"midnightcontrols.action.drink": "Beber",
"midnightcontrols.action.eat": "Comer",
"midnightcontrols.action.equip": "Equipar",
"midnightcontrols.action.exit": "Sair da tela",
"midnightcontrols.action.forward": "Para frente",
"midnightcontrols.action.hit": "Atacar",
"midnightcontrols.action.hotbar_left": "Move a barra rápida para esquerda",
"midnightcontrols.action.hotbar_right": "Move a barra rápida para direita",
"midnightcontrols.action.inventory": "Invetário",
"midnightcontrols.action.jump": "Pular",
"midnightcontrols.action.left": "Esquerda",
"midnightcontrols.action.pause_game": "Pausar",
"midnightcontrols.action.pick_block": "Pegar bloco",
"midnightcontrols.action.pickup": "Pegar",
"midnightcontrols.action.pickup_all": "Pegar todos",
"midnightcontrols.action.place": "Colocar",
"midnightcontrols.action.player_list": "Lista de jogadores",
"midnightcontrols.action.quick_move": "Mover rápido",
"midnightcontrols.action.right": "Direita",
"midnightcontrols.action.screenshot": "Tirar Screenshot",
"midnightcontrols.action.slot_up": "Mover Slot para cima",
"midnightcontrols.action.slot_down": "Mover Slot para baixo",
"midnightcontrols.action.slot_left": "Mover Slot para esquerda",
"midnightcontrols.action.slot_right": "Mover Slot para direita",
"midnightcontrols.action.sneak": "Esgueirar",
"midnightcontrols.action.sprint": "Correr",
"midnightcontrols.action.swap_hands": "Trocar de mãos",
"midnightcontrols.action.toggle_perspective": "Alternar perspectiva",
"midnightcontrols.action.toggle_smooth_camera": "Ativar câmera cinemática",
"midnightcontrols.action.page_back": "Página anterior",
"midnightcontrols.action.page_next": "Próxima página",
"midnightcontrols.action.tab_back": "Aba anterior",
"midnightcontrols.action.tab_next": "Próxima aba",
"midnightcontrols.action.take": "Pegar item",
"midnightcontrols.action.take_all": "Pegar pilha",
"midnightcontrols.action.use": "Usar",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Aumentar o Zoom",
"midnightcontrols.action.zoom_out": "Diminuir o Zoom",
"midnightcontrols.action.zoom_reset": "Resetar o Zoom",
"midnightcontrols.action.emi_page_left": "Página anterior",
"midnightcontrols.action.emi_page_right": "Próxima pickup",
"midnightcontrols.category.emi": "EMI",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Botão L1",
"midnightcontrols.button.right_bumper": "Botão R1",
"midnightcontrols.button.back": "Voltar",
"midnightcontrols.button.start": "Começar",
"midnightcontrols.button.guide": "Guia",
"midnightcontrols.button.left_thumb": "Analógico esquerdo",
"midnightcontrols.button.right_thumb": "Analógico direito",
"midnightcontrols.button.dpad_up": "Botão direcional para cima",
"midnightcontrols.button.dpad_right": "Botão direcional para direita",
"midnightcontrols.button.dpad_down": "Botão direcional para baixo",
"midnightcontrols.button.dpad_left": "Botão direcional para esquerda",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Esquerda X+",
"midnightcontrols.axis.left_y+": "Esquerda Y+",
"midnightcontrols.axis.right_x+": "Direita X+",
"midnightcontrols.axis.right_y+": "Direita Y+",
"midnightcontrols.axis.left_trigger": "Gatilho da esquerda",
"midnightcontrols.axis.right_trigger": "Gatilho da direita",
"midnightcontrols.axis.left_x-": "Esquerda X-",
"midnightcontrols.axis.left_y-": "Esquerda Y-",
"midnightcontrols.axis.right_x-": "Direita X-",
"midnightcontrols.axis.right_y-": "Direita Y-",
"midnightcontrols.button.unknown": "Desconhecido (%d)",
"midnightcontrols.controller.tutorial.title": "Jogue o jogo com um controle!",
"midnightcontrols.controller.tutorial.description": "Vá para %s -> %s -> %s",
"midnightcontrols.controller.connected": "Controle %d conectado.",
"midnightcontrols.controller.disconnected": "Controle %d desconectado.",
"midnightcontrols.controller.mappings.1": "Para configurar o mapeamento do controle, por favor use %s",
"midnightcontrols.controller.mappings.3": "e cole o mapeamento no editor de arquivos do mapeamento.",
"midnightcontrols.controller.mappings.error": "Erro ao carregar mapeamentos.",
"midnightcontrols.controller.mappings.error.write": "Erro ao gravar mapeamentos no arquivo.",
"midnightcontrols.controller.mappings.updated": "Atualizando mapeamentos!",
"midnightcontrols.controller_type.default": "Padrão",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Controle de Switch/Wii",
"midnightcontrols.controller_type.xbox": "Controle de Xbox One/Series",
"midnightcontrols.controller_type.xbox_360": "Controle de Xbox 360",
"midnightcontrols.controller_type.steam_controller": "Controle da Steam",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "Controle de OUYA",
"midnightcontrols.controller_type.numbered": "Controle númerado",
"midnightcontrols.controls_mode.default": "Teclado/Mouse",
"midnightcontrols.controls_mode.controller": "Controle",
"midnightcontrols.controls_mode.touchscreen": "Tela sensível ao toque (Beta)",
"midnightcontrols.hud_side.LEFT": "Esquerda",
"midnightcontrols.hud_side.RIGHT": "Direita",
"midnightcontrols.menu.analog_movement": "Movemento analógico",
"midnightcontrols.menu.analog_movement.tooltip": "Quando possível, ative o movemento analógico.",
"midnightcontrols.menu.auto_switch_mode": "Modo de troca automática",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Se o modo de controle deve ser alterado automaticamente para o controle quando um for conectado.",
"midnightcontrols.menu.camera_mode": "Modo de câmera",
"midnightcontrols.menu.controller": "Controle",
"midnightcontrols.menu.controller2": "Segundo controle",
"midnightcontrols.menu.controller2.tooltip": "Segundo controle a ser usado, o que permite (por exemplo) o suporte a Joy-Cons.",
"midnightcontrols.menu.controller_toggle_sneak": "Alternar Esgueirar no Controle",
"midnightcontrols.menu.controller_toggle_sprint": "Alternar Correr no Controle",
"midnightcontrols.menu.controller_type": "Tipo de controle",
"midnightcontrols.menu.controller_type.tooltip": "O tipo de controle que você está usando (é preciso para mostrar os botões corretos)",
"midnightcontrols.menu.controls_mode": "Modo",
"midnightcontrols.menu.controls_mode.tooltip": "O modo de controle.",
"midnightcontrols.menu.copy_controller_guid": "Copiar o GUID",
"midnightcontrols.menu.current_controller_guid": "GUID do controle atual: %s",
"midnightcontrols.menu.double_tap_to_sprint": "Toque duplo para correr",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "Alterna se a tecla Andar para Frente faz o jogador correr quando pressionada duas vezes rapidamente",
"midnightcontrols.menu.fast_block_placing": "Colocação Rápida de Blocos",
"midnightcontrols.menu.fast_block_placing.tooltip": "Enquanto voando no criativo, ativar a Colocação de Rápida de Blocos. §cEm alguns servidores isso pode ser considerado como trapaça.",
"midnightcontrols.menu.fly_drifting": "Derrapagem ao Voar",
"midnightcontrols.menu.fly_drifting.tooltip": "Enquanto voando, ativa a deparragem/inércia Vanilla.",
"midnightcontrols.menu.fly_drifting_vertical": "Derrapagem vertical ao Voar",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Enquanto voando, ativa a deparragem/inércia vertical Vanilla.",
"midnightcontrols.menu.hud_enable": "Ativar HUD",
"midnightcontrols.menu.hud_enable.tooltip": "Alterna o indicador de botão do controle na tela.",
"midnightcontrols.menu.hud_side": "Lado do HUD",
"midnightcontrols.menu.hud_side.tooltip": "A posição do HUD.",
"midnightcontrols.menu.invert_right_x_axis": "Inverter X Direito",
"midnightcontrols.menu.invert_right_y_axis": "Inverter Y Direito",
"midnightcontrols.menu.joystick_as_mouse": "Sempre usar analógico esquerdo como mouse",
"midnightcontrols.menu.joystick_as_mouse.tooltip": "Fazer o analógico agir como um mouse em todos os menus.",
"midnightcontrols.menu.eye_tracker_as_mouse": "Usar o rastreador ocular como um mouse",
"midnightcontrols.menu.eye_tracker_as_mouse.tooltip": "Substituir o mouse com um dispositivo de rastreamento ocular, (por exemplo) o Tobii 5.",
"midnightcontrols.menu.eye_tracker_deadzone": "Tamanho da zona morta do rastreador ocular",
"midnightcontrols.menu.eye_tracker_deadzone.tooltip": "Parar o movimento da camera quando olhar perto da mira",
"midnightcontrols.menu.keyboard_controls": "Controles do teclado...",
"midnightcontrols.menu.left_dead_zone": "Zona morta do analógico esquerdo",
"midnightcontrols.menu.left_dead_zone.tooltip": "A zona morta para o analógico esquerdo do controle.",
"midnightcontrols.menu.mappings.open_input_str": "Abrir editor de arquivo de mapeamentos",
"midnightcontrols.menu.max_left_x_value": "Valor máximo do eixo X esquerdo",
"midnightcontrols.menu.max_left_x_value.tooltip": "Altera o valor máximo considerado pelo mod para o eixo X esquerdo. Útil se o seu eixo não usa toda a faixa e parece lento.",
"midnightcontrols.menu.max_left_y_value": "Valor máximo do eixo Y esquerdo",
"midnightcontrols.menu.max_left_y_value.tooltip": "Altera o valor máximo considerado pelo mod para o eixo Y esquerdo. Útil se o seu eixo não usa toda a faixa e parece lento.",
"midnightcontrols.menu.max_right_x_value": "Valor máximo do eixo X direito",
"midnightcontrols.menu.max_right_x_value.tooltip": "Altera o valor máximo considerado pelo mod para o eixo X direito. Útil se o seu eixo não usa toda a faixa e parece lento.",
"midnightcontrols.menu.max_right_y_value": "Valor máximo do eixo Y direito",
"midnightcontrols.menu.max_right_y_value.tooltip": "Altera o valor máximo considerado pelo mod para o eixo Y direito. Útil se o seu eixo não usa toda a faixa e parece lento.",
"midnightcontrols.menu.mouse_speed": "Velocidade do mouse",
"midnightcontrols.menu.mouse_speed.tooltip": "A velocidade do mouse emulado pelo controle",
"midnightcontrols.menu.move_chat": "Mover a caixa do chat para o topo",
"midnightcontrols.menu.move_chat.tooltip": "Move a caixa do chat para o topo, para uma melhor digitação em dispositivos com teclados na tela.",
"midnightcontrols.menu.multiple_mapping_tip": "(Dica: Você também pode inserir múltiplos mapeamentos ao mesmo tempo)",
"midnightcontrols.menu.reacharound.horizontal": "Colocação frontal de blocos",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Ativa colocação frontal de blocos, §cEm alguns servidores isso pode ser considerado como trapaça.§r.",
"midnightcontrols.menu.reacharound.vertical": "Alcance vertical",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Ativa alcance vertical, §c§cEm alguns servidores isso pode ser considerado como trapaça.§r.",
"midnightcontrols.menu.reload_controller_mappings": "Recarregar o mapeamento do controle",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Recarrega o arquivo de mapeamentos do controle.",
"midnightcontrols.menu.right_dead_zone": "Zona morta do analógico direito",
"midnightcontrols.menu.right_dead_zone.tooltip": "A zona morta para o analógico direito do controle..",
"midnightcontrols.menu.rotation_speed": "Velocidade de rotação do eixo X",
"midnightcontrols.menu.rotation_speed.tooltip": "A velocidade de rotação do eixo X da câmera no modo controle.",
"midnightcontrols.menu.y_axis_rotation_speed": "Velocidade de rotação do eixo Y",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "A velocidade de rotação do eixo Y da câmera no modo controle.",
"midnightcontrols.menu.separate_controller_profile": "Separar perfil do controle",
"midnightcontrols.menu.separator.controller": "Controle",
"midnightcontrols.menu.separator.general": "Geral",
"midnightcontrols.menu.title": "MidnightControls - Configurações",
"midnightcontrols.menu.title.controller": "Configurações do controle",
"midnightcontrols.menu.title.controller_controls": "Mapeamento de controle",
"midnightcontrols.menu.title.gameplay": "Configurações de jogabilidade",
"midnightcontrols.menu.title.general": "Configurações gerais",
"midnightcontrols.menu.title.hud": "Configurações de HUD",
"midnightcontrols.menu.title.mappings.string": "Editor de arquivo de mapeamentos",
"midnightcontrols.menu.title.touch": "Configurações de toque",
"midnightcontrols.menu.title.visual": "Configurações de aparência",
"midnightcontrols.menu.touch_break_delay": "Atrasado de destruição do toque",
"midnightcontrols.menu.touch_speed": "Velocidade do toque",
"midnightcontrols.menu.invert_touch": "Inverter direção do toque",
"midnightcontrols.menu.touch_mode": "Modo de interação de toque",
"midnightcontrols.menu.touch_transparency": "Transparência do HUD de toque",
"midnightcontrols.menu.touch_with_controller": "Toque no modo do controle",
"midnightcontrols.menu.unfocused_input": "Entrada desfocada",
"midnightcontrols.menu.unfocused_input.tooltip": "Permite entrada de sinais do controle quando a janela não está em foco.",
"midnightcontrols.menu.virtual_mouse": "Mouse virtual",
"midnightcontrols.menu.virtual_mouse.tooltip": "Ativa o mouse virtual, que é útil durante o modo tela dividida.",
"midnightcontrols.menu.virtual_mouse.skin": "Tema do mouse virtual",
"midnightcontrols.menu.hide_cursor": "Desativar cursor do mouse padrão",
"midnightcontrols.menu.hide_cursor.tooltip": "Esconde o cursor padrão do mouse, deixando apenas o mouse virtual visível.",
"midnightcontrols.narrator.unbound": "Desmapear %s",
"midnightcontrols.not_bound": "Não mapeado",
"midnightcontrols.virtual_mouse.skin.default_light": "Padrão claro",
"midnightcontrols.virtual_mouse.skin.default_dark": "Padrão escuro",
"midnightcontrols.virtual_mouse.skin.second_light": "Segundo claro",
"midnightcontrols.virtual_mouse.skin.second_dark": "Segundo escuro",
"midnightcontrols.midnightconfig.category.controller": "Controle",
"midnightcontrols.midnightconfig.category.misc": "Variados",
"midnightcontrols.midnightconfig.category.screens": "Telas",
"midnightcontrols.midnightconfig.category.gameplay": "Jogabilidade",
"midnightcontrols.midnightconfig.category.touch": "Toque",
"midnightcontrols.midnightconfig.category.visual": "Visual",
"modmenu.descriptionTranslation.midnightcontrols": "Adiciona suporte a controles e aprimora o controle no geral.\nDerivado do LambdaControls, que infelizmente foi descontinuado."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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