From a6b3ce1351c8f0dd280deae5d3a161eea72c0f08 Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sun, 20 Dec 2020 15:13:03 +0100 Subject: [PATCH] added mouse support to editable widgets added missing hotkeys to launcher context menu updated docs --- Changes.txt | 6 +- docs/index.html | 64 ++++++------- src/debugger/gui/RomListSettings.hxx | 2 + src/gui/ContextMenu.cxx | 6 +- src/gui/EditTextWidget.cxx | 27 ++---- src/gui/EditableWidget.cxx | 135 +++++++++++++++++++++++++++ src/gui/EditableWidget.hxx | 14 ++- src/gui/LauncherDialog.cxx | 36 +++++-- src/gui/PopUpWidget.cxx | 43 ++++----- src/gui/PopUpWidget.hxx | 1 + src/gui/WhatsNewDialog.cxx | 1 + 11 files changed, 245 insertions(+), 90 deletions(-) diff --git a/Changes.txt b/Changes.txt index af5b01a62..013c1a57c 100644 --- a/Changes.txt +++ b/Changes.txt @@ -16,9 +16,11 @@ * Added high scores saving. - * Enhanced cut/copy/paste for text editing. (TODO: PromptWidget) + * Enhanced cut/copy/paste for text editing (except PromptWidget). - * Added undo and redo to text editing. (TODO: PromptWidget) + * Added undo and redo to text editing (except PromptWidget). + + * Added mouse support for text editing (except PromptWidget). * Added wildcard support to launcher dialog filter. diff --git a/docs/index.html b/docs/index.html index 071244afc..23ed6905a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1987,23 +1987,18 @@ Key (macOS) - Load previous game in ROM (multicart ROM, TIA mode) + Load previous game in ROM (multicart ROM) Shift-Control + r Shift-Control + r - Reload current ROM (singlecart ROM, TIA mode)
- Load next game in ROM (multicart ROM, TIA mode) + Reload current ROM (singlecart ROM)
+ Load next game in ROM (multicart ROM) Control + r Control + r - Reload ROM listing (ROM launcher mode) - Control + r - Control + r - - - Emulate 'frying' effect (TIA mode) + Emulate 'frying' effect Backspace Backspace @@ -4007,7 +4002,7 @@

ROM Launcher Context Menu

The ROM launcher also contains a context menu, selected by clicking the - right mouse button anywhere in the current window. This context menu + right mouse button in the ROM list. This context menu contains the following items:

    @@ -4018,31 +4013,32 @@ its functionality, and use ROM properties as defined by the ROM itself. The dialog is as follows (see Advanced Configuration - Game Properties for more information concerning ROM properties):

    - - - - - - -
         - - - - - - - - - - - -
    ItemFor more information,
    see Commandline
    Bankswitch type-bs
    TV type-tv
    Left difficulty-ld
    Right difficulty-rd
    Startup mode-debug
    Left joy items-holdjoy0
    Right joy items-holdjoy1
    Console: Select-holdselect
    Console: Reset-holdreset
    -

    -
    + + + + + + +
         + + + + + + + + + + + +
    ItemFor more information,
    see Commandline
    Bankswitch type-bs
    TV type-tv
    Left difficulty-ld
    Right difficulty-rd
    Startup mode-debug
    Left joy items-holdjoy0
    Right joy items-holdjoy1
    Console: Select-holdselect
    Console: Reset-holdreset
    +

    +
    +

    This dialog can also be opened by pressing 'Control + p'.

    -
  1. High scores: This option displays the +
  2. High scores: This option displays the High Scores dialog for the selected ROM. Only available if high score - properties have been setup for the ROM.
  3. + properties have been setup for the ROM. Also available via 'Control + h' keys combo.
  4. Reload listing: Selecting this performs a reload of the current listing. It is an alternative to pressing the 'Control + r' @@ -4355,7 +4351,7 @@

    • Developer key-combo shortcuts, used to change TIA state dynamically (ie, while the emulation is still running). See Keyboard Layout - - Developer Keys in TIA mode for more information.
    • + Developer Keys for more information.
    • Commandline options influencing emulation state. See Using the Command Line - Developer Commands for more information.
    • diff --git a/src/debugger/gui/RomListSettings.hxx b/src/debugger/gui/RomListSettings.hxx index a589b0334..d22a63d2b 100644 --- a/src/debugger/gui/RomListSettings.hxx +++ b/src/debugger/gui/RomListSettings.hxx @@ -34,6 +34,8 @@ class RomListSettings : public Dialog, public CommandSender RomListSettings(GuiObject* boss, const GUI::Font& font); ~RomListSettings() override = default; + bool isShading() const override { return false; } + /** Show dialog onscreen at the specified coordinates ('data' will be the currently selected line number in RomListWidget) */ void show(uInt32 x, uInt32 y, const Common::Rect& bossRect, int data = -1); diff --git a/src/gui/ContextMenu.cxx b/src/gui/ContextMenu.cxx index 11ccbb082..2ecf50302 100644 --- a/src/gui/ContextMenu.cxx +++ b/src/gui/ContextMenu.cxx @@ -35,8 +35,8 @@ ContextMenu::ContextMenu(GuiObject* boss, const GUI::Font& font, _cmd(cmd), _maxWidth(width) { - addItems(items); setArrows(); + addItems(items); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -48,10 +48,10 @@ void ContextMenu::addItems(const VariantList& items) // Resize to largest string int maxwidth = _maxWidth; for(const auto& e: _entries) - maxwidth = std::max(maxwidth, _font.getStringWidth(e.first)); + maxwidth = std::max(maxwidth, _font.getStringWidth(e.first) + _textOfs * 2 + 2); _x = _y = 0; - _w = maxwidth + PopUpWidget::dropDownWidth(_font); // 23; + _w = maxwidth; _h = 1; // recalculate this in ::recalc() _scrollUpColor = _firstEntry > 0 ? kScrollColor : kColor; diff --git a/src/gui/EditTextWidget.cxx b/src/gui/EditTextWidget.cxx index dde104002..b6d65e84e 100644 --- a/src/gui/EditTextWidget.cxx +++ b/src/gui/EditTextWidget.cxx @@ -27,7 +27,8 @@ EditTextWidget::EditTextWidget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h, const string& text) : EditableWidget(boss, font, x, y, w, h + 2, text) { - _flags = Widget::FLAG_ENABLED | Widget::FLAG_CLEARBG | Widget::FLAG_RETAIN_FOCUS; + _flags = Widget::FLAG_ENABLED | Widget::FLAG_CLEARBG + | Widget::FLAG_RETAIN_FOCUS | Widget::FLAG_TRACK_MOUSE; EditableWidget::startEditMode(); // We're always in edit mode @@ -56,24 +57,16 @@ void EditTextWidget::setText(const string& str, bool changed) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EditTextWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) { - if(!isEditable()) - return; - - resetSelection(); - x += _editScrollOffset; - - int width = 0; - uInt32 i; - - for (i = 0; i < editString().size(); ++i) + if(b == MouseButton::LEFT) { - width += _font.getCharWidth(editString()[i]); - if (width >= x) - break; - } + if(!isEditable()) + return; - if (setCaretPos(i)) - setDirty(); + resetSelection(); + if(setCaretPos(toCaretPos(x))) + setDirty(); + } + EditableWidget::handleMouseDown(x, y, b, clickCount); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/EditableWidget.cxx b/src/gui/EditableWidget.cxx index 837aac3bb..113848fb6 100644 --- a/src/gui/EditableWidget.cxx +++ b/src/gui/EditableWidget.cxx @@ -19,6 +19,7 @@ #include "StellaKeys.hxx" #include "FBSurface.hxx" #include "Font.hxx" +#include "ContextMenu.hxx" #include "OSystem.hxx" #include "EventHandler.hxx" #include "UndoHandler.hxx" @@ -39,6 +40,9 @@ EditableWidget::EditableWidget(GuiObject* boss, const GUI::Font& font, _bgcolorlo = kDlgColor; _textcolor = kTextColor; _textcolorhi = kTextColor; + + // add mouse context menu + myMouseMenu = make_unique(this, font, EmptyVarList); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -120,6 +124,115 @@ void EditableWidget::lostFocusWidget() _selectSize = 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int EditableWidget::toCaretPos(int x) const +{ + int i; + + x += caretOfs(); + for(i = 0; i < _editString.size(); ++i) + { + x -= _font.getCharWidth(_editString[i]); + if(x <= 0) + break; + } + return i; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) +{ + // Grab right mouse button for context menu, send left to base class + if(b == MouseButton::RIGHT && isEnabled() && !myMouseMenu->isVisible()) + { + VariantList items; + #ifndef BSPF_MACOS + if(isEditable()) + VarList::push_back(items, " Cut Ctrl+X ", "cut"); + VarList::push_back(items, " Copy Ctrl+C ", "copy"); + if(isEditable()) + VarList::push_back(items, " Paste Ctrl+V ", "paste"); + #else + if(isEditable()) + VarList::push_back(items, " Cut Cmd+X ", "cut"); + VarList::push_back(items, " Copy Cmd+C ", "copy"); + if(isEditable()) + VarList::push_back(items, " Paste Cmd+V ", "paste"); + #endif + myMouseMenu->addItems(items); + + // Add menu at current x,y mouse location + myMouseMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect()); + return; + } + else if(b == MouseButton::LEFT && isEnabled()) + { + _isDragging = true; + + if(clickCount == 2) + { + // If left mouse button is double clicked, mark word under cursor + markWord(); + return; + } + } + Widget::handleMouseDown(x, y, b, clickCount); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) +{ + _isDragging = false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::handleMouseMoved(int x, int y) +{ + if(isEditable() && _isDragging) + { + int deltaPos = toCaretPos(x) - _caretPos; + + if(deltaPos) + { + moveCaretPos(deltaPos); + setDirty(); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) +{ + if(cmd == ContextMenu::kItemSelectedCmd) + { + const string& rmb = myMouseMenu->getSelectedTag().toString(); + + if(rmb == "cut") + { + if(cutSelectedText()) + sendCommand(EditableWidget::kChangedCmd, 0, _id); + } + else if(rmb == "copy") + { + if(!isEditable()) + { + // Copy everything if widget is not editable + _caretPos = 0; + _selectSize = int(_editString.length()); + } + copySelectedText(); + } + else if(rmb == "paste") + { + if(pasteSelectedText()) + sendCommand(EditableWidget::kChangedCmd, 0, _id); + } + setDirty(); + } + else + Widget::handleCommand(sender, cmd, data, id); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EditableWidget::tryInsertChar(char c, int pos) { @@ -634,6 +747,28 @@ bool EditableWidget::moveWord(int direction, bool select) return handled; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::markWord() +{ + _selectSize = 0; + + while(_caretPos + _selectSize < int(_editString.size())) + { + if(_editString[_caretPos + _selectSize] == ' ') + break; + _selectSize++; + } + + while(_caretPos > 0) + { + if(_editString[_caretPos - 1] == ' ') + break; + _caretPos--; + _selectSize++; + } + return _selectSize > 0; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const string EditableWidget::selectString() const { diff --git a/src/gui/EditableWidget.hxx b/src/gui/EditableWidget.hxx index 553d35522..f6a44069a 100644 --- a/src/gui/EditableWidget.hxx +++ b/src/gui/EditableWidget.hxx @@ -22,6 +22,7 @@ #include "Widget.hxx" #include "Rect.hxx" +#include "ContextMenu.hxx" #include "UndoHandler.hxx" /** @@ -66,6 +67,13 @@ class EditableWidget : public Widget, public CommandSender void setTextFilter(const TextFilter& filter) { _filter = filter; } protected: + void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; + void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; + void handleMouseMoved(int x, int y) override; + void handleCommand(CommandSender* sender, int cmd, int data, int id); + virtual int caretOfs() const { return _editScrollOffset; } + int toCaretPos(int x) const; + void receivedFocusWidget() override; void lostFocusWidget() override; void tick() override; @@ -95,6 +103,7 @@ class EditableWidget : public Widget, public CommandSender bool killLine(int direction); bool killWord(int direction); bool moveWord(int direction, bool select); + bool markWord(); bool killSelectedText(bool addEdit = true); int selectStartPos(); @@ -109,9 +118,12 @@ class EditableWidget : public Widget, public CommandSender bool tryInsertChar(char c, int pos); private: + unique_ptr myMouseMenu; + bool _isDragging{false}; + bool _editable{true}; string _editString; - int _maxLen{0}; + int _maxLen{0}; unique_ptr myUndoHandler; int _caretPos{0}; diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 13f89e624..e05ca3efc 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -634,9 +634,28 @@ void LauncherDialog::showOnlyROMs(bool state) void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated) { // Grab the key before passing it to the actual dialog and check for - // Control-R (reload ROM listing) - if(StellaModTest::isControl(mod) && key == KBDK_R) - reload(); + // context menu keys + if(StellaModTest::isControl(mod)) + { + switch(key) + { + case KBDK_P: + myGlobalProps->open(); + break; + + case KBDK_H: + if(instance().highScores().enabled()) + openHighScores(); + break; + + case KBDK_R: + reload(); + break; + + default: + break; + } + } else #if defined(RETRON77) // handle keys used by R77 @@ -725,21 +744,22 @@ Event::Type LauncherDialog::getJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir void LauncherDialog::handleMouseDown(int x, int y, MouseButton b, int clickCount) { // Grab right mouse button for context menu, send left to base class - if(b == MouseButton::RIGHT) + if(b == MouseButton::RIGHT + && x + getAbsX() >= myList->getLeft() && x + getAbsX() <= myList->getRight() + && y + getAbsY() >= myList->getTop() && y + getAbsY() <= myList->getBottom()) { // Dynamically create context menu for ROM list options VariantList items; if(!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode())) - VarList::push_back(items, "Power-on options" + ELLIPSIS, "override"); + VarList::push_back(items, " Power-on options" + ELLIPSIS + " Ctrl+P", "override"); if(instance().highScores().enabled()) - VarList::push_back(items, "High scores" + ELLIPSIS, "highscores"); - VarList::push_back(items, "Reload listing", "reload"); + VarList::push_back(items, " High scores" + ELLIPSIS + " Ctrl+H", "highscores"); + VarList::push_back(items, " Reload listing Ctrl+R ", "reload"); myMenu->addItems(items); // Add menu at current x,y mouse location myMenu->show(x + getAbsX(), y + getAbsY(), surface().dstRect()); - } else Dialog::handleMouseDown(x, y, b, clickCount); diff --git a/src/gui/PopUpWidget.cxx b/src/gui/PopUpWidget.cxx index b4dd27b84..ab9560ed8 100644 --- a/src/gui/PopUpWidget.cxx +++ b/src/gui/PopUpWidget.cxx @@ -32,7 +32,8 @@ PopUpWidget::PopUpWidget(GuiObject* boss, const GUI::Font& font, _label(label), _labelWidth(labelWidth) { - _flags = Widget::FLAG_ENABLED | Widget::FLAG_RETAIN_FOCUS; + _flags = Widget::FLAG_ENABLED | Widget::FLAG_RETAIN_FOCUS + | Widget::FLAG_TRACK_MOUSE; _bgcolor = kDlgColor; _bgcolorhi = kDlgColor; // do not highlight the background _textcolor = kTextColor; @@ -51,7 +52,7 @@ PopUpWidget::PopUpWidget(GuiObject* boss, const GUI::Font& font, myTextY = (_h - _font.getFontHeight()) / 2; myArrowsY = (_h - _arrowHeight) / 2; - myMenu = make_unique(this, font, list, cmd, w); + myMenu = make_unique(this, font, list, cmd, w + dropDownWidth(font)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -127,33 +128,25 @@ const Variant& PopUpWidget::getSelectedTag() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PopUpWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) { - resetSelection(); - if(!isEditable() || x > _w - dropDownWidth(_font)) + if(b == MouseButton::LEFT) { - if(isEnabled() && !myMenu->isVisible()) + resetSelection(); + if(!isEditable() || x > _w - dropDownWidth(_font)) { - // Add menu just underneath parent widget - myMenu->show(getAbsX() + _labelWidth, getAbsY() + getHeight(), - dialog().surface().dstRect(), myMenu->getSelected()); + if(isEnabled() && !myMenu->isVisible()) + { + // Add menu just underneath parent widget + myMenu->show(getAbsX() + _labelWidth, getAbsY() + getHeight(), + dialog().surface().dstRect(), myMenu->getSelected()); + } + } + else + { + if(setCaretPos(toCaretPos(x))) + setDirty(); } } - else - { - x += _editScrollOffset - _labelWidth; - - int width = 0; - uInt32 i; - - for(i = 0; i < editString().size(); ++i) - { - width += _font.getCharWidth(editString()[i]); - if(width >= x) - break; - } - - if(setCaretPos(i)) - setDirty(); - } + EditableWidget::handleMouseDown(x, y, b, clickCount); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/PopUpWidget.hxx b/src/gui/PopUpWidget.hxx index babe28686..0128cf1d9 100644 --- a/src/gui/PopUpWidget.hxx +++ b/src/gui/PopUpWidget.hxx @@ -72,6 +72,7 @@ class PopUpWidget : public EditableWidget void handleMouseWheel(int x, int y, int direction) override; bool handleEvent(Event::Type e) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + int caretOfs() const override { return _editScrollOffset - _labelWidth; } void setArrow(); void drawWidget(bool hilite) override; diff --git a/src/gui/WhatsNewDialog.cxx b/src/gui/WhatsNewDialog.cxx index c366bc41d..45c110bca 100644 --- a/src/gui/WhatsNewDialog.cxx +++ b/src/gui/WhatsNewDialog.cxx @@ -49,6 +49,7 @@ WhatsNewDialog::WhatsNewDialog(OSystem& osystem, DialogContainer& parent, const #else add(ypos, "added high scores saving"); add(ypos, "enhanced cut/copy/paste and undo/redo for text editing"); + add(ypos, "added mouse support for text editing"); add(ypos, "added wildcard support to launcher dialog filter"); add(ypos, "added option to search subdirectories in launcher"); add(ypos, "added tooltips to many UI items");