From d68f73edfcf01510039978ed4fef0f70ba606f43 Mon Sep 17 00:00:00 2001 From: twanvl Date: Thu, 23 Aug 2007 23:29:20 +0000 Subject: [PATCH] Various tweaks and fixes, mostly to the drop down lists git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@620 0fc631ac-6414-0410-93d0-97cfa31319b6 --- data/magic.mse-game/game | 30 ++++- doc/type/word_list_word.txt | 2 + src/gfx/blend_image.cpp | 18 +-- src/gui/control/card_viewer.cpp | 21 +++- src/gui/control/card_viewer.hpp | 3 +- src/gui/control/graph.cpp | 8 +- src/gui/drop_down_list.cpp | 11 +- src/gui/drop_down_list.hpp | 11 +- src/gui/value/text.cpp | 187 +++++++++++++++++++++++--------- src/gui/value/text.hpp | 3 + src/render/text/symbol.cpp | 1 + src/render/text/viewer.cpp | 28 +++-- src/render/value/choice.cpp | 2 +- src/render/value/color.cpp | 4 +- src/render/value/text.cpp | 4 +- src/script/functions/basic.cpp | 2 +- src/util/spec_sort.cpp | 3 +- 17 files changed, 246 insertions(+), 92 deletions(-) diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index 6db58c3b..0cfc08f7 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -442,11 +442,13 @@ init script: cmc := to_text + { 1 * number_of_items(in: sort_text(order:"SWUBRG")) # colored mana - 1 * number_of_items(in: sort_text(order:"/")) # guild mana, W/U -> 2 - 1 - + 1 * sort_text(order: "[0123456789]") # colorless mana + + 1 * filter_text(match: "^[0123456789]+(?!/)") # colorless mana, but not 1/2 + + 1 * (length(sort_text(order:"compound(1/2)")) / 3) # compensate for 1/2. Should actually be 1.5 * } colored_mana := to_text + { number_of_items(in: sort_text(order: "WUBRG")) # colored mana - number_of_items(in: sort_text(order:"/")) # guild mana, W/U -> 2 - 1 + + 1 * (length(sort_text(order:"compound(1/2)")) / 3) # compensate for 1/2. } primary_card_color := { artifact := chosen(choice:"artifact") @@ -1232,14 +1234,18 @@ statistics category: word list: name: type word: - name: Basic + name: Basic is prefix: true word: - name: Legendary - line below: true + name: Legendary is prefix: true + word: + name: Tribal + is prefix: true + line below: true word: Creature word: Artifact + word: Artifact Creature word: Enchantment word: Instant word: Sorcery @@ -1256,11 +1262,15 @@ word list: word list: name: artifact word: + name: + line below: true word: Equipment word list: name: land word: + name: + line below: true word: Plains word: Island word: Swamp @@ -1270,11 +1280,15 @@ word list: word list: name: enchantment word: + name: + line below: true word: Aura word list: name: spell word: + name: + line below: true word: Arcane @@ -1455,6 +1469,9 @@ keyword parameter type: # match: [A-Z][a-z, ]*([A-Z][a-z, ]*\xEB00) # commented out because it stopped prefix param from working, version below allows all "walks", including "Dame Judi Denchwalk", doesn't trigger #in middle of sentences, and doesn't trigger in chains of keywords. match: [A-Z][A-Z,a-z ]* example: Forest +keyword parameter type: + name: a + match: an? ############################# All Magic keywords # By JrEye and Neko_Asakami, Updated by Pichoro and Buttock1234 @@ -1872,3 +1889,8 @@ keyword: match: Grandeur mode: pseudo rules: Grandeur — Discard another card named ~: [effect]. +keyword: + keyword: Champion + match: Champion a name + mode: core + reminder: When this comes into play, sacrifice it unless you remove another {param2} you control from the game. When this leaves play, that card returns to play. diff --git a/doc/type/word_list_word.txt b/doc/type/word_list_word.txt index 6ba7614f..125b3e73 100644 --- a/doc/type/word_list_word.txt +++ b/doc/type/word_list_word.txt @@ -6,6 +6,8 @@ A word in a [[type:word list]]. ! Property Type Default Description | @name@ [[type:string]] ''Required'' The word | @line below@ [[type:boolean]] @false@ Display a line below this item in the list? +| @is prefix@ [[type:boolean]] @false@ Should this word be used as a prefix before another word from the list?
+ Think "Legendary ". Note the space after it, words are directly concatenated. | @words@ [[type:list]] of [[type:word list word]]s A submenu A word can also be given in a short form, in that case only the name is specified. diff --git a/src/gfx/blend_image.cpp b/src/gfx/blend_image.cpp index 9c5e5a4d..3b7623f3 100644 --- a/src/gfx/blend_image.cpp +++ b/src/gfx/blend_image.cpp @@ -81,18 +81,22 @@ void set_alpha(Image& img, const Image& img_alpha) { } if (!img.HasAlpha()) img.InitAlpha(); Byte *im = img.GetAlpha(), *al = img_alpha.GetData(); - UInt size = img.GetWidth() * img.GetHeight(); - for (UInt i = 0 ; i < size ; ++i) { + size_t size = img.GetWidth() * img.GetHeight(); + for (size_t i = 0 ; i < size ; ++i) { im[i] = (im[i] * al[i*3]) / 255; } } void set_alpha(Image& img, double alpha) { - if (!img.HasAlpha()) img.InitAlpha(); Byte b_alpha = alpha * 255; - Byte *im = img.GetAlpha(); - UInt size = img.GetWidth() * img.GetHeight(); - for (UInt i = 0 ; i < size ; ++i) { - im[i] = (im[i] * b_alpha) / 255; + if (!img.HasAlpha()) { + img.InitAlpha(); + memset(img.GetAlpha(), b_alpha, img.GetWidth() * img.GetHeight()); + } else { + Byte *im = img.GetAlpha(); + size_t size = img.GetWidth() * img.GetHeight(); + for (size_t i = 0 ; i < size ; ++i) { + im[i] = (im[i] * b_alpha) / 255; + } } } diff --git a/src/gui/control/card_viewer.cpp b/src/gui/control/card_viewer.cpp index d127e91c..6e2714c8 100644 --- a/src/gui/control/card_viewer.cpp +++ b/src/gui/control/card_viewer.cpp @@ -98,21 +98,30 @@ bool CardViewer::shouldDraw(const ValueViewer& v) const { } // helper class for overdrawDC() -class CardViewer::OverdrawDC : private wxClientDC, public wxBufferedDC { - public: - OverdrawDC(CardViewer* window) +class CardViewer::OverdrawDC_aux : private wxClientDC { + protected: + wxBufferedDC bufferedDC; + + OverdrawDC_aux(CardViewer* window) : wxClientDC(window) { - wxBufferedDC::Init((wxClientDC*)this, window->buffer); + bufferedDC.Init((wxClientDC*)this, window->buffer); } }; +class CardViewer::OverdrawDC : private OverdrawDC_aux, public RotatedDC { + public: + OverdrawDC(CardViewer* window) + : OverdrawDC_aux(window) + , RotatedDC(bufferedDC, window->getRotation(), QUALITY_LOW) + {} +}; -shared_ptr CardViewer::overdrawDC() { +shared_ptr CardViewer::overdrawDC() { #ifdef _DEBUG // don't call from onPaint assert(!inOnPaint()); #endif - return shared_ptr((wxBufferedDC*)new OverdrawDC(this)); + return shared_ptr(new OverdrawDC(this)); } Rotation CardViewer::getRotation() const { diff --git a/src/gui/control/card_viewer.hpp b/src/gui/control/card_viewer.hpp index 7e1f1df4..49055a33 100644 --- a/src/gui/control/card_viewer.hpp +++ b/src/gui/control/card_viewer.hpp @@ -28,7 +28,7 @@ class CardViewer : public wxControl, public DataViewer { /// Get a dc to draw on the card outside onPaint /** May NOT be called while in onPaint/draw */ - shared_ptr overdrawDC(); + shared_ptr overdrawDC(); /// Invalidate and redraw the entire viewer void redraw(); @@ -59,6 +59,7 @@ class CardViewer : public wxControl, public DataViewer { bool up_to_date; ///< Is the buffer up to date? class OverdrawDC; + class OverdrawDC_aux; }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/gui/control/graph.cpp b/src/gui/control/graph.cpp index 7664f4bd..5d8f1574 100644 --- a/src/gui/control/graph.cpp +++ b/src/gui/control/graph.cpp @@ -87,16 +87,16 @@ GraphData::GraphData(const GraphDataPre& d) left--; } } + // drop empty tail + while (a->groups.size() > 1 && a->groups.back().size == 0) { + a->groups.pop_back(); + } // Also keep non-numeric entries FOR_EACH(c, counts) { a->groups.push_back(GraphGroup(c.first, c.second)); a->max = max(a->max, c.second); a->total += c.second; } - // drop empty tail - while (a->groups.size() > 1 && a->groups.back().size == 0) { - a->groups.pop_back(); - } } else if (a->order) { // specific group order FOR_EACH_CONST(gn, *a->order) { diff --git a/src/gui/drop_down_list.cpp b/src/gui/drop_down_list.cpp index 8af5973b..97ef38e8 100644 --- a/src/gui/drop_down_list.cpp +++ b/src/gui/drop_down_list.cpp @@ -138,7 +138,7 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) { if (selected_item == NO_SELECTION && itemCount() > 0) selected_item = 0; // select first item by default mouse_down = false; Window::Show(); - if (!parent_menu && GetParent()->HasCapture()) { + if (isRoot() && GetParent()->HasCapture()) { // release capture on parent GetParent()->ReleaseMouse(); } @@ -168,7 +168,7 @@ void DropDownList::hide(bool event, bool allow_veto) { void DropDownList::realHide() { if (!IsShown()) return; Window::Hide(); -// onHide(); + onHide(); hideSubMenu(); if (parent_menu) { parent_menu->open_sub_menu = nullptr; @@ -226,7 +226,12 @@ int DropDownList::itemPosition(size_t item) const { void DropDownList::redrawArrowOnParent() { if (viewer) { ValueEditor* e = viewer->getEditor(); - if (e) e->redraw(); + if (e && viewer->viewer.nativeLook()) { + CardEditor& editor = static_cast(viewer->viewer); + shared_ptr dcP = editor.overdrawDC(); + RotatedDC& dc = *dcP; + draw_drop_down_arrow(&editor, dc.getDC(), dc.tr(viewer->getStyle()->getRect().grow(1)), IsShown()); + } } } diff --git a/src/gui/drop_down_list.hpp b/src/gui/drop_down_list.hpp index bdda6287..390b738f 100644 --- a/src/gui/drop_down_list.hpp +++ b/src/gui/drop_down_list.hpp @@ -45,6 +45,10 @@ class DropDownList : public wxPopupWindow { /// Prepare for showing the list virtual void onShow() {} + /// Do something after hiding the list + virtual void onHide() {} + + inline bool isRoot() { return parent_menu == nullptr; } // --------------------------------------------------- : Selection static const size_t NO_SELECTION = (size_t)-1; @@ -98,9 +102,7 @@ class DropDownList : public wxPopupWindow { void onPaint(wxPaintEvent&); void onLeftDown(wxMouseEvent&); void onLeftUp (wxMouseEvent&); - protected: - virtual void onMotion(wxMouseEvent&); // allow override - private: + void onMotion(wxMouseEvent&); // --------------------------------------------------- : Privates @@ -115,7 +117,8 @@ class DropDownList : public wxPopupWindow { void draw(DC& dc); void drawItem(DC& dc, int y, size_t item); - void redrawArrowOnParent(); + protected: + virtual void redrawArrowOnParent(); // allow override }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/gui/value/text.cpp b/src/gui/value/text.cpp index 7d899a18..92751989 100644 --- a/src/gui/value/text.cpp +++ b/src/gui/value/text.cpp @@ -83,44 +83,84 @@ class DropDownWordList : public DropDownList { public: DropDownWordList(Window* parent, bool is_submenu, TextValueEditor& tve, const WordListPosP& pos, const WordListWordP& list); + void setWords(const WordListWordP& words2); void setWords(const WordListPosP& pos2); protected: virtual void onShow(); + virtual void onHide(); + virtual void redrawArrowOnParent(); virtual size_t itemCount() const { return words->words.size(); } virtual bool lineBelow(size_t item) const { return words->words[item]->line_below; } virtual String itemText(size_t item) const { return words->words[item]->name; } + virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; virtual DropDownList* submenu(size_t item) const; virtual size_t selection() const; virtual void select(size_t item); private: TextValueEditor& tve; WordListPosP pos; - mutable vector submenus; WordListWordP words; ///< The words we are listing + bool has_checkboxes; ///< Do we need checkboxes? + mutable vector active; ///< Which items are checked? + mutable vector submenus; }; DropDownWordList::DropDownWordList(Window* parent, bool is_submenu, TextValueEditor& tve, const WordListPosP& pos, const WordListWordP& words) : DropDownList(parent, is_submenu, is_submenu ? nullptr : &tve) , tve(tve), pos(pos) - , words(words) + , has_checkboxes(false) { - item_size.height = max(16., item_size.height); + setWords(words); } void DropDownWordList::setWords(const WordListPosP& pos2) { - if (words != pos2->word_list) { - // switch to different list - submenus.clear(); - } + setWords((WordListWordP)pos2->word_list); pos = pos2; - words = pos2->word_list; +} +void DropDownWordList::setWords(const WordListWordP& words2) { + if (words == words2) return; + // switch to different list + submenus.clear(); + words = words2; + // do we need checkboxes? + has_checkboxes = false; + FOR_EACH(w, words->words) { + if (w->is_prefix) { + has_checkboxes = true; + break; + } + } + // size of items + icon_size.width = has_checkboxes ? 16 : 0; + item_size.height = max(16., item_size.height); } void DropDownWordList::onShow() { pos->active = true; } +void DropDownWordList::onHide() { + if (isRoot()) { + pos->active = false; + } +} + +void DropDownWordList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { + if (has_checkboxes) { + bool radio = !words->words[item]->is_prefix; + // draw checkbox + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + dc.DrawRectangle(x,y,16,16); + wxRect rect = RealRect(x+2,y+2,12,12); + if (radio) { + draw_radiobox(nullptr, dc, rect, active[item], itemEnabled(item)); + } else { + draw_checkbox(nullptr, dc, rect, active[item], itemEnabled(item)); + } + } +} DropDownList* DropDownWordList::submenu(size_t item) const { if (item >= submenus.size()) submenus.resize(item + 1); @@ -138,23 +178,55 @@ size_t DropDownWordList::selection() const { // current selection String current = untag(tve.value().value().substr(pos->start, pos->end - pos->start)); // find selection + size_t selected = NO_SELECTION; + bool prefix_selected = true; size_t i = 0; + active.resize(words->words.size()); FOR_EACH(w, words->words) { - if (current == w->name) return i; + if (w->is_prefix) { + if (starts_with(current, w->name)) { + active[i] = true; + current = current.substr(w->name.size()); + } else { + active[i] = false; + } + } else { + active[i] = (current == w->name); + } + if (active[i] && (selected == NO_SELECTION || (prefix_selected && !w->is_prefix))) { + selected = i; + prefix_selected = w->is_prefix; + } ++i; } - return NO_SELECTION; + return selected; } void DropDownWordList::select(size_t item) { + // determine new value + String new_value; + bool toggling_prefix = words->words[item]->is_prefix; + for (size_t i = 0 ; i < words->words.size() ; ++i) { + const WordListWord& w = *words->words[i]; + if (w.is_prefix && active[i] != (i == item)) { + new_value += w.name; + } else if (!w.is_prefix && (i == item || (toggling_prefix && active[i]))) { + new_value += w.name; + } + } + // set value pos->active = false; tve.selection_start_i = pos->start; tve.selection_end_i = pos->end; tve.fixSelection(TYPE_INDEX); - tve.replaceSelection(escape(words->words[item]->name), + tve.replaceSelection(escape(new_value), format_string(_ACTION_("change"), tve.field().name)); } +void DropDownWordList::redrawArrowOnParent() { + tve.redrawSelection(tve.selection_start_i, tve.selection_end_i, !tve.dropDownShown()); +} + // ----------------------------------------------------------------------------- : TextValueEditor IMPLEMENT_VALUE_EDITOR(Text) @@ -220,6 +292,7 @@ bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) { bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) { if (dropDownShown()) return false; select_words = true; + selecting = true; size_t index = v.indexAt(style().getRotation().trInv(pos)); moveSelection(TYPE_INDEX, prevWordBoundry(index), true, MOVE_MID); moveSelection(TYPE_INDEX, nextWordBoundry(index), false, MOVE_MID); @@ -412,7 +485,7 @@ void TextValueEditor::onMenu(wxCommandEvent& ev) { } */ -// ----------------------------------------------------------------------------- : Other overrides +// ----------------------------------------------------------------------------- : Drawing void TextValueEditor::draw(RotatedDC& dc) { // update scrollbar @@ -432,6 +505,45 @@ void TextValueEditor::draw(RotatedDC& dc) { } } +void TextValueEditor::redrawSelection(size_t old_selection_start_i, size_t old_selection_end_i, bool old_drop_down_shown) { + if (!isCurrent()) return; + if (old_drop_down_shown && dropDownShown()) return; + // Hide caret + wxCaret* caret = editor().GetCaret(); + if (caret->IsVisible()) caret->Hide(); + // Destroy the clientDC before reshowing the caret, prevent flicker on MSW + { + // Move selection + shared_ptr dcP = editor().overdrawDC(); + RotatedDC& dc = *dcP; + if (nativeLook()) { + // clip the dc to the region of this control + dc.SetClippingRegion(style().getRect()); + } + // clear old selection by drawing it again + if (!old_drop_down_shown) { + v.drawSelection(dc, style(), old_selection_start_i, old_selection_end_i); + } + // scroll? + scroll_with_cursor = true; + if (ensureCaretVisible()) { + // we can't redraw just the selection because we must scroll + updateScrollbar(); + redraw(); + } else { + // draw new selection + if (!dropDownShown()) { + v.drawSelection(dc, style(), selection_start_i, selection_end_i); + } + // redraw drop down indicators + drawWordListIndicators(dc); + } + } + showCaret(); +} + +// ----------------------------------------------------------------------------- : Other overrides + wxCursor rotated_ibeam; wxCursor TextValueEditor::cursor(const RealPoint& pos) const { @@ -725,39 +837,10 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& } void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_start, Movement dir) { - if (!isCurrent()) { - // selection is only visible for curent editor, we can do a move the simple way - moveSelectionNoRedraw(t, new_end, also_move_start, dir); - return; - } - // Hide caret - wxCaret* caret = editor().GetCaret(); - if (caret->IsVisible()) caret->Hide(); - // Destroy the clientDC before reshowing the caret, prevent flicker on MSW - { - // Move selection - shared_ptr dc = editor().overdrawDC(); - RotatedDC rdc(*dc, viewer.getRotation(), QUALITY_LOW); - if (nativeLook()) { - // clip the dc to the region of this control - rdc.SetClippingRegion(style().getRect()); - } - // clear old selection by drawing it again - v.drawSelection(rdc, style(), selection_start_i, selection_end_i); - // move - moveSelectionNoRedraw(t, new_end, also_move_start, dir); - // scroll? - scroll_with_cursor = true; - if (ensureCaretVisible()) { - // we can't redraw just the selection because we must scroll - updateScrollbar(); - redraw(); - } else { - // draw new selection - v.drawSelection(rdc, style(), selection_start_i, selection_end_i); - } - } - showCaret(); + size_t old_start = selection_start_i; + size_t old_end = selection_end_i; + moveSelectionNoRedraw(t, new_end, also_move_start, dir); + redrawSelection(old_start, old_end, dropDownShown()); } void TextValueEditor::moveSelectionNoRedraw(IndexType t, size_t new_end, bool also_move_start, Movement dir) { @@ -1045,15 +1128,18 @@ void TextValueEditor::drawWordListIndicators(RotatedDC& dc) { // draw background if (current && wl->active) { dc.SetPen (Color(0, 128,255)); - dc.SetBrush(Color(128,192,255)); - } else if (current) { + dc.SetBrush(Color(64, 160,255)); + } else if (current && selection_end_i >= wl->start && selection_end_i <= wl->end) { dc.SetPen (Color(64, 160,255)); dc.SetBrush(Color(160,208,255)); } else { dc.SetPen (Color(128,128,128)); dc.SetBrush(Color(192,192,192)); } - dc.DrawRectangle(RealRect(r.right(), r.top(), 9, r.height)); + dc.DrawRectangle(RealRect(r.right(), r.top() - 1, 9, r.height + 2)); + // draw rectangle around value + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(r.move(-1,-1,2,2)); // draw foreground /* dc.SetPen (*wxTRANSPARENT_PEN); @@ -1062,7 +1148,7 @@ void TextValueEditor::drawWordListIndicators(RotatedDC& dc) { dc.getDC().DrawPolygon(3, poly, r.right() + 2, r.bottom() - 5); */ dc.SetPen (*wxBLACK_PEN); - double x = r.right(), y = r.bottom(); + double x = r.right(), y = r.bottom() - 1; dc.DrawLine(RealPoint(x + 4, y - 3), RealPoint(x + 5, y - 3)); dc.DrawLine(RealPoint(x + 3, y - 4), RealPoint(x + 6, y - 4)); dc.DrawLine(RealPoint(x + 2, y - 5), RealPoint(x + 7, y - 5)); @@ -1090,13 +1176,14 @@ WordListPosP TextValueEditor::findWordList(size_t index) const { bool TextValueEditor::wordListDropDown(const WordListPosP& wl) { if (!wl) return false; + if (dropDownShown()) return false; // show dropdown if (drop_down) { drop_down->setWords(wl); } else { drop_down.reset(new DropDownWordList(&editor(), false, *this, wl, wl->word_list)); } - RealRect rect = wl->rect.move(style().left, style().top, 0, 0); + RealRect rect = wl->rect.move(style().left, style().top - 1, 0, 2); drop_down->show(false, wxPoint(0,0), &rect); return true; } diff --git a/src/gui/value/text.hpp b/src/gui/value/text.hpp index 2a95f8d6..b7ba49f1 100644 --- a/src/gui/value/text.hpp +++ b/src/gui/value/text.hpp @@ -111,6 +111,9 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { /** t specifies what kind of position new_end is */ void moveSelectionNoRedraw(IndexType t, size_t new_end, bool also_move_start=true, Movement dir = MOVE_MID); + /// Redraw the selection + void redrawSelection(size_t old_selection_start_i, size_t old_selection_end_i, bool old_drop_down_shown); + /// Replace the current selection with 'replacement', name the action /** replacement should be a tagged string (i.e. already escaped) */ void replaceSelection(const String& replacement, const String& name); diff --git a/src/render/text/symbol.cpp b/src/render/text/symbol.cpp index 7240587a..a0047984 100644 --- a/src/render/text/symbol.cpp +++ b/src/render/text/symbol.cpp @@ -12,6 +12,7 @@ // ----------------------------------------------------------------------------- : SymbolTextElement void SymbolTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const { + if (!(what & DRAW_NORMAL)) return; if (font.font) { font.font->draw(dc, ctx, rect, font.size * scale, font.alignment, content.substr(start - this->start, end-start)); } diff --git a/src/render/text/viewer.cpp b/src/render/text/viewer.cpp index 0b637d7b..c60be9da 100644 --- a/src/render/text/viewer.cpp +++ b/src/render/text/viewer.cpp @@ -40,9 +40,9 @@ struct TextViewer::Line { return top + line_height > 0 && top < rot.getInternalSize().height; } - /// Draws a selection indicator on this line from start to end + /// Get a rectangle of the selection on this line /** start and end need not be in this line */ - void drawSelection(RotatedDC& dc, size_t start, size_t end); + RealRect selectionRectangle(const Rotation& rot, size_t start, size_t end); }; size_t TextViewer::Line::posToIndex(double x) const { @@ -89,6 +89,13 @@ void TextViewer::draw(RotatedDC& dc, const TextStyle& style, DrawWhat what) { } } +/// Intersection between two rectangles +RealRect intersect(const RealRect& a, const RealRect& b) { + RealPoint tl = piecewise_max(a.topLeft(), b.topLeft()); + RealPoint br = piecewise_min(a.bottomRight(), b.bottomRight()); + return RealRect(tl, RealSize(br - tl)); +} + void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end) { Rotater r(dc, style.getRotation()); if (sel_start == sel_end) return; @@ -96,18 +103,25 @@ void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel dc.SetBrush(*wxBLACK_BRUSH); dc.SetPen(*wxTRANSPARENT_PEN); dc.SetLogicalFunction(wxINVERT); + RealRect prev_rect(0,0,0,0); FOR_EACH(l, lines) { - l.drawSelection(dc, sel_start, sel_end); + RealRect rect = l.selectionRectangle(dc, sel_start, sel_end); + if (rect.height > 0) dc.DrawRectangle(rect); + // compensate for overlap between lines + RealRect overlap = intersect(rect, prev_rect); + if (overlap.height > 0 && overlap.width > 0) dc.DrawRectangle(overlap); + prev_rect = rect; } dc.SetLogicalFunction(wxCOPY); } -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) { +RealRect TextViewer::Line::selectionRectangle(const Rotation& rot, size_t sel_start, size_t sel_end) { + if (visible(rot) && sel_start < 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)); + return RealRect(x1, top, x2 - x1, line_height); + } else { + return RealRect(0,0,0,0); } } diff --git a/src/render/value/choice.cpp b/src/render/value/choice.cpp index 6617b47c..8b9d72cc 100644 --- a/src/render/value/choice.cpp +++ b/src/render/value/choice.cpp @@ -37,7 +37,7 @@ void ChoiceValueViewer::draw(RotatedDC& dc) { } else { img_options.width = (int) dc.trX(style().width); img_options.height = (int) dc.trY(style().height); - img_options.preserve_aspect = style().alignment == ALIGN_STRETCH ? ASPECT_STRETCH : ASPECT_FIT; + img_options.preserve_aspect = (style().alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT; } Image image = img.generate(img_options, true); ImageCombine combine = img.combine(); diff --git a/src/render/value/color.cpp b/src/render/value/color.cpp index 05159519..cadca657 100644 --- a/src/render/value/color.cpp +++ b/src/render/value/color.cpp @@ -70,8 +70,8 @@ void ColorValueViewer::draw(RotatedDC& dc) { bool ColorValueViewer::containsPoint(const RealPoint& p) const { // distance to each side - double left = p.x - style().left, right = style().left + style().width - p.x - 1; - double top = p.y - style().top, bottom = style().top + style().height - p.y - 1; + double left = p.x - style().left, right = style().right - p.x - 1; + double top = p.y - style().top, bottom = style().bottom - p.y - 1; if (left < 0 || right < 0 || top < 0 || bottom < 0 || // outside bounding box (left >= style().left_width && right >= style().right_width && // outside horizontal border top >= style().top_width && bottom >= style().bottom_width)) { // outside vertical border diff --git a/src/render/value/text.cpp b/src/render/value/text.cpp index 23eedee3..c638f1e6 100644 --- a/src/render/value/text.cpp +++ b/src/render/value/text.cpp @@ -29,10 +29,12 @@ void TextValueViewer::draw(RotatedDC& dc) { if (!v.prepared()) { v.prepare(dc, value().value(), style(), viewer.getContext()); } + if (viewer.drawFocus() && isCurrent()) { + v.draw(dc, style(), DRAW_ACTIVE); + } v.draw(dc, style(), (DrawWhat)( DRAW_NORMAL | (viewer.drawBorders() ? DRAW_BORDERS : 0) - | (viewer.drawFocus() && isCurrent() ? DRAW_ACTIVE : 0) )); } diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index 9a53fcb2..eb6079d2 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -600,7 +600,7 @@ void init_script_basic_functions(Context& ctx) { ctx.setVariable(_("tag remove rule"), script_tag_remove_rule); // collection ctx.setVariable(_("position"), script_position_of); - ctx.setVariable(_("length"), script_number_of_items); + ctx.setVariable(_("length"), script_length); ctx.setVariable(_("number of items"), script_number_of_items); ctx.setVariable(_("filter list"), script_filter_list); ctx.setVariable(_("sort list"), script_sort_list); diff --git a/src/util/spec_sort.cpp b/src/util/spec_sort.cpp index 41ef6373..40bf92ae 100644 --- a/src/util/spec_sort.cpp +++ b/src/util/spec_sort.cpp @@ -169,7 +169,8 @@ void mixed_sort(const String& spec, String& input, String& ret) { /// Sort a string, find a compound item /** Removed used characters from input! */ void compound_sort(const String& spec, String& input, String& ret) { - while (size_t pos = input.find(spec)) { + size_t pos = input.find(spec); + while (pos != String::npos) { ret += spec; for (size_t j = 0 ; j < spec.size() ; ++j) input.SetChar(pos + j, REMOVED); pos = input.find(spec, pos + 1);