mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 13:06:59 -04:00
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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+19
-19
@@ -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(_("<sep"));
|
||||
while (seppos != String::npos) {
|
||||
@@ -580,11 +580,11 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) {
|
||||
if (selection_start_i <= seppos && selection_end_i > 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(_("<sep"), seppos + 1);
|
||||
@@ -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) {
|
||||
if (dir == MOVE_LEFT) return start;
|
||||
if (dir == MOVE_RIGHT) return end;
|
||||
if (pos * 2 > 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
|
||||
|
||||
@@ -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"))) {
|
||||
// skip tag contents, tag counts as a single 'character'
|
||||
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) {
|
||||
// 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user