mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
scrollbar in text editor
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@154 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -26,12 +26,6 @@ Rotation TextCtrl::getRotation() const {
|
||||
}
|
||||
|
||||
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);
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
}
|
||||
@@ -60,8 +54,11 @@ void TextCtrl::setValue(String* value) {
|
||||
IndexMap<FieldP,ValueP> values; values.add(field, value);
|
||||
setStyles(set->stylesheet, styles);
|
||||
setData(values);
|
||||
// determine required height
|
||||
viewers.front()->getEditor()->determineSize();
|
||||
// determine size
|
||||
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));
|
||||
}
|
||||
valueChanged();
|
||||
@@ -97,7 +94,14 @@ void TextCtrl::onInit() {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -108,7 +108,7 @@ void ChoiceValueEditor::draw(RotatedDC& dc) {
|
||||
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.);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor {
|
||||
virtual void onLoseFocus();
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void determineSize();
|
||||
virtual void determineSize(bool);
|
||||
|
||||
private:
|
||||
DropDownListP drop_down;
|
||||
|
||||
@@ -144,7 +144,7 @@ void ColorValueEditor::draw(RotatedDC& dc) {
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown());
|
||||
}
|
||||
}
|
||||
void ColorValueEditor::determineSize() {
|
||||
void ColorValueEditor::determineSize(bool) {
|
||||
style().height = 20;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class ColorValueEditor : public ColorValueViewer, public ValueEditor {
|
||||
virtual void onLoseFocus();
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void determineSize();
|
||||
virtual void determineSize(bool);
|
||||
|
||||
private:
|
||||
DropDownListP drop_down;
|
||||
|
||||
@@ -96,7 +96,7 @@ class ValueEditor {
|
||||
/// The cursor type to use when the mouse is over this control
|
||||
virtual wxCursor cursor() const { return wxCursor(); }
|
||||
/// 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
|
||||
virtual void onShow(bool) {}
|
||||
};
|
||||
|
||||
@@ -19,6 +19,6 @@ void SymbolValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent&) {
|
||||
wnd->Show();
|
||||
}
|
||||
|
||||
void SymbolValueEditor::determineSize() {
|
||||
void SymbolValueEditor::determineSize(bool) {
|
||||
style().height = 50;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class SymbolValueEditor : public SymbolValueViewer, public ValueEditor {
|
||||
DECLARE_VALUE_EDITOR(Symbol);
|
||||
|
||||
virtual void onLeftDClick(const RealPoint& pos, wxMouseEvent&);
|
||||
virtual void determineSize();
|
||||
virtual void determineSize(bool);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+63
-57
@@ -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)
|
||||
, tve(te)
|
||||
, tve(tve)
|
||||
{}
|
||||
|
||||
void TextValueEditorScrollBar::onScroll(wxScrollWinEvent& ev) {
|
||||
@@ -57,8 +57,12 @@ IMPLEMENT_VALUE_EDITOR(Text)
|
||||
, selection_start (0), selection_end (0)
|
||||
, selection_start_i(0), selection_end_i(0)
|
||||
, select_words(false)
|
||||
, scrollbar(nullptr)
|
||||
{}
|
||||
, scrollbar(nullptr), scroll_with_cursor(false)
|
||||
{
|
||||
if (viewer.nativeLook() && field().multi_line) {
|
||||
scrollbar = new TextValueEditorScrollBar(*this);
|
||||
}
|
||||
}
|
||||
|
||||
TextValueEditor::~TextValueEditor() {
|
||||
delete scrollbar;
|
||||
@@ -234,10 +238,13 @@ void TextValueEditor::onMenu(wxCommandEvent& ev) {
|
||||
// ----------------------------------------------------------------------------- : Other overrides
|
||||
|
||||
void TextValueEditor::draw(RotatedDC& dc) {
|
||||
// update scrollbar
|
||||
prepareDrawScrollbar(dc);
|
||||
// draw text
|
||||
TextValueViewer::draw(dc);
|
||||
// draw selection
|
||||
if (isCurrent()) {
|
||||
v.drawSelection(dc, style(), selection_start_i, selection_end_i);
|
||||
|
||||
// show caret, onAction() would be a better place
|
||||
// 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
|
||||
@@ -469,7 +476,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
|
||||
}
|
||||
fixSelection(TYPE_INDEX, MOVE_MID);
|
||||
// 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) {
|
||||
@@ -495,15 +502,15 @@ void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_
|
||||
// move
|
||||
moveSelectionNoRedraw(t, new_end, also_move_start, dir);
|
||||
// scroll?
|
||||
// scrollWithCursor = true;
|
||||
// if (onMove()) {
|
||||
// // we can't redraw just the selection because we must scroll
|
||||
// updateScrollbar();
|
||||
// editor.refreshEditor();
|
||||
// } else {
|
||||
scroll_with_cursor = true;
|
||||
if (ensureCaretVisible()) {
|
||||
// we can't redraw just the selection because we must scroll
|
||||
updateScrollbar();
|
||||
// editor.refreshEditor();
|
||||
} else {
|
||||
// draw new selection
|
||||
v.drawSelection(rdc, style(), selection_start_i, selection_end_i);
|
||||
// }
|
||||
}
|
||||
}
|
||||
showCaret();
|
||||
}
|
||||
@@ -545,46 +552,6 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) {
|
||||
// find next separator
|
||||
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)
|
||||
|
||||
// ----------------------------------------------------------------------------- : Native look / scrollbar
|
||||
|
||||
void TextValueEditor::determineSize() {
|
||||
void TextValueEditor::determineSize(bool force_fit) {
|
||||
if (!nativeLook()) return;
|
||||
style().angle = 0; // no rotation in nativeLook
|
||||
if (scrollbar) {
|
||||
// muliline, determine scrollbar size
|
||||
style().height = 100;
|
||||
if (!force_fit) style().height = 100;
|
||||
int sbw = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
|
||||
scrollbar->SetSize(
|
||||
style().left + style().width - sbw + 1,
|
||||
@@ -667,8 +634,47 @@ void TextValueEditor::onMouseWheel(const RealPoint& pos, wxMouseEvent& ev) {
|
||||
|
||||
void TextValueEditor::scrollTo(int pos) {
|
||||
// scroll
|
||||
// r.scrollTo(pos);
|
||||
v.scrollTo(pos);
|
||||
// move the cursor if needed
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -76,7 +76,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
|
||||
// --------------------------------------------------- : Other
|
||||
|
||||
virtual wxCursor cursor() const;
|
||||
virtual void determineSize();
|
||||
virtual void determineSize(bool force_fit = false);
|
||||
virtual void onShow(bool);
|
||||
virtual void draw(RotatedDC&);
|
||||
|
||||
@@ -127,8 +127,17 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
|
||||
|
||||
friend class TextValueEditorScrollBar;
|
||||
|
||||
/// When the cursor moves, should the scrollposition change?
|
||||
bool scroll_with_cursor;
|
||||
|
||||
/// Scroll to the given position, called by scrollbar
|
||||
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
|
||||
|
||||
@@ -229,6 +229,66 @@ double TextViewer::heightOfLastLine() const {
|
||||
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
|
||||
|
||||
void TextViewer::prepareElements(const String& text, const TextStyle& style, Context& ctx) {
|
||||
|
||||
@@ -90,6 +90,31 @@ class TextViewer {
|
||||
/// Return the height of the last line
|
||||
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:
|
||||
// --------------------------------------------------- : More drawing
|
||||
double scale; /// < Scale when drawing
|
||||
|
||||
@@ -111,6 +111,7 @@ class Scriptable {
|
||||
|
||||
inline operator const T& () const { return value; }
|
||||
inline const T& operator ()() const { return value; }
|
||||
inline T& mutate () { return value; }
|
||||
inline bool isScripted() const { return script; }
|
||||
/// Has this value been read from a Reader?
|
||||
inline bool hasBeenRead() const { return !script.unparsed.empty(); }
|
||||
|
||||
Reference in New Issue
Block a user