From db917c2b71a32b6ebac2f9a4dc5bc5d525c4935e Mon Sep 17 00:00:00 2001 From: twanvl Date: Sun, 10 Dec 2006 20:51:16 +0000 Subject: [PATCH] correct cursor movement accross lines & zero width things git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@108 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/gui/value/text.cpp | 37 ++++++++++++++++++++++++++++++++++++- src/gui/value/text.hpp | 1 + src/render/text/viewer.cpp | 28 +++++++++++++++++++--------- src/render/text/viewer.hpp | 2 ++ src/util/real_point.hpp | 11 +++++++++-- src/util/vector2d.hpp | 10 ++++++---- 6 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/gui/value/text.cpp b/src/gui/value/text.cpp index 3d75402b..1e88dc16 100644 --- a/src/gui/value/text.cpp +++ b/src/gui/value/text.cpp @@ -55,6 +55,7 @@ END_EVENT_TABLE () IMPLEMENT_VALUE_EDITOR(Text) , selection_start(0), selection_end(0) + , select_words(false) , scrollbar(nullptr) {} @@ -65,6 +66,7 @@ TextValueEditor::~TextValueEditor() { // ----------------------------------------------------------------------------- : Mouse void TextValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { + select_words = false; moveSelection(v.indexAt(style().getRotation().trInv(pos)), !ev.ShiftDown(), MOVE_MID); } void TextValueEditor::onLeftUp(const RealPoint& pos, wxMouseEvent&) { @@ -73,11 +75,18 @@ void TextValueEditor::onLeftUp(const RealPoint& pos, wxMouseEvent&) { void TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) { if (ev.LeftIsDown()) { - moveSelection(v.indexAt(style().getRotation().trInv(pos)), false, MOVE_MID); + size_t index = v.indexAt(style().getRotation().trInv(pos)); + if (select_words) { + // TODO: on the left, swap start and end + moveSelection(index < selection_start ? prevWordBoundry(index) : nextWordBoundry(index), false, MOVE_MID); + } else { + moveSelection(index, false, MOVE_MID); + } } } void TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) { + select_words = true; size_t index = v.indexAt(style().getRotation().trInv(pos)); moveSelection(prevWordBoundry(index), true, MOVE_MID); moveSelection(nextWordBoundry(index), false, MOVE_MID); @@ -216,6 +225,24 @@ void TextValueEditor::onMenu(wxCommandEvent& ev) { void TextValueEditor::draw(RotatedDC& dc) { TextValueViewer::draw(dc); v.drawSelection(dc, style(), selection_start, selection_end); + // DEBUG, TODO: REMOVEME + Rotater r(dc, style().getRotation()); + /*dc.SetPen(*wxRED_PEN); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetTextForeground(*wxGREEN); + dc.SetFont(wxFont(6,wxFONTFAMILY_SWISS,wxNORMAL,wxNORMAL)); + for (size_t i = 0 ; i < value().value().size() ; i += 10) { + RealRect r = v.charRect(i); + r.width = max(r.width,1.); + dc.DrawRectangle(r); + dc.DrawText(String()<<(int)i, r.position()+RealSize(1,5)); + }*/ + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetTextForeground(*wxBLUE); + dc.SetFont(wxFont(6,wxFONTFAMILY_SWISS,wxNORMAL,wxNORMAL)); + dc.DrawRectangle(RealRect(style().width-50,style().height-10,50,10)); + dc.DrawText(String::Format(_("%d - %d"),selection_start, selection_end), RealPoint(style().width-50,style().height-10)); } wxCursor rotated_ibeam; @@ -473,6 +500,14 @@ void TextValueEditor::moveSelection(size_t new_end, bool also_move_start, Moveme v.drawSelection(rdc, style(), selection_start, selection_end); // } showCaret(); + // TODO; DEBUG!! + Rotater r(rdc, style().getRotation()); + rdc.SetPen(*wxTRANSPARENT_PEN); + rdc.SetBrush(*wxWHITE_BRUSH); + rdc.SetTextForeground(*wxBLUE); + rdc.SetFont(wxFont(6,wxFONTFAMILY_SWISS,wxNORMAL,wxNORMAL)); + rdc.DrawRectangle(RealRect(style().width-50,style().height-10,50,10)); + rdc.DrawText(String::Format(_("%d - %d"),selection_start, selection_end), RealPoint(style().width-50,style().height-10)); } void TextValueEditor::moveSelectionNoRedraw(size_t new_end, bool also_move_start, Movement dir) { diff --git a/src/gui/value/text.hpp b/src/gui/value/text.hpp index d31fc5bb..ab5e0849 100644 --- a/src/gui/value/text.hpp +++ b/src/gui/value/text.hpp @@ -85,6 +85,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { private: size_t selection_start, selection_end; ///< Cursor position/selection (if any) TextValueEditorScrollBar* scrollbar; ///< Scrollbar for multiline fields in native look + bool select_words; ///< Select whole words when dragging the mouse? // --------------------------------------------------- : Selection / movement diff --git a/src/render/text/viewer.cpp b/src/render/text/viewer.cpp index 7966297f..173b468e 100644 --- a/src/render/text/viewer.cpp +++ b/src/render/text/viewer.cpp @@ -49,6 +49,7 @@ size_t TextViewer::Line::posToIndex(double x) const { // largest index with pos <= x vector::const_iterator it2 = lower_bound(positions.begin(), positions.end(), x); if (it2 == positions.begin()) return start; + if (it2 == positions.end()) --it2; // we don't want to find the position beyond the end // first index with pos > x vector::const_iterator it1 = it2 - 1; if (x - *it1 <= *it2 - x) return it1 - positions.begin() + start; // it1 is closer @@ -95,8 +96,8 @@ void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel void TextViewer::Line::drawSelection(RotatedDC& dc, size_t sel_start, size_t sel_end) { if (!visible(dc)) return; if (sel_start < end() && sel_end > start) { - double x1 = positions[sel_start - start]; - double x2 = positions[min(end(), sel_end) - start]; + double x1 = positions[max(start, sel_start) - start]; + double x2 = positions[min(end(), sel_end) - start]; dc.DrawRectangle(RealRect(x1, top, x2 - x1, line_height)); } } @@ -110,7 +111,7 @@ void TextViewer::reset() { const TextViewer::Line& TextViewer::findLine(size_t index) const { FOR_EACH_CONST(l, lines) { - if (l.end() > index) return l; + if (l.end() >= index) return l; } return lines.front(); } @@ -176,13 +177,22 @@ bool TextViewer::isVisible(size_t index) const { } size_t TextViewer::firstVisibleChar(size_t index, int delta) const { if (lines.empty()) return index; - const Line& l = findLine(index); - int pos = (int)(index - l.start); - while (pos + delta > 0 && (size_t)pos + delta + 1 < l.positions.size()) { - if (l.positions[pos + 1] - l.positions[pos] > 0.0001) break; - pos += delta; + const Line* l = &findLine(index); + while (true) { + int pos = (int)(index - l->start); + while (index == l->end() || (pos + delta >= 0 && (size_t)pos + delta < l->positions.size())) { + if (index == l->end() || l->positions[pos + 1] - l->positions[pos] > 0.0001) { + return index; + } + pos += delta; + index += delta; + } + // move to another line, if not at start/end + if (l + delta < &lines.front()) return 0; + if (l + delta > &lines.back()) return l->end(); + index += delta; + l += delta; } - return pos + l.start; } double TextViewer::heightOfLastLine() const { diff --git a/src/render/text/viewer.hpp b/src/render/text/viewer.hpp index d8492276..66844b21 100644 --- a/src/render/text/viewer.hpp +++ b/src/render/text/viewer.hpp @@ -79,6 +79,8 @@ class TextViewer { /// Is the character at the given index visible? bool isVisible(size_t index) const; /// Find the first character index that is at/before/after the given index, and which has a nonzero width + /** More precisely: it returns a position so that no character after it has zero width + */ size_t firstVisibleChar(size_t index, int delta) const; /// Return the height of the last line diff --git a/src/util/real_point.hpp b/src/util/real_point.hpp index e4b5b736..426c761e 100644 --- a/src/util/real_point.hpp +++ b/src/util/real_point.hpp @@ -79,7 +79,7 @@ class RealSize { /// Can be converted to a wxSize, with integer components inline operator wxSize() { - return wxSize(realRound(width), realRound(height)); + return wxSize(to_int(width), to_int(height)); } }; @@ -161,7 +161,14 @@ class RealRect : private RealPoint, private RealSize { } inline operator wxRect() const { - return wxRect(x, y, width, height); + // Prevent rounding errors, for example if + // x = 0.6 and width = 0.6 + // the right = 1.2 + // so we want a rectangle from 0 to 1 + // not from 0 to 0 + int i_l = to_int(x), i_r = to_int(right()); + int i_t = to_int(y), i_b = to_int(bottom()); + return wxRect(i_l, i_t, i_r - i_l, i_b - i_t); } }; diff --git a/src/util/vector2d.hpp b/src/util/vector2d.hpp index 85a38d00..c0c2102d 100644 --- a/src/util/vector2d.hpp +++ b/src/util/vector2d.hpp @@ -14,9 +14,11 @@ // ----------------------------------------------------------------------------- : Rounding -// Rounding function for converting doubles to integers -inline int realRound(double d) { - return d > 0 ? d + 0.5 : d - 0.5; +/// Rounding function for converting doubles to integers, +/** Intentionally uses slightly less then 0.5, to give a more consistent result + * when for instance something like "x/2" is used. */ +inline int to_int(double d) { + return d > 0 ? d + 0.4999995 : d - 0.4999995; } // ----------------------------------------------------------------------------- : Vector2D @@ -110,7 +112,7 @@ class Vector2D { } inline operator wxPoint() const { - return wxPoint(realRound(x), realRound(y)); + return wxPoint(to_int(x), to_int(y)); } // Vector at infinity