diff --git a/apps/posix_launcher.cpp b/apps/posix_launcher.cpp index a1c1d32..c7390ef 100644 --- a/apps/posix_launcher.cpp +++ b/apps/posix_launcher.cpp @@ -269,6 +269,7 @@ void MessageLoopThread(EsGeneric) { textboxOutput = EsTextboxCreate(panel, ES_TEXTBOX_MULTILINE | ES_CELL_FILL, &styleMonospacedTextbox); EsSpacerCreate(panel, ES_CELL_H_FILL, ES_STYLE_SEPARATOR_HORIZONTAL); textboxInput = EsTextboxCreate(panel, ES_CELL_H_FILL, &styleMonospacedTextbox); + EsTextboxEnableSmartQuotes(textboxInput, false); textboxInput->messageUser = ProcessTextboxInputMessage; EsElementFocus(textboxInput); } else if (message->type == MSG_RECEIVED_OUTPUT) { diff --git a/apps/system_monitor.cpp b/apps/system_monitor.cpp index ec8dc57..3e25449 100644 --- a/apps/system_monitor.cpp +++ b/apps/system_monitor.cpp @@ -377,7 +377,7 @@ void ProcessApplicationMessage(EsMessage *message) { EsPanel *switcher = EsPanelCreate(window, ES_CELL_FILL | ES_PANEL_SWITCHER, ES_STYLE_PANEL_WINDOW_DIVIDER); instance->switcher = switcher; - instance->textboxGeneralLog = EsTextboxCreate(switcher, ES_TEXTBOX_MULTILINE | ES_CELL_FILL, &styleMonospacedTextbox); + instance->textboxGeneralLog = EsTextboxCreate(switcher, ES_TEXTBOX_MULTILINE | ES_CELL_FILL | ES_ELEMENT_DISABLED, &styleMonospacedTextbox); AddListView(&instance->listViewProcesses, switcher, ListViewProcessesCallback, listViewProcessesColumns, sizeof(listViewProcessesColumns), ES_LIST_VIEW_SINGLE_SELECT); diff --git a/apps/text_editor.cpp b/apps/text_editor.cpp index 3bd2c2a..bde33f2 100644 --- a/apps/text_editor.cpp +++ b/apps/text_editor.cpp @@ -108,6 +108,7 @@ void SetLanguage(Instance *instance, uint32_t newLanguage) { instance->syntaxHighlightingLanguage = newLanguage; EsTextboxSetupSyntaxHighlighting(instance->textboxDocument, newLanguage); + EsTextboxEnableSmartQuotes(instance->textboxDocument, !newLanguage); } void FormatPopupCreate(Instance *instance) { diff --git a/desktop/api.cpp b/desktop/api.cpp index 2942e62..8ced5bf 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -87,6 +87,7 @@ struct GlobalData { volatile float uiScale; volatile bool swapLeftAndRightButtons; volatile bool showCursorShadow; + volatile bool useSmartQuotes; volatile bool enableHoverState; }; diff --git a/desktop/os.header b/desktop/os.header index 6805604..a9c72d7 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -2413,6 +2413,7 @@ function void EsTextboxSetTextSize(EsTextbox *textbox, uint16_t size); function void EsTextboxSetFont(EsTextbox *textbox, EsFont font); function void EsTextboxSetupSyntaxHighlighting(EsTextbox *textbox, uint32_t language, uint32_t *customColors = ES_NULL, size_t customColorCount = 0); function void EsTextboxStartEdit(EsTextbox *textbox); +function void EsTextboxEnableSmartQuotes(EsTextbox *textbox, bool enabled); // Sliders. diff --git a/desktop/settings.cpp b/desktop/settings.cpp index 88c3703..0d4530e 100644 --- a/desktop/settings.cpp +++ b/desktop/settings.cpp @@ -138,6 +138,7 @@ void SettingsUpdateGlobalAndWindowManager() { api.global->clickChainTimeoutMs = EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("click_chain_timeout_ms")); api.global->swapLeftAndRightButtons = EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("swap_left_and_right_buttons")); api.global->showCursorShadow = EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("show_cursor_shadow")); + api.global->useSmartQuotes = EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("use_smart_quotes")); api.global->enableHoverState = EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("enable_hover_state")); { @@ -541,6 +542,9 @@ void SettingsPageKeyboard(EsElement *element, SettingsPage *page) { EsTextDisplayCreate(testBox, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, INTERFACE_STRING(DesktopSettingsKeyboardTestTextboxIntroduction)); EsSpacerCreate(testBox, ES_FLAGS_DEFAULT, 0, 0, 5); EsTextboxCreate(testBox, ES_CELL_H_LEFT)->accessKey = 'T'; + + table = EsPanelCreate(container, ES_CELL_H_FILL, &styleSettingsCheckboxGroup); + SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsKeyboardUseSmartQuotes), 'Q', "general", "use_smart_quotes"); } void SettingsPageDisplay(EsElement *element, SettingsPage *page) { diff --git a/desktop/text.cpp b/desktop/text.cpp index 835e534..c74a153 100644 --- a/desktop/text.cpp +++ b/desktop/text.cpp @@ -2919,6 +2919,8 @@ struct EsTextbox : EsElement { uint32_t syntaxHighlightingLanguage; uint32_t syntaxHighlightingColors[8]; + bool smartQuotes; + bool inRightClickDrag; // For smart context menus: @@ -4056,6 +4058,7 @@ bool EsTextboxFind(EsTextbox *textbox, const char *needle, intptr_t _needleBytes EsAssert(byteIndex <= bufferBytes); // Invalid find byte offset. // TODO Case-insensitive search. + // TODO Ignore quotation mark type. if (flags & ES_TEXTBOX_FIND_BACKWARDS) { if (bufferBytes >= needleBytes) { @@ -4482,10 +4485,23 @@ int ProcessTextboxMessage(EsElement *element, EsMessage *message) { int ic, isc; ConvertScancodeToCharacter(message->keyboard.scancode, &ic, &isc, true, textbox->flags & ES_TEXTBOX_MULTILINE); + int character = (message->keyboard.modifiers & ES_MODIFIER_SHIFT) ? isc : ic; if (ic != -1 && (message->keyboard.modifiers & ~ES_MODIFIER_SHIFT) == 0) { + if (textbox->smartQuotes && api.global->useSmartQuotes) { + DocumentLine *currentLine = &textbox->lines[textbox->carets[0].line]; + const char *buffer = currentLine->GetBuffer(textbox); + bool left = !textbox->carets[0].byte || buffer[textbox->carets[0].byte - 1] == ' '; + + if (character == '"') { + character = left ? 0x201C : 0x201D; + } else if (character == '\'') { + character = left ? 0x2018 : 0x2019; + } + } + char buffer[4]; - EsTextboxInsert(textbox, buffer, utf8_encode((message->keyboard.modifiers & ES_MODIFIER_SHIFT) ? isc : ic, buffer)); + EsTextboxInsert(textbox, buffer, utf8_encode(character, buffer)); if (buffer[0] == '\n' && textbox->carets[0].line) { // Copy the indentation from the previous line. @@ -4695,6 +4711,8 @@ EsTextbox *EsTextboxCreate(EsElement *parent, uint64_t flags, const EsStyle *sty textbox->currentStyle->GetTextStyle(&textbox->textStyle); + textbox->smartQuotes = true; + DocumentLine firstLine = {}; firstLine.height = TextGetLineHeight(textbox, &textbox->textStyle); textbox->lines.Add(firstLine); @@ -4934,6 +4952,10 @@ void EsTextboxSetupSyntaxHighlighting(EsTextbox *textbox, uint32_t language, uin textbox->Repaint(true); } +void EsTextboxEnableSmartQuotes(EsTextbox *textbox, bool enabled) { + textbox->smartQuotes = enabled; +} + // --------------------------------- Text displays. // TODO Inline images and icons. diff --git a/res/System Configuration Template.ini b/res/System Configuration Template.ini index 3b050d6..c9afaec 100644 --- a/res/System Configuration Template.ini +++ b/res/System Configuration Template.ini @@ -8,6 +8,7 @@ show_cursor_shadow=1 scroll_lines_per_notch=3 ui_scale=100 window_color=6 +use_smart_quotes=1 enable_hover_state=1 [ui] diff --git a/shared/strings.cpp b/shared/strings.cpp index b018c92..2c88568 100644 --- a/shared/strings.cpp +++ b/shared/strings.cpp @@ -122,6 +122,7 @@ DEFINE_INTERFACE_STRING(DesktopSettingsKeyboardKeyRepeatDelay, "Key repeat delay DEFINE_INTERFACE_STRING(DesktopSettingsKeyboardKeyRepeatRate, "Key repeat rate:"); DEFINE_INTERFACE_STRING(DesktopSettingsKeyboardCaretBlinkRate, "Caret blink rate:"); DEFINE_INTERFACE_STRING(DesktopSettingsKeyboardTestTextboxIntroduction, "Try your settings in the textbox below:"); +DEFINE_INTERFACE_STRING(DesktopSettingsKeyboardUseSmartQuotes, "Use smart quotes when typing"); DEFINE_INTERFACE_STRING(DesktopSettingsMouseDoubleClickSpeed, "Double click time:"); DEFINE_INTERFACE_STRING(DesktopSettingsMouseSpeed, "Cursor movement speed:"); diff --git a/util/api_table.ini b/util/api_table.ini index 13c1e94..b06cfd8 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -456,3 +456,4 @@ _EsUISetFont=454 EsWorkQueue=455 EsWorkIsExiting=456 EsPanelRadioGroupGetChecked=457 +EsTextboxEnableSmartQuotes=458