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);