Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions src/tel/eden/mod/EdenModClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,24 @@ public boolean isOpenEmotePickerMouseBound() {
return openEmotePickerKey.saveString().startsWith("key.mouse.");
}

public boolean matchesOpenEmotePickerKey(net.minecraft.client.input.KeyEvent event) {
return openEmotePickerKey.matches(event);
}

/**
* If the picker shares chat's gameplay keybind, vanilla opens chat before the chat
* screen can inspect the triggering press. Detect that held key during chat init
* so the picker still opens without requiring a second press.
*/
public boolean shouldOpenEmotePickerOnChatOpen() {
Minecraft mc = Minecraft.getInstance();
if (isOpenEmotePickerMouseBound() || mc.options == null) {
if (!shouldAllowGameplayEmotePickerOpen() || isOpenEmotePickerMouseBound() || mc.options == null) {
return false;
}
return openEmotePickerKey.saveString().equals(mc.options.keyChat.saveString()) && openEmotePickerKey.isDown();
return edenmod$matchesGameplayChatKeybind(mc.options.keyChat) || edenmod$matchesGameplayChatKeybind(mc.options.keyCommand);
}

/** Queue the emote picker for the next chat screen, opening chat if needed. */
public void openCenteredEmotePicker() {
Minecraft mc = Minecraft.getInstance();
requestCenteredEmotePicker();
Expand All @@ -339,6 +349,24 @@ public java.util.List<PendingEntry> knownPendingAspects() {
return knownPendingAspects;
}

private boolean shouldAllowGameplayEmotePickerOpen() {
if (!config.emotePickerOpenFromGameplay) {
return false;
}
return switch (config.chatEmoteToolsMode) {
case UI, UI_AND_AUTO -> true;
case AUTO, NONE -> false;
};
}

private boolean shouldOpenEmotePickerFromGameplay(Minecraft client) {
return shouldAllowGameplayEmotePickerOpen() && client.screen == null;
}

private boolean edenmod$matchesGameplayChatKeybind(KeyMapping vanillaKey) {
return openEmotePickerKey.saveString().equals(vanillaKey.saveString()) && openEmotePickerKey.isDown();
}

/** The error from the last aspects-pending reply, or {@code null} if it succeeded. */
public String pendingAspectsError() {
return pendingAspectsError;
Expand Down Expand Up @@ -555,10 +583,12 @@ private void onClientTick(Minecraft client) {
}
}
while (openEmotePickerKey.consumeClick()) {
if (client.screen instanceof ChatScreen && isOpenEmotePickerMouseBound()) {
continue;
// ChatScreenMixin handles picker input while chat is already open. Only the
// gameplay-entry path goes through this tick hook, keeping the hot path to one
// cheap screen/config check and preventing stale queued clicks from replaying.
if (shouldOpenEmotePickerFromGameplay(client)) {
openCenteredEmotePicker();
}
openCenteredEmotePicker();
}
pollCommandKeybinds(client);
if (pendingUpdateNotification && client.player != null) {
Expand Down
7 changes: 7 additions & 0 deletions src/tel/eden/mod/config/BridgeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ public String label() {
/** Which chat emote tools are enabled: inline/picker UI, autocomplete, both, or none. */
public ChatEmoteToolsMode chatEmoteToolsMode = ChatEmoteToolsMode.UI_AND_AUTO;

/**
* Whether the emote-picker keybind may open chat directly from gameplay when no
* other screen is open. When disabled, the binding only works while chat is
* already focused.
*/
public boolean emotePickerOpenFromGameplay = true;

/** Visible emote-picker columns in the chat overlay. */
public int emotePickerColumns = 5;

Expand Down
1 change: 1 addition & 0 deletions src/tel/eden/mod/gui/BridgeConfigScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ protected void init() {
addToggleRow("My login/logout messages", () -> config.announceSelfPresence, v -> config.announceSelfPresence = v, "On", "Off", true);
addToggleRow("Party feed", () -> config.partyAnnounce, v -> config.partyAnnounce = v, "On", "Off", true);
addCycleRow("Chat emote tools", () -> config.chatEmoteToolsMode.label(), () -> config.chatEmoteToolsMode = nextChatEmoteToolsMode(config.chatEmoteToolsMode), () -> config.chatEmoteToolsMode = BridgeConfig.ChatEmoteToolsMode.UI_AND_AUTO);
addToggleRow("Allow emote picker outside chat", () -> config.emotePickerOpenFromGameplay, v -> config.emotePickerOpenFromGameplay = v, "Allowed", "Chat only", true);
addCycleRow("Game messages", () -> shortGameModeLabel(config.gameDisplayMode), () -> config.gameDisplayMode = nextGameMode(config.gameDisplayMode), () -> config.gameDisplayMode = BridgeConfig.GameDisplayMode.ALL);
PreviewSizeSlider slider = new PreviewSizeSlider(CONTROL_W, 20);
addSliderRow("Image preview size", slider, slider::syncFromConfig, () -> config.imagePreviewSize = 40);
Expand Down
21 changes: 21 additions & 0 deletions src/tel/eden/mod/mixin/ChatScreenMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ public abstract class ChatScreenMixin {
@Unique
private int edenmod$sliderPreviewRows = -1;

@Unique
private boolean edenmod$suppressPickerKeyChars;

@Inject(method = "init", at = @At("TAIL"))
private void edenmod$addEmoteFormatter(CallbackInfo ci) {
input.addFormatter((text, cursor) -> {
Expand All @@ -152,6 +155,8 @@ public abstract class ChatScreenMixin {
FormattedCharSequence formatted = ChatEmoteFormatter.format(text);
return formatted;
});
// Shared chat/picker bindings open the screen before this mixin sees the original
// key press, so init needs to revive the queued picker request for that case.
if (EdenModClient.instance().shouldOpenEmotePickerOnChatOpen()) {
EdenModClient.instance().requestCenteredEmotePicker();
}
Expand All @@ -164,6 +169,14 @@ public abstract class ChatScreenMixin {
edenmod$resetOverlayState();
return;
}
if (edenmod$isChatEmoteUiVisible() && !edenmod$pickerOpen && EdenModClient.instance().matchesOpenEmotePickerKey(event)) {
double[] mouse = edenmod$currentMousePosition();
edenmod$openPickerForMode(mouse[0], mouse[1]);
edenmod$suppressPickerKeyChars = true;
input.setEditable(false);
cir.setReturnValue(true);
return;
}
if (edenmod$isChatEmoteUiVisible() && edenmod$pickerOpen && event.key() == GLFW.GLFW_KEY_ESCAPE) {
if (edenmod$pickerSettingsOpen) {
edenmod$pickerSettingsOpen = false;
Expand Down Expand Up @@ -261,6 +274,10 @@ public abstract class ChatScreenMixin {

@Inject(method = "render", at = @At("TAIL"))
private void edenmod$renderChatOverlays(GuiGraphics graphics, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (edenmod$suppressPickerKeyChars) {
input.setEditable(true);
edenmod$suppressPickerKeyChars = false;
}
// Hovering an attack-timer head shows that player's IGN (works regardless of the
// emote-tool settings, like the timer click).
AttackTimerMenu.renderGoerTooltip(graphics, mouseX, mouseY);
Expand Down Expand Up @@ -989,6 +1006,10 @@ public abstract class ChatScreenMixin {

@Unique
private void edenmod$resetOverlayState() {
if (edenmod$suppressPickerKeyChars) {
input.setEditable(true);
edenmod$suppressPickerKeyChars = false;
}
edenmod$pickerOpen = false;
edenmod$pickerSettingsOpen = false;
edenmod$draggingSlider = null;
Expand Down
Loading