Commit 321362d6 authored by twanvl's avatar twanvl

scrollbar in text editor

parent fecb675a
...@@ -26,12 +26,6 @@ Rotation TextCtrl::getRotation() const { ...@@ -26,12 +26,6 @@ Rotation TextCtrl::getRotation() const {
} }
void TextCtrl::draw(DC& dc) { void TextCtrl::draw(DC& dc) {
if (!viewers.empty()) {
wxSize cs = GetClientSize();
Style& style = *viewers.front()->getStyle();
style.width = cs.GetWidth() - 2;
style.height = cs.GetHeight() - 2;
}
RotatedDC rdc(dc, getRotation(), false); RotatedDC rdc(dc, getRotation(), false);
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
} }
...@@ -60,8 +54,11 @@ void TextCtrl::setValue(String* value) { ...@@ -60,8 +54,11 @@ void TextCtrl::setValue(String* value) {
IndexMap<FieldP,ValueP> values; values.add(field, value); IndexMap<FieldP,ValueP> values; values.add(field, value);
setStyles(set->stylesheet, styles); setStyles(set->stylesheet, styles);
setData(values); setData(values);
// determine required height // determine size
viewers.front()->getEditor()->determineSize(); wxSize cs = GetClientSize();
style->width = cs.GetWidth() - 2;
style->height = cs.GetHeight() - 2;
viewers.front()->getEditor()->determineSize(true);
SetMinSize(wxSize(style->width + 6, style->height + 6)); SetMinSize(wxSize(style->width + 6, style->height + 6));
} }
valueChanged(); valueChanged();
...@@ -97,7 +94,14 @@ void TextCtrl::onInit() { ...@@ -97,7 +94,14 @@ void TextCtrl::onInit() {
} }
void TextCtrl::onSize(wxSizeEvent&) { void TextCtrl::onSize(wxSizeEvent&) {
Refresh(false); if (!viewers.empty()) {
wxSize cs = GetClientSize();
Style& style = *viewers.front()->getStyle();
style.width = cs.GetWidth() - 2;
style.height = cs.GetHeight() - 2;
viewers.front()->getEditor()->determineSize(true);
}
onChange();
} }
BEGIN_EVENT_TABLE(TextCtrl, DataEditor) BEGIN_EVENT_TABLE(TextCtrl, DataEditor)
......
...@@ -108,7 +108,7 @@ void ChoiceValueEditor::draw(RotatedDC& dc) { ...@@ -108,7 +108,7 @@ void ChoiceValueEditor::draw(RotatedDC& dc) {
draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown()); draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown());
} }
} }
void ChoiceValueEditor::determineSize() { void ChoiceValueEditor::determineSize(bool) {
style().height = max(style().height(), 16.); style().height = max(style().height(), 16.);
} }
......
...@@ -29,7 +29,7 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor { ...@@ -29,7 +29,7 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor {
virtual void onLoseFocus(); virtual void onLoseFocus();
virtual void draw(RotatedDC& dc); virtual void draw(RotatedDC& dc);
virtual void determineSize(); virtual void determineSize(bool);
private: private:
DropDownListP drop_down; DropDownListP drop_down;
......
...@@ -144,7 +144,7 @@ void ColorValueEditor::draw(RotatedDC& dc) { ...@@ -144,7 +144,7 @@ void ColorValueEditor::draw(RotatedDC& dc) {
draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown()); draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown());
} }
} }
void ColorValueEditor::determineSize() { void ColorValueEditor::determineSize(bool) {
style().height = 20; style().height = 20;
} }
......
...@@ -28,7 +28,7 @@ class ColorValueEditor : public ColorValueViewer, public ValueEditor { ...@@ -28,7 +28,7 @@ class ColorValueEditor : public ColorValueViewer, public ValueEditor {
virtual void onLoseFocus(); virtual void onLoseFocus();
virtual void draw(RotatedDC& dc); virtual void draw(RotatedDC& dc);
virtual void determineSize(); virtual void determineSize(bool);
private: private:
DropDownListP drop_down; DropDownListP drop_down;
......
...@@ -96,7 +96,7 @@ class ValueEditor { ...@@ -96,7 +96,7 @@ class ValueEditor {
/// The cursor type to use when the mouse is over this control /// The cursor type to use when the mouse is over this control
virtual wxCursor cursor() const { return wxCursor(); } virtual wxCursor cursor() const { return wxCursor(); }
/// determines prefered size in the native look, update the style /// determines prefered size in the native look, update the style
virtual void determineSize() {} virtual void determineSize(bool force_fit = false) {}
/// The editor is shown or hidden /// The editor is shown or hidden
virtual void onShow(bool) {} virtual void onShow(bool) {}
}; };
......
...@@ -19,6 +19,6 @@ void SymbolValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent&) { ...@@ -19,6 +19,6 @@ void SymbolValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent&) {
wnd->Show(); wnd->Show();
} }
void SymbolValueEditor::determineSize() { void SymbolValueEditor::determineSize(bool) {
style().height = 50; style().height = 50;
} }
...@@ -21,7 +21,7 @@ class SymbolValueEditor : public SymbolValueViewer, public ValueEditor { ...@@ -21,7 +21,7 @@ class SymbolValueEditor : public SymbolValueViewer, public ValueEditor {
DECLARE_VALUE_EDITOR(Symbol); DECLARE_VALUE_EDITOR(Symbol);
virtual void onLeftDClick(const RealPoint& pos, wxMouseEvent&); virtual void onLeftDClick(const RealPoint& pos, wxMouseEvent&);
virtual void determineSize(); virtual void determineSize(bool);
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -29,9 +29,9 @@ class TextValueEditorScrollBar : public wxWindow { ...@@ -29,9 +29,9 @@ class TextValueEditorScrollBar : public wxWindow {
}; };
TextValueEditorScrollBar::TextValueEditorScrollBar(TextValueEditor& te) TextValueEditorScrollBar::TextValueEditorScrollBar(TextValueEditor& tve)
: wxWindow(&tve.editor(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxVSCROLL | wxALWAYS_SHOW_SB) : wxWindow(&tve.editor(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxVSCROLL | wxALWAYS_SHOW_SB)
, tve(te) , tve(tve)
{} {}
void TextValueEditorScrollBar::onScroll(wxScrollWinEvent& ev) { void TextValueEditorScrollBar::onScroll(wxScrollWinEvent& ev) {
...@@ -57,8 +57,12 @@ IMPLEMENT_VALUE_EDITOR(Text) ...@@ -57,8 +57,12 @@ IMPLEMENT_VALUE_EDITOR(Text)
, selection_start (0), selection_end (0) , selection_start (0), selection_end (0)
, selection_start_i(0), selection_end_i(0) , selection_start_i(0), selection_end_i(0)
, select_words(false) , select_words(false)
, scrollbar(nullptr) , scrollbar(nullptr), scroll_with_cursor(false)
{} {
if (viewer.nativeLook() && field().multi_line) {
scrollbar = new TextValueEditorScrollBar(*this);
}
}
TextValueEditor::~TextValueEditor() { TextValueEditor::~TextValueEditor() {
delete scrollbar; delete scrollbar;
...@@ -234,10 +238,13 @@ void TextValueEditor::onMenu(wxCommandEvent& ev) { ...@@ -234,10 +238,13 @@ void TextValueEditor::onMenu(wxCommandEvent& ev) {
// ----------------------------------------------------------------------------- : Other overrides // ----------------------------------------------------------------------------- : Other overrides
void TextValueEditor::draw(RotatedDC& dc) { void TextValueEditor::draw(RotatedDC& dc) {
// update scrollbar
prepareDrawScrollbar(dc);
// draw text
TextValueViewer::draw(dc); TextValueViewer::draw(dc);
// draw selection
if (isCurrent()) { if (isCurrent()) {
v.drawSelection(dc, style(), selection_start_i, selection_end_i); v.drawSelection(dc, style(), selection_start_i, selection_end_i);
// show caret, onAction() would be a better place // show caret, onAction() would be a better place
// but it has to be done after the viewer has updated the TextViewer // but it has to be done after the viewer has updated the TextViewer
// we could do that ourselfs, but we need a dc for that // we could do that ourselfs, but we need a dc for that
...@@ -469,7 +476,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& ...@@ -469,7 +476,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
} }
fixSelection(TYPE_INDEX, MOVE_MID); fixSelection(TYPE_INDEX, MOVE_MID);
// scroll with next update // scroll with next update
// scrollWithCursor = true; scroll_with_cursor = true;
} }
void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_start, Movement dir) { void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_start, Movement dir) {
...@@ -495,15 +502,15 @@ void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_ ...@@ -495,15 +502,15 @@ void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_
// move // move
moveSelectionNoRedraw(t, new_end, also_move_start, dir); moveSelectionNoRedraw(t, new_end, also_move_start, dir);
// scroll? // scroll?
// scrollWithCursor = true; scroll_with_cursor = true;
// if (onMove()) { if (ensureCaretVisible()) {
// // we can't redraw just the selection because we must scroll // we can't redraw just the selection because we must scroll
// updateScrollbar(); updateScrollbar();
// editor.refreshEditor(); // editor.refreshEditor();
// } else { } else {
// draw new selection // draw new selection
v.drawSelection(rdc, style(), selection_start_i, selection_end_i); v.drawSelection(rdc, style(), selection_start_i, selection_end_i);
// } }
} }
showCaret(); showCaret();
} }
...@@ -545,46 +552,6 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) { ...@@ -545,46 +552,6 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) {
// find next separator // find next separator
seppos = val.find(_("<sep"), seppos + 1); seppos = val.find(_("<sep"), seppos + 1);
} }
// REMOVEME
/*size_t size = val.size();
selection_end = min(size, selection_end);
selection_start = min(size, selection_start);
// start and end must not be inside or between tags
selection_start = v.firstVisibleChar(selection_start, dir == MOVE_LEFT ? -1 : +1);
selection_end = v.firstVisibleChar(selection_end, dir == MOVE_LEFT ? -1 : +1);
// start and end must be on the same side of separators
size_t seppos = val.find(_("<sep"));
while (seppos != String::npos) {
size_t sepend = skip_tag(val,match_close_tag(val, seppos));
if (selection_start <= seppos && selection_end > seppos) selection_end = seppos; // not on same side
if (selection_start >= sepend && selection_end < sepend) selection_end = sepend; // not on same side
if (selection_start > seppos && selection_start < sepend) {
// start inside separator
selection_start = move(selection_start, seppos, sepend, dir);
}
if (selection_end > seppos && selection_end < sepend) {
// end inside separator
selection_end = selection_start < sepend ? seppos : sepend;
}
// find next separator
seppos = val.find(_("<sep"), seppos + 1);
}
// start or end in an <atom>? if so, move them out
size_t atompos = val.find(_("<atom"));
while (atompos != String::npos) {
size_t atomend = skip_tag(val,match_close_tag(val, atompos));
if (selection_start > atompos && selection_start < atomend) { // start inside atom
selection_start = move(selection_start, atompos, atomend, dir);
}
if (selection_end > atompos && selection_end < atomend) { // end inside atom
selection_end = move(selection_end, atompos, atomend, dir);
}
// find next atom
atompos = val.find(_("<atom"), atompos + 1);
}
*/
// TODO? : More checks?
} }
...@@ -626,12 +593,12 @@ size_t TextValueEditor::move(size_t pos, size_t start, size_t end, Movement dir) ...@@ -626,12 +593,12 @@ size_t TextValueEditor::move(size_t pos, size_t start, size_t end, Movement dir)
// ----------------------------------------------------------------------------- : Native look / scrollbar // ----------------------------------------------------------------------------- : Native look / scrollbar
void TextValueEditor::determineSize() { void TextValueEditor::determineSize(bool force_fit) {
if (!nativeLook()) return; if (!nativeLook()) return;
style().angle = 0; // no rotation in nativeLook style().angle = 0; // no rotation in nativeLook
if (scrollbar) { if (scrollbar) {
// muliline, determine scrollbar size // muliline, determine scrollbar size
style().height = 100; if (!force_fit) style().height = 100;
int sbw = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); int sbw = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
scrollbar->SetSize( scrollbar->SetSize(
style().left + style().width - sbw + 1, style().left + style().width - sbw + 1,
...@@ -667,8 +634,47 @@ void TextValueEditor::onMouseWheel(const RealPoint& pos, wxMouseEvent& ev) { ...@@ -667,8 +634,47 @@ void TextValueEditor::onMouseWheel(const RealPoint& pos, wxMouseEvent& ev) {
void TextValueEditor::scrollTo(int pos) { void TextValueEditor::scrollTo(int pos) {
// scroll // scroll
// r.scrollTo(pos); v.scrollTo(pos);
// move the cursor if needed // move the cursor if needed
// refresh // refresh
// editor.refreshEditor(); // viewer.onChange();
}
bool TextValueEditor::ensureCaretVisible() {
if (scrollbar && scroll_with_cursor) {
scroll_with_cursor = false;
return v.ensureVisible(style().height - style().padding_top - style().padding_bottom, selection_end_i);
}
return false;
}
void TextValueEditor::updateScrollbar() {
assert(scrollbar);
int position = (int)v.firstVisibleLine();
int page_size = (int)v.visibleLineCount(style().height - style().padding_top - style().padding_bottom);
int range = (int)v.lineCount();
scrollbar->SetScrollbar(
wxVERTICAL,
position,
page_size,
range,
page_size > 1 ? page_size - 1 : 0
);
}
void TextValueEditor::prepareDrawScrollbar(RotatedDC& dc) {
if (scrollbar) {
// don't draw under the scrollbar
int scrollbar_width = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
style().width.mutate() -= scrollbar_width;
// prepare text, and remember scroll position
double scroll_pos = v.getExactScrollPosition();
v.prepare(dc, value().value(), style(), viewer.getContext());
v.setExactScrollPosition(scroll_pos);
// scroll to the same place, but always show the caret
ensureCaretVisible();
// update after scrolling
updateScrollbar();
style().width.mutate() += scrollbar_width;
}
} }
\ No newline at end of file
...@@ -76,7 +76,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -76,7 +76,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
// --------------------------------------------------- : Other // --------------------------------------------------- : Other
virtual wxCursor cursor() const; virtual wxCursor cursor() const;
virtual void determineSize(); virtual void determineSize(bool force_fit = false);
virtual void onShow(bool); virtual void onShow(bool);
virtual void draw(RotatedDC&); virtual void draw(RotatedDC&);
...@@ -127,8 +127,17 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -127,8 +127,17 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
friend class TextValueEditorScrollBar; friend class TextValueEditorScrollBar;
/// When the cursor moves, should the scrollposition change?
bool scroll_with_cursor;
/// Scroll to the given position, called by scrollbar /// Scroll to the given position, called by scrollbar
void scrollTo(int pos); void scrollTo(int pos);
/// Update the scrollbar to show the current scroll position
void updateScrollbar();
/// Scrolls to ensure the caret stays visible, return true if the control is scrolled
bool ensureCaretVisible();
/// Prepare for drawing if there is a scrollbar
void prepareDrawScrollbar(RotatedDC& dc);
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -229,6 +229,66 @@ double TextViewer::heightOfLastLine() const { ...@@ -229,6 +229,66 @@ double TextViewer::heightOfLastLine() const {
return lines.back().line_height; return lines.back().line_height;
} }
// ----------------------------------------------------------------------------- : Scrolling
size_t TextViewer::lineCount() const {
return lines.size();
}
size_t TextViewer::visibleLineCount(double height) const {
size_t count = 0;
FOR_EACH_CONST(l, lines) {
if (l.top + l.line_height > height) return count;
if (l.top >= 0) ++count;
}
return count;
}
size_t TextViewer::firstVisibleLine() const {
size_t i = 0;
FOR_EACH_CONST(l, lines) {
if (l.top >= 0) return i;
i++;
}
return 0; //no visible lines
}
void TextViewer::scrollTo(size_t line_id) {
scrollBy(-lines.at(line_id).top);
}
void TextViewer::scrollBy(double delta) {
if (delta == 0) return;
FOR_EACH(l, lines) {
l.top += delta;
}
}
bool TextViewer::ensureVisible(double height, size_t char_id) {
const Line& line = findLine(char_id);
if (line.top < 0) {
// scroll up
scrollBy(-line.top);
return true;
} else if (line.bottom() > height) {
// scroll down
FOR_EACH(l, lines) {
if (l.top > 0) scrollBy(-l.line_height); // scroll down a single line ...
if (line.bottom() <= height) break; // ... until we can see the current line
}
return true;
} else {
return false; // line was already visible
}
}
double TextViewer::getExactScrollPosition() const {
if (lines.empty()) return 0;
return -lines.front().top;
}
void TextViewer::setExactScrollPosition(double pos) {
if (lines.empty()) return; // no scrolling is needed
pos += lines.front().top;
scrollBy(-pos);
}
// ----------------------------------------------------------------------------- : Elements // ----------------------------------------------------------------------------- : Elements
void TextViewer::prepareElements(const String& text, const TextStyle& style, Context& ctx) { void TextViewer::prepareElements(const String& text, const TextStyle& style, Context& ctx) {
......
...@@ -90,6 +90,31 @@ class TextViewer { ...@@ -90,6 +90,31 @@ class TextViewer {
/// Return the height of the last line /// Return the height of the last line
double heightOfLastLine() const; double heightOfLastLine() const;
// --------------------------------------------------- : Lines/scrolling
/// The total number of lines
size_t lineCount() const;
/// number of fully visible lines, height gives the height of the box
size_t visibleLineCount(double height) const;
/// the index of the first visible line
size_t firstVisibleLine() const;
// scroll so line_id becomes the first visible line
void scrollTo(size_t line_id);
/// Ensure the specified character is fully visible
/* Always scrolls by a whole line.
* Returns true if the editor has scrolled.
*/
bool ensureVisible(double height, size_t char_id);
/// Get exact scroll position
double getExactScrollPosition() const;
/// Set exact scroll position
void setExactScrollPosition(double pos);
private:
/// Scroll all lines a given amount
void scrollBy(double delta);
private: private:
// --------------------------------------------------- : More drawing // --------------------------------------------------- : More drawing
double scale; /// < Scale when drawing double scale; /// < Scale when drawing
......
...@@ -111,6 +111,7 @@ class Scriptable { ...@@ -111,6 +111,7 @@ class Scriptable {
inline operator const T& () const { return value; } inline operator const T& () const { return value; }
inline const T& operator ()() const { return value; } inline const T& operator ()() const { return value; }
inline T& mutate () { return value; }
inline bool isScripted() const { return script; } inline bool isScripted() const { return script; }
/// Has this value been read from a Reader? /// Has this value been read from a Reader?
inline bool hasBeenRead() const { return !script.unparsed.empty(); } inline bool hasBeenRead() const { return !script.unparsed.empty(); }
......
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