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 @@
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):
-
-
-  |
- |
-
-
- Item | For 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 |
-
-
- |
-
-
+
+
+  |
+ |
+
+
+ Item | For 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'.
-
High scores: This option displays the
+ 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.
+ properties have been setup for the ROM. Also available via 'Control + h' keys combo.
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");