Add missing mouse scrolling in console + Smooth Scrolling (#790)

* Add mouse wheel scrolling for DJUI Console

forgot to do that before

Bonus: optional Smooth Scrolling (activate in menu options)
Dutch - @benjipg
English - me
French - @Blockyyy
German - @iZePlayzYT
Italian - @wall_e20
Russian - @yoyeet961
Spanish - me

Missing translations:
Czech-Help
Japanese-uhhhh
Polish-maybe
Portuguese- might be able to get this one in tomorrow

i really think something needs to be done about the DJUI prefixing

* Return of the eepy

- actually added the config option
- NEW TRANSLATION:
  - portuguese - @saniky

* Czech translation

by @Dominicentek

* add missing translations, correct others

since this is an actual feature present in major programs (e.g. Chromium), it is widely localized and i can find accepted translations for the term
I have gone and used those instead where applicable

* address nitpicking

* unified clamping

* new clamp + fixed chat jumping to 0 when below max
This commit is contained in:
Cooliokid956 2025-05-22 07:26:29 -05:00 committed by GitHub
parent bbabaa3c79
commit aac982a3eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 103 additions and 61 deletions

View File

@ -176,6 +176,7 @@ DJUI_FONT = "DJUI Font"
AUTO = "Automatický"
CENTER = "Střed"
GRADIENTS = "Gradienty"
SMOOTH_SCROLLING = "Plynulé Posouvání"
FONT_NORMAL = "Normální"
FONT_ALIASED = "Hladký"
LIGHT_THEME = "Světlo"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "DJUI-lettertype"
AUTO = "Automatisch"
CENTER = "Centreren"
GRADIENTS = "Kleurverlopen"
SMOOTH_SCROLLING = "Soepel Scrollen"
FONT_NORMAL = "Normaal"
FONT_ALIASED = "Glad"
LIGHT_THEME = "Licht"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "DJUI Font"
AUTO = "Auto"
CENTER = "DJUI Center"
GRADIENTS = "DJUI Gradients"
SMOOTH_SCROLLING = "DJUI Smooth Scrolling"
FONT_NORMAL = "Normal"
FONT_ALIASED = "Aliased"
LIGHT_THEME = "Light"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "Police DJUI"
AUTO = "Automatique"
CENTER = "Centrer le menu"
GRADIENTS = "Dégradés"
SMOOTH_SCROLLING = "Défilement Fluide"
FONT_NORMAL = "Normal"
FONT_ALIASED = "Lisse"
LIGHT_THEME = "Clair"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "DJUI-Schriftart"
AUTO = "Automatisch"
CENTER = "Zentrieren"
GRADIENTS = "Farbverläufe"
SMOOTH_SCROLLING = "Flüssiges Scrollen"
FONT_NORMAL = "Normal"
FONT_ALIASED = "Glatt"
LIGHT_THEME = "Hell"

View File

@ -174,6 +174,7 @@ DJUI_FONT = "Font DJUI"
AUTO = "Automatico"
CENTER = "Centrare"
GRADIENTS = "Gradienti"
SMOOTH_SCROLLING = "Scorrimento Fluido"
FONT_NORMAL = "Normale"
FONT_ALIASED = "Liscio"
LIGHT_THEME = "Luce"

View File

@ -177,6 +177,7 @@ DJUI_FONT = "DJUIのフォント"
AUTO = "自動"
CENTER = "中心にDJUIを表示"
GRADIENTS = "グラデーション"
SMOOTH_SCROLLING = "スムーススクロール"
FONT_NORMAL = "普通"
FONT_ALIASED = "エイリアス"
LIGHT_THEME = "ライト"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "Czcionka DJUI"
AUTO = "Automatyczna"
CENTER = "Wyśrodkowane"
GRADIENTS = "Gradienty"
SMOOTH_SCROLLING = "Płynne Przewijanie"
FONT_NORMAL = "Normalna"
FONT_ALIASED = "Wygładzona"
LIGHT_THEME = "Jasny"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "Fonte da DJUI"
AUTO = "Automático"
CENTER = "Centralizar"
GRADIENTS = "Gradientes"
SMOOTH_SCROLLING = "Rolagem Suave"
FONT_NORMAL = "Normal"
FONT_ALIASED = "Suave"
LIGHT_THEME = "Claro"

View File

@ -175,6 +175,7 @@ DJUI_FONT = "Шрифт DJUI"
AUTO = "Автоматический"
CENTER = "Центр"
GRADIENTS = "Градиенты"
SMOOTH_SCROLLING = "Плавная Прокрутка"
FONT_NORMAL = "Обычный"
FONT_ALIASED = "Гладкий"
LIGHT_THEME = "Свет"

View File

@ -176,6 +176,7 @@ DJUI_FONT = "Fuente DJUI"
AUTO = "Automático"
CENTER = "Centrar"
GRADIENTS = "Gradientes"
SMOOTH_SCROLLING = "Desplazamiento Suave"
FONT_NORMAL = "Normal"
FONT_ALIASED = "Alias"
LIGHT_THEME = "Claro"

View File

@ -128,6 +128,7 @@ unsigned int configGamepadNumber = 0;
bool configBackgroundGamepad = true;
bool configDisableGamepads = false;
bool configUseStandardKeyBindingsChat = false;
bool configSmoothScrolling = false;
// free camera settings
bool configEnableFreeCamera = false;
bool configFreeCameraAnalog = false;
@ -270,6 +271,7 @@ static const struct ConfigOption options[] = {
{.name = "disable_gamepads", .type = CONFIG_TYPE_BOOL, .boolValue = &configDisableGamepads},
#endif
{.name = "use_standard_key_bindings_chat", .type = CONFIG_TYPE_BOOL, .boolValue = &configUseStandardKeyBindingsChat},
{.name = "smooth_scrolling", .type = CONFIG_TYPE_BOOL, .boolValue = &configSmoothScrolling},
{.name = "stick_rotate_left", .type = CONFIG_TYPE_BOOL, .boolValue = &configStick.rotateLeft},
{.name = "stick_invert_left_x", .type = CONFIG_TYPE_BOOL, .boolValue = &configStick.invertLeftX},
{.name = "stick_invert_left_y", .type = CONFIG_TYPE_BOOL, .boolValue = &configStick.invertLeftY},

View File

@ -87,6 +87,7 @@ extern unsigned int configGamepadNumber;
extern bool configBackgroundGamepad;
extern bool configDisableGamepads;
extern bool configUseStandardKeyBindingsChat;
extern bool configSmoothScrolling;
// free camera settings
extern bool configEnableFreeCamera;
extern bool configFreeCameraAnalog;

View File

@ -6,6 +6,7 @@
#include "pc/chat_commands.h"
#include "pc/configfile.h"
#include "djui.h"
#include "engine/math_util.h"
struct DjuiChatBox* gDjuiChatBox = NULL;
bool gDjuiChatBoxFocus = false;
@ -100,6 +101,17 @@ bool djui_chat_box_render(struct DjuiBase* base) {
struct DjuiChatBox* chatBox = (struct DjuiChatBox*)base;
struct DjuiBase* ccBase = &chatBox->chatContainer->base;
djui_base_set_size(ccBase, 1.0f, chatBox->base.comp.height - 32 - 8);
if (chatBox->scrolling) {
f32 yMax = chatBox->chatContainer->base.elem.height - chatBox->chatFlow->base.height.value;
f32 target = chatBox->chatFlow->base.y.value + (chatBox->scrollY - chatBox->chatFlow->base.y.value) * (configSmoothScrolling ? 0.5f : 1.f);
chatBox->chatFlow->base.y.value = clamp(target, yMax, 0.f);
if (target < yMax || 0.f < target) {
chatBox->scrollY = clamp(target, yMax, 0.f);
}
} else { chatBox->scrollY = chatBox->chatFlow->base.y.value; }
printf("%f\n", chatBox->chatFlow->base.y.value);
if (sDjuiChatBoxClearText) {
sDjuiChatBoxClearText = false;
djui_inputbox_set_text(gDjuiChatBox->chatInput, "");
@ -407,10 +419,7 @@ static bool djui_chat_box_input_on_key_down(struct DjuiBase* base, int scancode)
if (gDjuiChatBox == NULL) { return false; }
f32 yMax = gDjuiChatBox->chatContainer->base.elem.height - gDjuiChatBox->chatFlow->base.height.value;
f32* yValue = &gDjuiChatBox->chatFlow->base.y.value;
bool canScrollUp = (*yValue > yMax);
bool canScrollDown = (*yValue < 0);
f32 pageAmount = gDjuiChatBox->chatContainer->base.elem.height * 3.0f / 4.0f;
char previousText[MAX_CHAT_MSG_LENGTH];
@ -419,74 +428,59 @@ static bool djui_chat_box_input_on_key_down(struct DjuiBase* base, int scancode)
switch (scancode) {
case SCANCODE_UP:
if (!configUseStandardKeyBindingsChat && (gDjuiChatBox->chatInput && gDjuiChatBox->chatInput->buffer && gDjuiChatBox->chatInput->buffer[0] != '/')) {
gDjuiChatBox->scrolling = true;
if (canScrollDown) { *yValue = fmin(*yValue + 15, 0); }
gDjuiChatBox->scrollY += 15;
} else {
sent_history_update_current_message(&sentHistory, gDjuiChatBox->chatInput->buffer);
sent_history_navigate(&sentHistory, true);
if (strcmp(previousText, gDjuiChatBox->chatInput->buffer) != 0) { reset_tab_completion_all(); }
}
return true;
break;
case SCANCODE_DOWN:
if (!configUseStandardKeyBindingsChat && (gDjuiChatBox->chatInput && gDjuiChatBox->chatInput->buffer && gDjuiChatBox->chatInput->buffer[0] != '/')) {
gDjuiChatBox->scrolling = true;
if (canScrollUp) { *yValue = fmax(*yValue - 15, yMax); }
gDjuiChatBox->scrollY -= 15;
} else {
sent_history_update_current_message(&sentHistory, gDjuiChatBox->chatInput->buffer);
sent_history_navigate(&sentHistory, false);
if (strcmp(previousText, gDjuiChatBox->chatInput->buffer) != 0) { reset_tab_completion_all(); }
}
return true;
break;
case SCANCODE_PAGE_UP:
gDjuiChatBox->scrolling = true;
if (canScrollDown) {
if (configUseStandardKeyBindingsChat) {
*yValue = fmin(*yValue + 15, 0);
} else {
*yValue = fmin(*yValue + pageAmount, 0);
}
}
return true;
gDjuiChatBox->scrollY += configUseStandardKeyBindingsChat ? 15 : pageAmount;
break;
case SCANCODE_PAGE_DOWN:
gDjuiChatBox->scrolling = true;
if (canScrollUp) {
if (configUseStandardKeyBindingsChat) {
*yValue = fmax(*yValue - 15, yMax);
} else {
*yValue = fmax(*yValue - pageAmount, yMax);
}
}
return true;
gDjuiChatBox->scrollY -= configUseStandardKeyBindingsChat ? 15 : pageAmount;
break;
case SCANCODE_POS1:
gDjuiChatBox->scrolling = true;
if (canScrollDown) { *yValue = fmin(*yValue + pageAmount, 0); }
return true;
gDjuiChatBox->scrollY += pageAmount;
break;
case SCANCODE_END:
gDjuiChatBox->scrolling = true;
if (canScrollUp) { *yValue = fmax(*yValue - pageAmount, yMax); }
return true;
gDjuiChatBox->scrollY -= pageAmount;
break;
case SCANCODE_TAB:
handle_tab_completion();
return true;
break;
case SCANCODE_ENTER:
reset_tab_completion_all();
sent_history_reset_navigation(&sentHistory);
djui_chat_box_input_enter(gDjuiChatBox->chatInput);
return true;
break;
case SCANCODE_ESCAPE:
reset_tab_completion_all();
sent_history_reset_navigation(&sentHistory);
djui_chat_box_input_escape(gDjuiChatBox->chatInput);
return true;
break;
default:
{
bool returnValueOnOtherKeyDown = djui_inputbox_on_key_down(base, scancode);
if (strcmp(previousText, gDjuiChatBox->chatInput->buffer) != 0) {
reset_tab_completion_all();
}
return returnValueOnOtherKeyDown;
bool returnValueOnOtherKeyDown = djui_inputbox_on_key_down(base, scancode);
if (strcmp(previousText, gDjuiChatBox->chatInput->buffer) != 0) {
reset_tab_completion_all();
}
return returnValueOnOtherKeyDown;
}
if (!gDjuiConsole->scrolling) {
gDjuiConsole->scrolling = gDjuiConsole->scrollY < 0 && gDjuiConsole->scrollY > yMax;
}
return true;
}
static void djui_chat_box_input_on_text_input(struct DjuiBase *base, char* text) {
@ -506,17 +500,16 @@ static void djui_chat_box_input_on_scroll(UNUSED struct DjuiBase *base, UNUSED f
if (gDjuiChatBox == NULL) { return; }
f32 yMax = gDjuiChatBox->chatContainer->base.elem.height - gDjuiChatBox->chatFlow->base.height.value;
f32* yValue = &gDjuiChatBox->chatFlow->base.y.value;
bool canScrollUp = (*yValue > yMax);
bool canScrollDown = (*yValue < 0);
y *= 24;
y *= 24.f;
if (gDjuiInputHeldControl) { y /= 2; }
if (gDjuiInputHeldShift) { y *= 3; }
gDjuiChatBox->scrolling = true;
if (y > 0 && canScrollDown) { *yValue = fmin(*yValue + y, 0); }
if (y < 0 && canScrollUp) { *yValue = fmax(*yValue + y, yMax); }
gDjuiChatBox->scrollY += y;
if (!gDjuiConsole->scrolling) {
gDjuiConsole->scrolling = gDjuiConsole->scrollY < 0 && gDjuiConsole->scrollY > yMax;
}
}
void djui_chat_box_toggle(void) {

View File

@ -7,6 +7,7 @@ struct DjuiChatBox {
struct DjuiFlowLayout* chatFlow;
struct DjuiInputbox* chatInput;
bool scrolling;
f32 scrollY;
};
extern struct DjuiChatBox* gDjuiChatBox;

View File

@ -3,6 +3,7 @@
#include "djui.h"
#include "djui_console.h"
#include "pc/pc_main.h"
#include "engine/math_util.h"
#define MAX_CONSOLE_MESSAGES 500
@ -50,7 +51,18 @@ void djui_console_message_dequeue(void) {
}
bool djui_console_render(struct DjuiBase* base) {
struct DjuiConsole* console = (struct DjuiConsole*)base;
djui_base_set_size(base, gDjuiRoot->base.width.value, gDjuiRoot->base.height.value * 0.5f);
if (console->scrolling) {
f32 yMax = console->base.comp.height - console->flow->base.height.value;
f32 target = console->flow->base.y.value + (console->scrollY - console->flow->base.y.value) * (configSmoothScrolling ? 0.5f : 1.f);
console->flow->base.y.value = clamp(target, yMax, 0.f);
if (target < yMax || 0.f < target) {
console->scrollY = clamp(target, yMax, 0.f);
if (target > 0.f) { gDjuiConsole->scrolling = false; }
}
} else { console->scrollY = console->flow->base.y.value; }
djui_rect_render(base);
return true;
@ -74,32 +86,48 @@ void djui_console_toggle(void) {
}
}
static void djui_console_on_scroll(UNUSED struct DjuiBase *base, UNUSED float x, float y) {
if (gDjuiConsole == NULL) { return; }
f32 yMax = gDjuiConsole->base.comp.height - gDjuiConsole->flow->base.height.value;
y *= 24.f;
if (gDjuiInputHeldControl) { y /= 2; }
if (gDjuiInputHeldShift) { y *= 3; }
gDjuiConsole->scrollY -= y;
if (!gDjuiConsole->scrolling) {
gDjuiConsole->scrolling = y > 0 && gDjuiConsole->scrollY > yMax;
}
}
static bool djui_console_on_key_down(UNUSED struct DjuiBase* base, int scancode) {
if (gDjuiConsole == NULL) { return false; }
f32 yMax = gDjuiConsole->base.comp.height - gDjuiConsole->flow->base.height.value;
f32* yValue = &gDjuiConsole->flow->base.y.value;
bool canScrollUp = (*yValue > yMax);
bool canScrollDown = (*yValue < 0);
f32 pageAmount = gDjuiConsole->base.comp.height * 3.0f / 4.0f;
switch (scancode) {
case SCANCODE_UP:
if (canScrollUp) { *yValue = fmax(*yValue - 15, yMax); }
gDjuiConsole->scrollY -= 15;
break;
case SCANCODE_DOWN:
if (canScrollDown) { *yValue = fmin(*yValue + 15, 0); }
gDjuiConsole->scrollY += 15;
break;
case SCANCODE_PAGE_UP:
if (canScrollUp) { *yValue = fmax(*yValue - pageAmount, yMax); }
gDjuiConsole->scrollY -= pageAmount;
break;
case SCANCODE_PAGE_DOWN:
if (canScrollDown) { *yValue = fmin(*yValue + pageAmount, 0); }
gDjuiConsole->scrollY += pageAmount;
break;
case SCANCODE_ESCAPE: djui_console_toggle(); break;
default: break;
}
gDjuiConsole->scrolling = (*yValue != 0);
if (!gDjuiConsole->scrolling) {
gDjuiConsole->scrolling = gDjuiConsole->scrollY < 0 && gDjuiConsole->scrollY > yMax;
}
return true;
}
@ -139,8 +167,9 @@ void djui_console_message_create(const char* message, enum ConsoleMessageLevel l
f32 heightAdjust = messageHeight + gDjuiConsole->flow->margin.value;
cfBase->height.value += heightAdjust;
if (gDjuiConsole->scrolling) {
if (gDjuiConsole->scrolling && gDjuiConsole->scrollY != 0) {
cfBase->y.value -= heightAdjust;
gDjuiConsole->scrollY -= heightAdjust;
}
sDjuiConsoleMessages++;
@ -148,8 +177,9 @@ void djui_console_message_create(const char* message, enum ConsoleMessageLevel l
if (cfBase->child) {
heightAdjust = cfBase->child->base->height.value + gDjuiConsole->flow->margin.value;
cfBase->height.value -= heightAdjust;
if (gDjuiConsole->scrolling) {
if (gDjuiConsole->scrolling && gDjuiConsole->scrollY != 0) {
cfBase->y.value += heightAdjust;
gDjuiConsole->scrollY += heightAdjust;
}
}
@ -177,6 +207,7 @@ struct DjuiConsole* djui_console_create(void) {
djui_interactable_create(base, NULL);
djui_interactable_hook_key(base, djui_console_on_key_down, NULL);
djui_interactable_hook_scroll(base, djui_console_on_scroll);
struct DjuiFlowLayout* flow = djui_flow_layout_create(base);
struct DjuiBase* cfBase = &flow->base;

View File

@ -11,6 +11,7 @@ struct DjuiConsole {
struct DjuiBase base;
struct DjuiFlowLayout* flow;
bool scrolling;
f32 scrollY;
};
#define CONSOLE_MAX_TMP_BUFFER 512

View File

@ -137,6 +137,8 @@ void djui_panel_main_menu_create(struct DjuiBase* caller) {
char* djuiFontChoices[2] = {DLANG(DJUI_THEMES, FONT_NORMAL), DLANG(DJUI_THEMES, FONT_ALIASED)};
djui_selectionbox_create(body, DLANG(DJUI_THEMES, DJUI_FONT), djuiFontChoices, 2, &configDjuiThemeFont, djui_panel_menu_options_djui_setting_change);
djui_checkbox_create(body, DLANG(DJUI_THEMES, SMOOTH_SCROLLING), &configSmoothScrolling, NULL);
if (gDjuiInMainMenu) {
// copy sound choices from gMainMenuSounds
int numSounds = sizeof(gMainMenuSounds) / sizeof(gMainMenuSounds[0]);