Skip to content

Commit

Permalink
expose command handling in texteditor
Browse files Browse the repository at this point in the history
  • Loading branch information
scheffle committed Dec 25, 2023
1 parent eab6baf commit 769b479
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 46 deletions.
175 changes: 129 additions & 46 deletions vstgui/lib/ctexteditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,16 @@ struct TextEditorView : public CView,
std::string getPlainText () const override;
void resetController () const override;
void setStyle (const Style& style) const override;
bool canHandleCommand (Command cmd) const override;
bool handleCommand (Command cmd) const override;

// commandos
void selectAll ();
bool doCut ();
bool doCopy ();
bool doPaste ();
void useSelectionForFind ();
void doFind (bool forward = true);
bool useSelectionForFind ();
bool doFind (bool forward = true);

private:
template<typename Proc>
Expand Down Expand Up @@ -286,6 +288,7 @@ struct TextEditorView : public CView,
mutable Lines::const_iterator stbInternalIterator;

String findString;
std::array<KeyboardEvent, static_cast<size_t> (Command::UseSelectionForFind) + 1> commandKeys;
};

#define VIRTUAL_KEY_BIT 0x80000000
Expand Down Expand Up @@ -361,13 +364,47 @@ struct LineNumberView : CView,
CView* textEditorView {nullptr};
};

//------------------------------------------------------------------------
void setKeyForCommand (KeyboardEvent& cmd, char16_t character, VirtualKey vKey, Modifiers mod)
{
cmd.character = character;
cmd.virt = vKey;
cmd.modifiers = mod;
}

//------------------------------------------------------------------------
TextEditorView::TextEditorView (ITextEditorController* controller)
: CView ({0, 0, 10, 10}), controller (controller)
{
setWantsFocus (true);
stb_textedit_initialize_state (&editState, false);
setKeyForCommand (commandKeys[static_cast<size_t> (Command::SelectAll)], u'a', VirtualKey::None,
{ModifierKey::Control});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::Cut)], u'x', VirtualKey::None,
{ModifierKey::Control});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::Copy)], u'c', VirtualKey::None,
{ModifierKey::Control});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::Paste)], u'v', VirtualKey::None,
{ModifierKey::Control});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::Undo)], u'z', VirtualKey::None,
{ModifierKey::Control});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::Redo)], u'z', VirtualKey::None,
{ModifierKey::Control, ModifierKey::Shift});
#if MAC
setKeyForCommand (commandKeys[static_cast<size_t> (Command::FindNext)], u'g', VirtualKey::None,
{ModifierKey::Control});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::FindPrevious)], u'g',
VirtualKey::None, {ModifierKey::Control, ModifierKey::Shift});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::UseSelectionForFind)], u'e',
VirtualKey::None, {ModifierKey::Control});
#else
setKeyForCommand (commandKeys[static_cast<size_t> (Command::FindNext)], 0, VirtualKey::F3, {});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::FindPrevious)], 0, VirtualKey::F3,
{ModifierKey::Shift});
setKeyForCommand (commandKeys[static_cast<size_t> (Command::UseSelectionForFind)], 0,
VirtualKey::F3, {ModifierKey::Control});
#endif
controller->onTextEditorCreated (*this);
setWantsFocus (true);
}

//------------------------------------------------------------------------
Expand Down Expand Up @@ -583,6 +620,83 @@ std::string TextEditorView::getPlainText () const { return convert (model.text);
//------------------------------------------------------------------------
void TextEditorView::resetController () const { controller = nullptr; }

//------------------------------------------------------------------------
bool TextEditorView::canHandleCommand (Command cmd) const
{
switch (cmd)
{
case Command::SelectAll:
return true;
case Command::UseSelectionForFind:
[[fallthrough]];
case Command::Cut:
[[fallthrough]];
case Command::Copy:
{
return editState.select_start != editState.select_end;
}
case Command::Paste:
{
if (auto clipboard = getFrame ()->getClipboard ())
{
auto count = clipboard->getCount ();
for (auto i = 0u; i < count; ++i)
{
if (clipboard->getDataType (i) == IDataPackage::kText)
return true;
}
}
return false;
}
case Command::Undo:
{
// TODO:
return true;
}
case Command::Redo:
{
// TODO:
return true;
}
case Command::FindNext:
[[fallthrough]];
case Command::FindPrevious:
{
return !findString.empty ();
}
}
return false;
}

//------------------------------------------------------------------------
bool TextEditorView::handleCommand (Command cmd) const
{
auto This = const_cast<TextEditorView*> (this);
switch (cmd)
{
case Command::SelectAll:
This->selectAll ();
return true;
case Command::Cut:
return This->doCut ();
case Command::Copy:
return This->doCopy ();
case Command::Paste:
return This->doPaste ();
case Command::Undo:
return This->callSTB ([=] () { stb_text_undo (This, &This->editState); });
case Command::Redo:
return This->callSTB ([=] () { stb_text_redo (This, &This->editState); });
case Command::FindNext:
return This->doFind (true);
case Command::FindPrevious:
return This->doFind (false);
case Command::UseSelectionForFind:
return This->useSelectionForFind ();
}
return false;
}

//------------------------------------------------------------------------
inline Range toLineSelection (const Range& line, size_t selStart, size_t selEnd)
{
Expand Down Expand Up @@ -772,49 +886,14 @@ void TextEditorView::onKeyboardEvent (KeyboardEvent& event)
}
});

if (event.modifiers.has (ModifierKey::Control))
for (auto index = 0u; index < commandKeys.size (); ++index)
{
switch (event.character)
const auto& cmd = commandKeys[index];
if (cmd.character == event.character && cmd.virt == event.virt &&
cmd.modifiers == event.modifiers)
{
case 'a':
if (handleCommand (static_cast<Command> (index)))
{
selectAll ();
event.consumed = true;
return;
}
case 'x':
{
if (doCut ())
event.consumed = true;
return;
}
case 'c':
{
if (doCopy ())
event.consumed = true;
return;
}
case 'v':
{
if (doPaste ())
event.consumed = true;
return;
}
case 'e':
{
useSelectionForFind ();
event.consumed = true;
return;
}
case 'g':
{
doFind (true);
event.consumed = true;
return;
}
case 'h':
{
doFind (false);
event.consumed = true;
return;
}
Expand Down Expand Up @@ -1618,19 +1697,21 @@ bool TextEditorView::doPaste ()
}

//------------------------------------------------------------------------
void TextEditorView::useSelectionForFind ()
bool TextEditorView::useSelectionForFind ()
{
if (auto range = makeRange (editState))
{
findString = model.text.substr (range.start, range.length);
return true;
}
return false;
}

//------------------------------------------------------------------------
void TextEditorView::doFind (bool forward)
bool TextEditorView::doFind (bool forward)
{
if (findString.empty ())
return;
return false;
auto pos = String::npos;
if (forward)
{
Expand Down Expand Up @@ -1660,7 +1741,9 @@ void TextEditorView::doFind (bool forward)
editState.select_end = editState.cursor + static_cast<int> (findString.length ());
onSelectionChanged (makeRange (editState));
onCursorChanged (oldCursor, editState.cursor);
return true;
}
return false;
}

//------------------------------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions vstgui/lib/ctexteditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ struct ITextEditor
};

virtual void setStyle (const Style& style) const = 0;

enum class Command
{
SelectAll,
Cut,
Copy,
Paste,
Undo,
Redo,
FindNext,
FindPrevious,
UseSelectionForFind,
};

virtual bool canHandleCommand (Command cmd) const = 0;
virtual bool handleCommand (Command cmd) const = 0;
};

//------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions vstgui/lib/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ struct Modifiers
{
Modifiers () = default;
Modifiers (const Modifiers&) = default;
Modifiers (const std::initializer_list<ModifierKey>& modifiers)
{
for (auto& mod : modifiers)
data |= cast (mod);
}
explicit Modifiers (ModifierKey modifier) : data (cast (modifier)) {}
Modifiers& operator= (const Modifiers&) = default;

Expand Down

0 comments on commit 769b479

Please sign in to comment.