From abf4494701bce619dc33475a744b3276fd365c2e Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Tue, 24 Aug 2021 19:00:54 +0100
Subject: [PATCH] sliders

---
 desktop/gui.cpp       |  95 ++++++++++++++++++++++++++++++++++++++++++
 desktop/os.header     |  15 ++++++-
 desktop/settings.cpp  |  11 ++++-
 desktop/styles.header |   2 +
 res/Theme Source.dat  | Bin 50052 -> 50824 bytes
 res/Themes/Theme.dat  | Bin 52048 -> 52516 bytes
 util/api_table.ini    |   3 ++
 7 files changed, 123 insertions(+), 3 deletions(-)

diff --git a/desktop/gui.cpp b/desktop/gui.cpp
index 3b8b0ce..168ba1b 100644
--- a/desktop/gui.cpp
+++ b/desktop/gui.cpp
@@ -4946,6 +4946,101 @@ EsIconDisplay *EsIconDisplayCreate(EsElement *parent, uint64_t flags, const EsSt
 	return display;
 }
 
+// --------------------------------- Sliders.
+
+struct EsSlider : EsElement {
+	EsElement *point;
+	double value;
+	uint32_t steps;
+	int32_t dragOffset;
+	bool inDrag, endDrag;
+};
+
+int ProcessSliderPointMessage(EsElement *element, EsMessage *message) {
+	EsSlider *slider = (EsSlider *) EsElementGetLayoutParent(element);
+
+	if (message->type == ES_MSG_MOUSE_LEFT_DRAG) {
+		double range = slider->width - slider->point->currentStyle->preferredWidth;
+		slider->inDrag = true;
+		EsSliderSetValue(slider, (message->mouseDragged.newPositionX + element->offsetX - slider->dragOffset) / range);
+	} else if (message->type == ES_MSG_MOUSE_LEFT_UP && slider->inDrag) {
+		slider->inDrag = false;
+		slider->endDrag = true; // Force sending the update message.
+		EsSliderSetValue(slider, slider->value);
+		slider->endDrag = false;
+	} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) {
+		slider->dragOffset = message->mouseDown.positionX;
+		EsElementFocus(slider);
+	} else {
+		return 0;
+	}
+
+	return ES_HANDLED;
+}
+
+int ProcessSliderMessage(EsElement *element, EsMessage *message) {
+	EsSlider *slider = (EsSlider *) element;
+
+	if (message->type == ES_MSG_LAYOUT) {
+		int pointWidth = slider->point->currentStyle->preferredWidth;
+		int pointHeight = slider->point->currentStyle->preferredHeight;
+		slider->point->InternalMove(pointWidth, pointHeight, (slider->width - pointWidth) * slider->value, (slider->height - pointHeight) / 2);
+	} else if (message->type == ES_MSG_FOCUSED_START) {
+		slider->point->customStyleState |= THEME_STATE_FOCUSED_ITEM;
+		slider->point->MaybeRefreshStyle();
+	} else if (message->type == ES_MSG_FOCUSED_END) {
+		slider->point->customStyleState &= ~THEME_STATE_FOCUSED_ITEM;
+		slider->point->MaybeRefreshStyle();
+	} else if (message->type == ES_MSG_KEY_TYPED && message->keyboard.scancode == ES_SCANCODE_LEFT_ARROW) {
+		EsSliderSetValue(slider, slider->value - (slider->steps ? 1.0 / slider->steps : 0.02));
+	} else if (message->type == ES_MSG_KEY_TYPED && message->keyboard.scancode == ES_SCANCODE_RIGHT_ARROW) {
+		EsSliderSetValue(slider, slider->value + (slider->steps ? 1.0 / slider->steps : 0.02));
+	} else if (message->type == ES_MSG_KEY_TYPED && message->keyboard.scancode == ES_SCANCODE_HOME) {
+		EsSliderSetValue(slider, 0.0);
+	} else if (message->type == ES_MSG_KEY_TYPED && message->keyboard.scancode == ES_SCANCODE_END) {
+		EsSliderSetValue(slider, 1.0);
+	} else if (message->type == ES_MSG_PAINT) {
+		// TODO Draw ticks.
+	} else {
+		return 0;
+	}
+
+	return ES_HANDLED;
+}
+
+double EsSliderGetValue(EsSlider *slider) {
+	return slider->value;
+}
+
+void EsSliderSetValue(EsSlider *slider, double newValue, bool sendUpdatedMessage) {
+	newValue = ClampDouble(0.0, 1.0, newValue);
+
+	if (slider->steps) {
+		newValue = EsCRTfloor((slider->steps - 1) * newValue + 0.5) / (slider->steps - 1);
+	}
+
+	double previous = slider->value;
+	if (previous == newValue && !slider->endDrag) return;
+	slider->value = newValue;
+	EsElementRelayout(slider);
+
+	if (sendUpdatedMessage) {
+		EsMessage m = { ES_MSG_SLIDER_MOVED, .sliderMoved = { .value = newValue, .previous = previous, .inDrag = slider->inDrag } };
+		EsMessageSend(slider, &m);
+	}
+}
+
+EsSlider *EsSliderCreate(EsElement *parent, uint64_t flags, const EsStyle *style, double value, uint32_t steps) {
+	EsSlider *slider = (EsSlider *) EsHeapAllocate(sizeof(EsSlider), true);
+	slider->Initialise(parent, flags | ES_ELEMENT_FOCUSABLE, ProcessSliderMessage, style ?: ES_STYLE_SLIDER_TRACK);
+	slider->cName = "slider";
+	slider->point = EsCustomElementCreate(slider, ES_FLAGS_DEFAULT, ES_STYLE_SLIDER_POINT);
+	slider->point->messageUser = ProcessSliderPointMessage;
+	slider->steps = steps;
+	EsSliderSetValue(slider, value, false);
+	return slider;
+}
+
 // --------------------------------- Message loop and core UI infrastructure.
 
 void EsElement::PrintTree(int depth) {
diff --git a/desktop/os.header b/desktop/os.header
index aecab21..4a466ee 100644
--- a/desktop/os.header
+++ b/desktop/os.header
@@ -13,6 +13,7 @@ opaque_type EsSplitter         EsElement;
 opaque_type EsImageDisplay     EsElement;
 opaque_type EsListDisplay      EsElement;
 opaque_type EsCanvasPane       EsElement;
+opaque_type EsSlider           EsElement;
 opaque_type EsTextPlan         none;
 opaque_type EsPaintTarget      none;
 opaque_type EsUndoManager      none;
@@ -945,6 +946,7 @@ enum EsMessageType {
 	ES_MSG_COLOR_CHANGED			= 0x3003	// Color well's color has changed. See message->colorChanged.
 	ES_MSG_LIST_DISPLAY_GET_MARKER		= 0x3004	// Get the string for a marker in an EsListDisplay. See message->getContent.
 	ES_MSG_REORDER_ITEM_TEST		= 0x3005
+	ES_MSG_SLIDER_MOVED			= 0x3006	// The slider has been moved.
 
 	// Desktop messages: 
 	ES_MSG_POWER_BUTTON_PRESSED		= 0x4801
@@ -1637,6 +1639,11 @@ struct EsMessageScrollbarMoved {
 	int scroll, previous;
 };
 
+struct EsMessageSliderMoved {
+	double value, previous;
+	bool inDrag;
+};
+
 struct EsMessageColorChanged {
 	uint32_t newColor;
 	bool pickerClosed;
@@ -1782,6 +1789,7 @@ struct EsMessage {
 
 		// Specific element messages:
 		EsMessageScrollbarMoved scrollbarMoved;
+		EsMessageSliderMoved sliderMoved;
 		EsMessageColorChanged colorChanged;
 		EsMessageNumberDragDelta numberDragDelta;
 		EsMessageNumberUpdated numberUpdated;
@@ -2315,7 +2323,6 @@ function EsElement *EsButtonGetCheckBuddy(EsButton *button);  // TODO Public pro
 // Textboxes.
 
 function EsTextbox *EsTextboxCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL);
-
 function bool EsTextboxFind(EsTextbox *textbox, STRING string, int32_t *line, int32_t *byte, uint32_t flags);
 function void EsTextboxInsert(EsTextbox *textbox, STRING string = BLANK_STRING, bool sendUpdatedMessage = true); // Deletes existing selection first.
 function char *EsTextboxGetContents(EsTextbox *textbox, size_t *bytes = ES_NULL, uint32_t flags = ES_FLAGS_DEFAULT); // Result will be zero-terminated; free with EsHeapFree.
@@ -2336,6 +2343,12 @@ function void EsTextboxSetTextStyle(EsTextbox *textbox, const EsTextStyle *textS
 function void EsTextboxSetupSyntaxHighlighting(EsTextbox *textbox, uint32_t language, uint32_t *customColors = ES_NULL, size_t customColorCount = 0);
 function void EsTextboxStartEdit(EsTextbox *textbox);
 
+// Sliders.
+
+function EsSlider *EsSliderCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL, double value = 0, uint32_t steps = 0);
+function double EsSliderGetValue(EsSlider *slider);
+function void EsSliderSetValue(EsSlider *slider, double newValue, bool sendUpdatedMessage = true);
+
 // Panels, spacers and splitters.
 
 function EsPanel *EsPanelCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL);
diff --git a/desktop/settings.cpp b/desktop/settings.cpp
index a38f0be..7e4717e 100644
--- a/desktop/settings.cpp
+++ b/desktop/settings.cpp
@@ -324,6 +324,15 @@ void SettingsPageMouse(EsElement *element, SettingsPage *page) {
 	// TODO Use a slider instead?
 	SettingsAddNumberBox(table, INTERFACE_STRING(DesktopSettingsMouseSpeed), 'M', "general", "cursor_speed", -20, 20, nullptr, 0, 0.05);
 
+	table = EsPanelCreate(container, ES_CELL_H_FILL, &styleSettingsCheckboxGroup);
+	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseUseAcceleration), 'C', "general", "use_cursor_acceleration");
+	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseSlowOnAlt), 'O', "general", "use_cursor_alt_slow");
+
+	EsSpacerCreate(container, ES_CELL_H_FILL, ES_STYLE_BUTTON_GROUP_SEPARATOR);
+
+	table = EsPanelCreate(container, ES_CELL_H_FILL | ES_PANEL_TABLE | ES_PANEL_HORIZONTAL, &styleSettingsTable);
+	EsPanelSetBands(table, 2);
+
 	// TODO Mouse trails.
 	EsTextDisplayCreate(table, ES_CELL_H_RIGHT | ES_CELL_H_PUSH, 0, INTERFACE_STRING(DesktopSettingsMouseCursorTrails));
 	textbox = EsTextboxCreate(table, ES_CELL_H_LEFT | ES_CELL_H_PUSH | ES_TEXTBOX_EDIT_BASED, &styleSettingsNumberTextbox);
@@ -342,8 +351,6 @@ void SettingsPageMouse(EsElement *element, SettingsPage *page) {
 	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseSwapLeftAndRightButtons), 'B', "general", "swap_left_and_right_buttons");
 	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseShowShadow), 'W', "general", "show_cursor_shadow");
 	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseLocateCursorOnCtrl), 'L', "general", "locate_cursor_on_ctrl");
-	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseUseAcceleration), 'C', "general", "use_cursor_acceleration");
-	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseSlowOnAlt), 'O', "general", "use_cursor_alt_slow");
 
 	EsSpacerCreate(container, ES_CELL_H_FILL, ES_STYLE_BUTTON_GROUP_SEPARATOR);
 
diff --git a/desktop/styles.header b/desktop/styles.header
index 98d21d2..bcaecb7 100644
--- a/desktop/styles.header
+++ b/desktop/styles.header
@@ -88,6 +88,8 @@ define_private ES_STYLE_SCROLLBAR_THUMB_HORIZONTAL (ES_STYLE_CAST(1367))
 define_private ES_STYLE_SCROLLBAR_THUMB_VERTICAL (ES_STYLE_CAST(1369))
 define_private ES_STYLE_SCROLLBAR_PAD (ES_STYLE_CAST(1371))
 define ES_STYLE_SEPARATOR_HORIZONTAL (ES_STYLE_CAST(1373))
+define_private ES_STYLE_SLIDER_POINT (ES_STYLE_CAST(1607))
+define_private ES_STYLE_SLIDER_TRACK (ES_STYLE_CAST(1601))
 define_private ES_STYLE_SPLIT_BAR_HORIZONTAL (ES_STYLE_CAST(1375))
 define_private ES_STYLE_SPLIT_BAR_VERTICAL (ES_STYLE_CAST(1377))
 define_private ES_STYLE_TASK_BAR_BAR (ES_STYLE_CAST(1379))
diff --git a/res/Theme Source.dat b/res/Theme Source.dat
index 54389e5b6f4d0d7a7c15f4b4d0dc39197e22e1e3..2bf4adf9199a24638b53aca9a8bb62e50cc9de30 100644
GIT binary patch
delta 421
zcmZo^XYS}_W*22(U{G(X*vQVzrQf2`meZ`-rry>QoRgW7TBH|{pP5&p+{VDzq}Zke
z7Y!*&OwLwlW7y2ewODxbb~$x5mOm_iSvNm&$uOB5Fk51>RCyJM-Yiwl!?^iS%~a{h
z=V#8@EH*QgadW_I5$4VPi!E6;n=P*4XJ%Avn_Q?PJ^6zJD|1c5?8zHFB_`Ky5Y|y@
zYiI>q19aVgCWaIvqXtGG69kxmpc%|+k^)g;lOLLhOrAbhesaTH8zvQyc?wLECc%ti
z%4wd=D5Sf2mP;ZdlWN=KkHT_nTtLudJ-Jcagt1}rLw@VY=hyyYRGWNZtrmY{<A3IV
ztxb);fn;+d>tz0Q3T!}TLnGs4{dMBvY`^}2UDFJ64Koll^h_2^H=W$O+<Nk<WyX`E
zT2C{o0}V3*vP2YkSpRi2G&VLu=q4!L+{m-30pxnd$$hPgESx|vd1G7Q=1rT<GcgKo
OZrUosy?M=n8%zMxAd-Io

delta 369
zcmeC^Wo~I_W*22(V32Ps+sMw$wb_bmkuam$WCbM&A!b%)<_4e?L&NL_AYfr(nOrL)
zt0u|>6zU2|ttip+NlZ%3Vbo}BWNc(iX=MA?(a_Y`(839lpIoRcHn~rccakj2WP>!8
z$#><7Hs6s|SKF+#L6&i|f~&tVQ)J8JiM1w^53FUI9G@aQxvf}YvVB?A<ot?BO3^J1
zZBF?`DXB#aT;BxVF{Ci0G%$jk0tHPClP6XbOx{^Gi7{sKy>f-gx5^5cVo}YzTRv&?
z_9|v+#?r~*(-bE2PM^0qY+5KIW7XsdwWdH@C7G&QCo765GyP?n9JtnabN=)IW_jMW
zhGwVy3I+zgpX>|_Mn)*MHmgparz}6YZMyvA{OLBEKgrK!-286A9Hz}b=GE|TKC|^a
m6Qj`PIXgtSg)>r9OPo@3^2^;ab8=GLx&PO;ZeDx%HWL8VVSgY1

diff --git a/res/Themes/Theme.dat b/res/Themes/Theme.dat
index 6cfcfb1b8ec04e6a9f5b80474db9d8acf0c6d29a..acb9d5b01680a259b393c01b104a9c62e097755b 100644
GIT binary patch
delta 4898
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
QLUc1=vq8Cd^jFsZ10byg)Bpeg

delta 4729
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

diff --git a/util/api_table.ini b/util/api_table.ini
index c2ad139..44645a6 100644
--- a/util/api_table.ini
+++ b/util/api_table.ini
@@ -294,7 +294,9 @@ EsTextPlanGetLineCount=292
 EsTextPlanDestroy=293
 EsTextDisplaySetContents=294
 EsSystemConfigurationReadInteger=295
+EsSliderCreate=296
 EsSystemConfigurationReadString=297
+EsSliderGetValue=298
 EsGameControllerStatePoll=299
 EsInstanceDestroy=300
 EsRandomU8=301
@@ -418,3 +420,4 @@ EsFontDatabaseInsertFile=418
 EsFileStoreGetSize=419
 EsFileStoreMap=420
 EsTimerCancel=421
+EsSliderSetValue=422