From 114c03b6e1199cc4a43814f2b7610ea79c06fee8 Mon Sep 17 00:00:00 2001 From: twanvl Date: Sat, 24 Mar 2007 23:42:33 +0000 Subject: [PATCH] improved cursor motion for up/down/home/end; text editor now remembers cursor positions (instead of indices) when applying scripts. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@237 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/data/action/value.cpp | 8 ++++---- src/data/action/value.hpp | 3 ++- src/gui/value/text.cpp | 38 ++++++++++++++++++------------------ src/util/tagged_string.cpp | 40 ++++++++++++++++++++++++++++++++++---- src/util/tagged_string.hpp | 8 +++++--- 5 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/data/action/value.cpp b/src/data/action/value.cpp index d28e6d2e..3f077772 100644 --- a/src/data/action/value.cpp +++ b/src/data/action/value.cpp @@ -130,10 +130,10 @@ TextValueAction* toggle_format_action(const TextValueP& value, const String& tag } } -TextValueAction* typing_action(const TextValueP& value, size_t start, size_t end, const String& replacement, const String& action_name) { +TextValueAction* typing_action(const TextValueP& value, size_t start_i, size_t end_i, size_t start, size_t end, const String& replacement, const String& action_name) { bool reverse = start > end; if (reverse) swap(start, end); - String new_value = tagged_substr_replace(value->value(), start, end, replacement); + String new_value = tagged_substr_replace(value->value(), start_i, end_i, replacement); if (value->value() == new_value) { // no change return nullptr; @@ -141,9 +141,9 @@ TextValueAction* typing_action(const TextValueP& value, size_t start, size_t end // if (name == _("Backspace")) { // // HACK: put start after end if (reverse) { - return new TextValueAction(value, end, start, start+replacement.size(), new_value, action_name); + return new TextValueAction(value, end, start, start+untag(replacement).size(), new_value, action_name); } else { - return new TextValueAction(value, start, end, start+replacement.size(), new_value, action_name); + return new TextValueAction(value, start, end, start+untag(replacement).size(), new_value, action_name); } } } diff --git a/src/data/action/value.hpp b/src/data/action/value.hpp index c3fab93b..c5e0044a 100644 --- a/src/data/action/value.hpp +++ b/src/data/action/value.hpp @@ -75,7 +75,8 @@ class TextValueAction : public ValueAction { TextValueAction* toggle_format_action(const TextValueP& value, const String& tag, size_t start, size_t end, const String& action_name); /// Typing in a TextValue, replace the selection [start...end) with replacement -TextValueAction* typing_action(const TextValueP& value, size_t start, size_t end, const String& replacement, const String& action_name); +/** start and end are cursor positions, start_i and end_i are indices*/ +TextValueAction* typing_action(const TextValueP& value, size_t start_i, size_t end_i, size_t start, size_t end, const String& replacement, const String& action_name); // ----------------------------------------------------------------------------- : Event diff --git a/src/gui/value/text.cpp b/src/gui/value/text.cpp index 4dc0501b..b20660c1 100644 --- a/src/gui/value/text.cpp +++ b/src/gui/value/text.cpp @@ -143,25 +143,25 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { } break; case WXK_UP: - moveSelection(TYPE_INDEX, v.moveLine(selection_end_i, -1), !ev.ShiftDown(), MOVE_LEFT); + moveSelection(TYPE_INDEX, v.moveLine(selection_end_i, -1), !ev.ShiftDown(), MOVE_LEFT_OPT); break; case WXK_DOWN: - moveSelection(TYPE_INDEX, v.moveLine(selection_end_i, +1), !ev.ShiftDown(), MOVE_RIGHT); + moveSelection(TYPE_INDEX, v.moveLine(selection_end_i, +1), !ev.ShiftDown(), MOVE_RIGHT_OPT); break; case WXK_HOME: // move to begining of line / all (if control) if (ev.ControlDown()) { - moveSelection(TYPE_INDEX, 0, !ev.ShiftDown(), MOVE_LEFT); + moveSelection(TYPE_INDEX, 0, !ev.ShiftDown(), MOVE_LEFT_OPT); } else { - moveSelection(TYPE_INDEX, v.lineStart(selection_end_i), !ev.ShiftDown(), MOVE_LEFT); + moveSelection(TYPE_INDEX, v.lineStart(selection_end_i), !ev.ShiftDown(), MOVE_LEFT_OPT); } break; case WXK_END: // move to end of line / all (if control) if (ev.ControlDown()) { - moveSelection(TYPE_INDEX, value().value().size(), !ev.ShiftDown(), MOVE_RIGHT); + moveSelection(TYPE_INDEX, value().value().size(), !ev.ShiftDown(), MOVE_RIGHT_OPT); } else { - moveSelection(TYPE_INDEX, v.lineEnd(selection_end_i), !ev.ShiftDown(), MOVE_RIGHT); + moveSelection(TYPE_INDEX, v.lineEnd(selection_end_i), !ev.ShiftDown(), MOVE_RIGHT_OPT); } break; case WXK_BACK: @@ -306,11 +306,11 @@ void TextValueEditor::onValueChange() { } void TextValueEditor::onAction(const Action& action, bool undone) { - TextValueViewer::onAction(action, undone); + TextValueViewer::onValueChange(); TYPE_CASE(action, TextValueAction) { - selection_start_i = action.selection_start; - selection_end_i = action.selection_end; - fixSelection(TYPE_INDEX); + selection_start = action.selection_start; + selection_end = action.selection_end; + fixSelection(TYPE_CURSOR); } } @@ -475,7 +475,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& fixSelection(); // execute the action before adding it to the stack, // because we want to run scripts before action listeners see the action - ValueAction* action = typing_action(valueP(), selection_start_i, selection_end_i, replacement, name); + ValueAction* action = typing_action(valueP(), selection_start_i, selection_end_i, selection_start, selection_end, replacement, name); if (!action) { // nothing changes, but move the selection anyway moveSelection(TYPE_CURSOR, selection_start); @@ -571,8 +571,8 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) { selection_end = index_to_cursor(value().value(), selection_end_i, dir); } // make sure the selection is at a valid position inside the text - selection_start_i = cursor_to_index(val, selection_start, selection_start == selection_end ? MOVE_MID : MOVE_RIGHT); - selection_end_i = cursor_to_index(val, selection_end, selection_start == selection_end ? MOVE_MID : MOVE_LEFT); + selection_start_i = cursor_to_index(val, selection_start, selection_start == selection_end ? MOVE_MID : MOVE_RIGHT_OPT); + selection_end_i = cursor_to_index(val, selection_end, selection_start == selection_end ? MOVE_MID : MOVE_LEFT_OPT); // start and end must be on the same side of separators size_t seppos = val.find(_(" seppos) { // not on same side, move selection end before sep selection_end = index_to_cursor(val, seppos, dir); - selection_end_i = cursor_to_index(val, selection_end, selection_start == selection_end ? MOVE_MID : MOVE_LEFT); + selection_end_i = cursor_to_index(val, selection_end, selection_start == selection_end ? MOVE_MID : MOVE_LEFT_OPT); } else if (selection_start_i >= sepend && selection_end_i < sepend) { // not on same side, move selection end after sep selection_end = index_to_cursor(val, sepend, dir); - selection_end_i = cursor_to_index(val, selection_end, selection_start == selection_end ? MOVE_MID : MOVE_LEFT); + selection_end_i = cursor_to_index(val, selection_end, selection_start == selection_end ? MOVE_MID : MOVE_LEFT_OPT); } // find next separator seppos = val.find(_(" start + end) return end; // past the middle - else return start; + if (dir < 0 /*MOVE_LEFT*/) return start; + if (dir > 0 /*MOVE_RIGHT*/) return end; + if (pos * 2 > start + end) return end; // past the middle + else return start; } // ----------------------------------------------------------------------------- : Native look / scrollbar diff --git a/src/util/tagged_string.cpp b/src/util/tagged_string.cpp index 64d459ba..7f82beb5 100644 --- a/src/util/tagged_string.cpp +++ b/src/util/tagged_string.cpp @@ -182,14 +182,46 @@ size_t index_to_cursor(const String& str, size_t index, Movement dir) { if (is_substr(str, i, _(" before && index < after) { - // index is inside an atom, determine on which side we want the cursor - if (dir == MOVE_RIGHT) { + // Index is inside an atom, determine on which side we want the cursor + // This is the only place where MOVE_LEFT/RIGHT and MOVE_*_OPT differ + // for the OPT version we must check if we are actually past any real characters + // but, if the atom is empty, it still counts as a single character! + if (dir == MOVE_LEFT) { + return cursor; + } else if (dir == MOVE_RIGHT) { return cursor + 1; } else if (dir == MOVE_MID) { // take the closest side return cursor + ((int)(after - index) < (int)(index - before)); + } else if (dir == MOVE_LEFT_OPT) { + // is there any non-tag after index? + bool empty = true; + while (i < close) { + c = str.GetChar(i); + if (c == _('<')) { + i = skip_tag(str, i); + } else if (i >= index) { + return cursor; // this is a non-tag character after index + } else { + empty = false; + ++i; + } + } + return empty ? cursor : cursor + 1; // still didn't pass any + } else if (dir == MOVE_RIGHT_OPT) { + // is index actually past any non-tag? + while (i < close) { + if (i >= index) { + return cursor; // we didn't pass any non-tag stuff + } + c = str.GetChar(i); + if (c != _('<')) break; + i = skip_tag(str, i); + } + return cursor + 1; // yes it is } } i = after; @@ -260,7 +292,7 @@ size_t cursor_to_index(const String& str, size_t cursor, Movement dir) { } } // This allows formating to be enabled without a selection - return dir == MOVE_RIGHT ? end - 1 : start; + return dir <= 0 /*MOVE_LEFT*/ ? start : end - 1; } // ----------------------------------------------------------------------------- : Untagged position diff --git a/src/util/tagged_string.hpp b/src/util/tagged_string.hpp index 2158191d..20cd879f 100644 --- a/src/util/tagged_string.hpp +++ b/src/util/tagged_string.hpp @@ -90,9 +90,11 @@ String anti_tag(const String& tag); /// Directions of cursor movement enum Movement -{ MOVE_LEFT = -1 ///< Always move the cursor to the left -, MOVE_MID = 0 ///< Move in whichever direction the distance to move is shorter (TODO: define shorter) -, MOVE_RIGHT = 1 ///< Always move the cursor to the right +{ MOVE_LEFT = -2 ///< Always move the cursor to the left +, MOVE_LEFT_OPT = -1 ///< Move the cursor to the left, but a position inside a tag is the same as that before +, MOVE_MID = 0 ///< Move in whichever direction the distance to move is shorter (TODO: define shorter) +, MOVE_RIGHT_OPT = 1 ///< Move the cursor to the left, but a position inside a tag is the same as that after +, MOVE_RIGHT = 2 ///< Always move the cursor to the right }; /// Find the cursor position corresponding to the given character index.