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 54389e5..2bf4adf 100644 Binary files a/res/Theme Source.dat and b/res/Theme Source.dat differ diff --git a/res/Themes/Theme.dat b/res/Themes/Theme.dat index 6cfcfb1..acb9d5b 100644 Binary files a/res/Themes/Theme.dat and b/res/Themes/Theme.dat differ 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