Commit be344ca6 authored by twanvl's avatar twanvl

improved cursor motion for up/down/home/end; text editor now remembers cursor...

improved cursor motion for up/down/home/end; text editor now remembers cursor positions (instead of indices) when applying scripts.
parent 794fa8a2
...@@ -130,10 +130,10 @@ TextValueAction* toggle_format_action(const TextValueP& value, const String& tag ...@@ -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; bool reverse = start > end;
if (reverse) swap(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) { if (value->value() == new_value) {
// no change // no change
return nullptr; return nullptr;
...@@ -141,9 +141,9 @@ TextValueAction* typing_action(const TextValueP& value, size_t start, size_t end ...@@ -141,9 +141,9 @@ TextValueAction* typing_action(const TextValueP& value, size_t start, size_t end
// if (name == _("Backspace")) { // if (name == _("Backspace")) {
// // HACK: put start after end // // HACK: put start after end
if (reverse) { 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 { } 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);
} }
} }
} }
......
...@@ -75,7 +75,8 @@ class TextValueAction : public ValueAction { ...@@ -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); 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 /// 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 // ----------------------------------------------------------------------------- : Event
......
...@@ -143,25 +143,25 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { ...@@ -143,25 +143,25 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
} }
break; break;
case WXK_UP: 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; break;
case WXK_DOWN: 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; break;
case WXK_HOME: case WXK_HOME:
// move to begining of line / all (if control) // move to begining of line / all (if control)
if (ev.ControlDown()) { if (ev.ControlDown()) {
moveSelection(TYPE_INDEX, 0, !ev.ShiftDown(), MOVE_LEFT); moveSelection(TYPE_INDEX, 0, !ev.ShiftDown(), MOVE_LEFT_OPT);
} else { } 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; break;
case WXK_END: case WXK_END:
// move to end of line / all (if control) // move to end of line / all (if control)
if (ev.ControlDown()) { 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 { } 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; break;
case WXK_BACK: case WXK_BACK:
...@@ -306,11 +306,11 @@ void TextValueEditor::onValueChange() { ...@@ -306,11 +306,11 @@ void TextValueEditor::onValueChange() {
} }
void TextValueEditor::onAction(const Action& action, bool undone) { void TextValueEditor::onAction(const Action& action, bool undone) {
TextValueViewer::onAction(action, undone); TextValueViewer::onValueChange();
TYPE_CASE(action, TextValueAction) { TYPE_CASE(action, TextValueAction) {
selection_start_i = action.selection_start; selection_start = action.selection_start;
selection_end_i = action.selection_end; selection_end = action.selection_end;
fixSelection(TYPE_INDEX); fixSelection(TYPE_CURSOR);
} }
} }
...@@ -475,7 +475,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& ...@@ -475,7 +475,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
fixSelection(); fixSelection();
// execute the action before adding it to the stack, // execute the action before adding it to the stack,
// because we want to run scripts before action listeners see the action // 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) { if (!action) {
// nothing changes, but move the selection anyway // nothing changes, but move the selection anyway
moveSelection(TYPE_CURSOR, selection_start); moveSelection(TYPE_CURSOR, selection_start);
...@@ -571,8 +571,8 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) { ...@@ -571,8 +571,8 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) {
selection_end = index_to_cursor(value().value(), selection_end_i, dir); selection_end = index_to_cursor(value().value(), selection_end_i, dir);
} }
// make sure the selection is at a valid position inside the text // 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_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); 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 // start and end must be on the same side of separators
size_t seppos = val.find(_("<sep")); size_t seppos = val.find(_("<sep"));
while (seppos != String::npos) { while (seppos != String::npos) {
...@@ -580,11 +580,11 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) { ...@@ -580,11 +580,11 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) {
if (selection_start_i <= seppos && selection_end_i > seppos) { if (selection_start_i <= seppos && selection_end_i > seppos) {
// not on same side, move selection end before sep // not on same side, move selection end before sep
selection_end = index_to_cursor(val, seppos, dir); 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) { } else if (selection_start_i >= sepend && selection_end_i < sepend) {
// not on same side, move selection end after sep // not on same side, move selection end after sep
selection_end = index_to_cursor(val, sepend, dir); 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 // find next separator
seppos = val.find(_("<sep"), seppos + 1); seppos = val.find(_("<sep"), seppos + 1);
...@@ -622,10 +622,10 @@ void TextValueEditor::select(size_t start, size_t end) { ...@@ -622,10 +622,10 @@ void TextValueEditor::select(size_t start, size_t end) {
} }
size_t TextValueEditor::move(size_t pos, size_t start, size_t end, Movement dir) { size_t TextValueEditor::move(size_t pos, size_t start, size_t end, Movement dir) {
if (dir == MOVE_LEFT) return start; if (dir < 0 /*MOVE_LEFT*/) return start;
if (dir == MOVE_RIGHT) return end; if (dir > 0 /*MOVE_RIGHT*/) return end;
if (pos * 2 > start + end) return end; // past the middle if (pos * 2 > start + end) return end; // past the middle
else return start; else return start;
} }
// ----------------------------------------------------------------------------- : Native look / scrollbar // ----------------------------------------------------------------------------- : Native look / scrollbar
......
...@@ -182,14 +182,46 @@ size_t index_to_cursor(const String& str, size_t index, Movement dir) { ...@@ -182,14 +182,46 @@ size_t index_to_cursor(const String& str, size_t index, Movement dir) {
if (is_substr(str, i, _("<atom")) || is_substr(str, i, _("<sep"))) { if (is_substr(str, i, _("<atom")) || is_substr(str, i, _("<sep"))) {
// skip tag contents, tag counts as a single 'character' // skip tag contents, tag counts as a single 'character'
size_t before = i; size_t before = i;
size_t after = match_close_tag_end(str, i); size_t close = match_close_tag(str, i);
size_t after = skip_tag(str, close);
if (index > before && index < after) { if (index > before && index < after) {
// index is inside an atom, determine on which side we want the cursor // Index is inside an atom, determine on which side we want the cursor
if (dir == MOVE_RIGHT) { // 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; return cursor + 1;
} else if (dir == MOVE_MID) { } else if (dir == MOVE_MID) {
// take the closest side // take the closest side
return cursor + ((int)(after - index) < (int)(index - before)); 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; i = after;
...@@ -260,7 +292,7 @@ size_t cursor_to_index(const String& str, size_t cursor, Movement dir) { ...@@ -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 // 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 // ----------------------------------------------------------------------------- : Untagged position
......
...@@ -90,9 +90,11 @@ String anti_tag(const String& tag); ...@@ -90,9 +90,11 @@ String anti_tag(const String& tag);
/// Directions of cursor movement /// Directions of cursor movement
enum Movement enum Movement
{ MOVE_LEFT = -1 ///< Always move the cursor to the left { MOVE_LEFT = -2 ///< Always move the cursor to the left
, MOVE_MID = 0 ///< Move in whichever direction the distance to move is shorter (TODO: define shorter) , MOVE_LEFT_OPT = -1 ///< Move the cursor to the left, but a position inside a tag is the same as that before
, MOVE_RIGHT = 1 ///< Always move the cursor to the right , 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. /// Find the cursor position corresponding to the given character index.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment