From 616ea3f725b69e6987102bbcbb373a02d696dba2 Mon Sep 17 00:00:00 2001
From: GenevensiS <66968533+G-e-n-e-v-e-n-s-i-S@users.noreply.github.com>
Date: Thu, 22 May 2025 12:52:26 +0200
Subject: [PATCH] Create slider field type
---
doc/type/field.txt | 14 +++++
resource/slider_center.png | Bin 0 -> 487 bytes
resource/slider_left.png | Bin 0 -> 529 bytes
resource/slider_right.png | Bin 0 -> 480 bytes
resource/slider_tick.png | Bin 0 -> 5704 bytes
resource/win32_res.rc | 5 ++
src/data/field.cpp | 2 +
src/data/field/slider.cpp | 75 ++++++++++++++++++++++++
src/data/field/slider.hpp | 60 +++++++++++++++++++
src/gui/drop_down_list.cpp | 114 +++++++++++++++++++++++++++----------
src/gui/drop_down_list.hpp | 13 ++++-
src/gui/value/choice.cpp | 24 +++++++-
12 files changed, 274 insertions(+), 33 deletions(-)
create mode 100644 resource/slider_center.png
create mode 100644 resource/slider_left.png
create mode 100644 resource/slider_right.png
create mode 100644 resource/slider_tick.png
create mode 100644 src/data/field/slider.cpp
create mode 100644 src/data/field/slider.hpp
diff --git a/doc/type/field.txt b/doc/type/field.txt
index e43abd74..eefa4a78 100644
--- a/doc/type/field.txt
+++ b/doc/type/field.txt
@@ -22,6 +22,7 @@ Fields are part of the [[file:style triangle]]:
* @multiple choice@
* @package choice@
* @boolean@
+ * @slider@
* @image@
* @symbol@
* @color@
@@ -49,6 +50,7 @@ The @type@ determines what values of this field contain:
| @multiple choice@ Zero or more choices from a list A single image or multiple images
| @package choice@ A choice from a list of installed [[type:package]]s Text and/or an image
| @boolean@ @yes@ or @no@ Text or an image or both
+| @slider@ A choice from a list of numbers Text or an image
| @color@ Any color or a restricted selection from a list A box filled with the color
| @image@ Any image The image
| @symbol@ A [[type:symbol]] edited with the symbol editor The image
@@ -78,6 +80,18 @@ Additional properties are available, depending on the type of field:
These choices must appear in the same order as they do in the @choices@ property.
| @"boolean"@ ''A boolean field is a choice field with the choices @"yes"@ and @"no"@.'' <<< <<< <<<
+
+| @"slider"@ ''A slider field is a choice field where the choices are all numbers.'' <<< <<< <<<
+| ^^^ @script@ [[type:script]] Script to apply to values of this field after each change.
+ If the script evaluates to a constant (i.e. doesn't use @value@) then values in this field can effectively not be edited.
+| ^^^ @default@ [[type:script]] Script to determine the value when it is in the default state (not edited).
+| ^^^ @initial@ [[type:string]] Initial value for new values for this field. Must be a number, otherwise it will be replaced with minimum.
+| ^^^ @default name@ [[type:string]] @"Default"@ Name of the default state.
+| ^^^ @minimum@ [[type:integer]] Minimum possible value for this field.
+| ^^^ @maximum@ [[type:integer]] Maximum possible value for this field.
+| ^^^ @increment@ [[type:integer]] Gap between possible values for this field.
+| ^^^ @choice colors@ [[type:map]] of opaque [[type:color]]s Colors of the choices for statistics graphs.
+| ^^^ @choice colors cardlist@ [[type:map]] of opaque [[type:color]]s Colors of the choices for lines in the card list,
see also the @card list color script@ property of [[type:game]]s.
| @"package choice"@
@script@ [[type:script]] Script to apply to values of this field after each change.
diff --git a/resource/slider_center.png b/resource/slider_center.png
new file mode 100644
index 0000000000000000000000000000000000000000..d191b98e143818d637786f293fa0390eb2c7a036
GIT binary patch
literal 487
zcmVEX>4Tx04R~2kUdMoP!xur)=!FpVsQ|M46YsR(
z2uz-K`ez=M=kt}C)t8p_s-FAvEQiC~Naw1=q*(#A195v=q?T1Q@4ViLcqVqjI0?m1
z;=M2qE!*8*98JuRr@hVEFuCm8zG@ejb2@Z!7%*W>L_tfMsB=o2eW^=Ezymo(8ZlIF
zs+odYQ7$K!W05D)Z7TA1%Dk>p2J#ro%O>0t%hTpz|Mz?C(z;000SaNLh0L01m?d01m?e$8V@)
z00007bV*G`2k8YA5hw-P*E}!)0037>L_t&-)5Van4uC)iLtoFvIQhZ;xDG}f#Jd4^
zStzBDrrJb90R#j9KEw$E^fd6=2PK7_;5`JqP|7VqegyFRp_{1xTGf3}zj57P4A;K;
ddStcN3~uD73)Jk|Q8NGl002ovPDHLkV1ju<)8POB
literal 0
HcmV?d00001
diff --git a/resource/slider_left.png b/resource/slider_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..127cccec9333c76b0cbabe750d06ed8154c8c8dd
GIT binary patch
literal 529
zcmV+s0`C2ZP)EX>4Tx04R~2kUdMoP!xur)=!FpVsQ|M46YsR(
z2uz-K`ez=M=kt}C)t8p_s-FAvEQiC~Naw1=q*(#A195v=q?T1Q@4ViLcqVqjI0?m1
z;=M2qE!*8*98JuRr@hVEFuCm8zG@ejb2@Z!7%*W>L_tfMsB=o2eW^=Ezymo(8ZlIF
zs+odYQ7$K!W05D)Z7TA1%Dk>p2J#ro%O>0t%hTpz|Mz?C(z;000SaNLh0L01m?d01m?e$8V@)
z00007bV*G`2k8YA5holuB#1u%004nWL_t&-(~XWX4#GeT1ZT%i_%I)*Ljvw7q69@y
zu;qAcZ?5Z1g)Nf-vu{u)1Sml0OKWKnLJG$0%n&*x0z>K1Rclo|j?%=aRz57)<>J{Z
zwa;Em%4{zH5R;FxG5Lz*2V+V`&g_@>7i)W;9*z3(TDE?}xwCuC$MRfEX>4Tx04R~2kUdMoP!xur)=!FpVsQ|M46YsR(
z2uz-K`ez=M=kt}C)t8p_s-FAvEQiC~Naw1=q*(#A195v=q?T1Q@4ViLcqVqjI0?m1
z;=M2qE!*8*98JuRr@hVEFuCm8zG@ejb2@Z!7%*W>L_tfMsB=o2eW^=Ezymo(8ZlIF
zs+odYQ7$K!W05D)Z7TA1%Dk>p2J#ro%O>0t%hTpz|Mz?C(z;000SaNLh0L01m?d01m?e$8V@)
z00007bV*G`2k8YA5hyDL0Wtpo002-)L_t&-(~Zu-4uCKS1JDAE@$B#V;c6B(?}fuS
zzBDa%LoOB{wLxBLIb8`y@KiIBYMzaOr7)6Fa$a|V#%wRQHgjlqiCKRxjWoWR8{ZG6
Wj}Fz51@<}s0000%BdD@87fcZ|`S6^CbItEmv1H
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
literal 0
HcmV?d00001
diff --git a/resource/win32_res.rc b/resource/win32_res.rc
index bbdf8f37..b7e3d252 100644
--- a/resource/win32_res.rc
+++ b/resource/win32_res.rc
@@ -192,6 +192,11 @@ shape_align_middle IMAGE "shape_align_middle.png"
shape_align_center IMAGE "shape_align_center.png"
shape_align_both IMAGE "shape_align_both.png"
+slider_left IMAGE "slider_left.png"
+slider_right IMAGE "slider_right.png"
+slider_center IMAGE "slider_center.png"
+slider_tick IMAGE "slider_tick.png"
+
// -------------------------------------------------------- : WX
//wxBITMAP_STD_COLOURS BITMAP "wx/msw/colours.bmp"
diff --git a/src/data/field.cpp b/src/data/field.cpp
index 9e85c63c..36536291 100644
--- a/src/data/field.cpp
+++ b/src/data/field.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -80,6 +81,7 @@ intrusive_ptr read_new(Reader& reader) {
else if (type == _("choice")) field = make_intrusive();
else if (type == _("multiple choice")) field = make_intrusive();
else if (type == _("boolean")) field = make_intrusive();
+ else if (type == _("slider")) field = make_intrusive();
else if (type == _("image")) field = make_intrusive();
else if (type == _("symbol")) field = make_intrusive();
else if (type == _("color")) field = make_intrusive();
diff --git a/src/data/field/slider.cpp b/src/data/field/slider.cpp
new file mode 100644
index 00000000..0fafc70e
--- /dev/null
+++ b/src/data/field/slider.cpp
@@ -0,0 +1,75 @@
+//+----------------------------------------------------------------------------+
+//| Description: Magic Set Editor - Program to make Magic (tm) cards |
+//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
+//| License: GNU General Public License 2 or later (see file COPYING) |
+//+----------------------------------------------------------------------------+
+
+// ----------------------------------------------------------------------------- : Includes
+
+#include
+#include
+#include
+#include
+
+// ----------------------------------------------------------------------------- : SliderField
+
+SliderField::SliderField()
+ : minimum(0)
+ , maximum(100)
+ , increment(1)
+{}
+
+IMPLEMENT_FIELD_TYPE(Slider, "slider");
+
+IMPLEMENT_REFLECTION(SliderField) {
+ REFLECT_BASE(Field); // NOTE: don't reflect as a ChoiceField
+ REFLECT(script);
+ REFLECT_N("default", default_script);
+ REFLECT(initial);
+ REFLECT(minimum);
+ REFLECT(maximum);
+ REFLECT(increment);
+}
+
+void SliderField::after_reading(Version ver) {
+ Field::after_reading(ver);
+
+ if (maximum < minimum) {
+ int temp = maximum;
+ maximum = minimum;
+ minimum = temp;
+ }
+ if (increment < 1) increment = 1;
+
+ for (int i = minimum; i < maximum; i += increment) {
+ choices->choices.push_back(make_intrusive(wxString::Format(wxT("%i"), i)));
+ }
+ choices->choices.push_back(make_intrusive(wxString::Format(wxT("%i"), maximum)));
+
+ int initial_int;
+ try {
+ initial_int = std::stoi(initial.ToStdString());
+ if (initial_int < minimum || initial_int > maximum) initial = wxString::Format(wxT("%i"), minimum);
+ }
+ catch (...) {
+ initial = wxString::Format(wxT("%i"), minimum);
+ }
+ choices->initIds();
+}
+// ----------------------------------------------------------------------------- : SliderStyle
+
+SliderStyle::SliderStyle(const ChoiceFieldP& field)
+ : ChoiceStyle(field)
+{
+ render_style = RENDER_TEXT;
+}
+
+IMPLEMENT_REFLECTION(SliderStyle) {
+ REFLECT_BASE(ChoiceStyle);
+}
+
+// ----------------------------------------------------------------------------- : SliderValue
+
+IMPLEMENT_REFLECTION_NAMELESS(SliderValue) {
+ REFLECT_BASE(ChoiceValue);
+}
diff --git a/src/data/field/slider.hpp b/src/data/field/slider.hpp
new file mode 100644
index 00000000..8218eee6
--- /dev/null
+++ b/src/data/field/slider.hpp
@@ -0,0 +1,60 @@
+//+----------------------------------------------------------------------------+
+//| Description: Magic Set Editor - Program to make Magic (tm) cards |
+//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
+//| License: GNU General Public License 2 or later (see file COPYING) |
+//+----------------------------------------------------------------------------+
+
+#pragma once
+
+// ----------------------------------------------------------------------------- : Includes
+
+#include
+#include
+
+// ----------------------------------------------------------------------------- : SliderField
+
+DECLARE_POINTER_TYPE(SliderField);
+DECLARE_POINTER_TYPE(SliderStyle);
+DECLARE_POINTER_TYPE(SliderValue);
+
+/// A field whos value is an integer from a range
+class SliderField : public ChoiceField {
+public:
+ SliderField();
+ DECLARE_FIELD_TYPE(SliderField);
+
+ int minimum, maximum, increment;
+
+ void after_reading(Version ver) override;
+};
+
+// ----------------------------------------------------------------------------- : SliderStyle
+
+/// The Style for a SliderField
+class SliderStyle : public ChoiceStyle {
+public:
+ SliderStyle(const ChoiceFieldP& field);
+ DECLARE_HAS_FIELD(Slider); // not DECLARE_STYLE_TYPE, because we use a normal ChoiceValueViewer/Editor
+ StyleP clone() const override;
+
+ // no extra data
+
+private:
+ DECLARE_REFLECTION();
+};
+
+// ----------------------------------------------------------------------------- : SliderValue
+
+/// The Value in a SliderField
+class SliderValue : public ChoiceValue {
+public:
+ inline SliderValue(const ChoiceFieldP& field) : ChoiceValue(field) {}
+ DECLARE_HAS_FIELD(Slider);
+ ValueP clone() const override;
+
+ // no extra data
+
+private:
+ DECLARE_REFLECTION();
+};
+
diff --git a/src/gui/drop_down_list.cpp b/src/gui/drop_down_list.cpp
index 779f2a1e..4d263a51 100644
--- a/src/gui/drop_down_list.cpp
+++ b/src/gui/drop_down_list.cpp
@@ -15,6 +15,12 @@
#include
#include
#include
+
+bool DropDownList::slider_loaded;
+wxBitmap DropDownList::slider_left;
+wxBitmap DropDownList::slider_right;
+wxBitmap DropDownList::slider_center;
+wxBitmap DropDownList::slider_tick;
// ----------------------------------------------------------------------------- : DropDownHider
@@ -91,7 +97,7 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) {
onShow();
// find selection
selected_item = selection();
- // width
+ // determine item width
size_t count = itemCount();
if (item_size.width == 100) { // not initialized
wxClientDC dc(this);
@@ -102,16 +108,19 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) {
item_size.width = max(item_size.width, text_width + icon_size.width + 14); // 14 = room for popup arrow + padding
}
}
- // height
- int line_count = 0;
- for (size_t i = 0 ; i < count ; ++i) if (lineBelow(i)) line_count += 1;
- // size
+ // determine dropdown size
RealSize border_size(2,2); // GetClientSize() - GetSize(), assume 1px borders
- RealSize size(
- item_size.width + marginW * 2,
- item_size.height * count + marginH * 2 + line_count
- );
- // placement
+ RealSize size;
+ if (is_slider) {
+ size.height = 70 + marginH * 2;
+ size.width = min(1000.0, max(150.0, max(100.0 + count, item_size.width + marginW * 2)));
+ } else {
+ int line_count = 0;
+ for (size_t i = 0; i < count; ++i) if (lineBelow(i)) line_count += 1;
+ size.height = item_size.height * count + marginH * 2 + line_count;
+ size.width = item_size.width + marginW * 2;
+ }
+ // determine placement
int parent_height = 0;
if (!in_place && viewer) {
// Position the drop down list below the editor control (based on the style)
@@ -302,17 +311,48 @@ void DropDownList::onPaint(wxPaintEvent&) {
void DropDownList::draw(DC& dc) {
// Size
wxSize cs = dc.GetSize();
+ size_t count = itemCount();
// Draw background & frame
- dc.SetPen (*wxTRANSPARENT_PEN);
+ dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.DrawRectangle(0, 0, cs.x, cs.y);
- dc.SetFont(*wxNORMAL_FONT);
- // Draw items
- int y = marginH - visible_start;
- size_t count = itemCount();
- for (size_t i = 0 ; i < count ; ++i) {
- drawItem(dc, y, i);
- y += (int)item_size.height + lineBelow(i);
+ dc.SetFont(*wxNORMAL_FONT);
+ if (is_slider) {
+ // If it's a slider, draw the slider
+ cs = GetClientSize();
+ dc.SetPen(*wxBLACK_PEN);
+ dc.SetFont(wxFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, _("Arial")));
+ int first_text_width;
+ dc.GetTextExtent(capitalize(itemText(0)), &first_text_width, nullptr);
+ int last_text_width;
+ dc.GetTextExtent(capitalize(itemText(count-1)), &last_text_width, nullptr);
+ dc.DrawText(capitalize(itemText(0)), marginW + 4, 14);
+ dc.DrawText(capitalize(itemText(count-1)), cs.x - marginW - 4 - last_text_width, 14);
+
+ int slider_start = first_text_width + marginW + 16;
+ int slider_end = cs.x - (last_text_width + marginW + 16);
+ dc.DrawBitmap(slider_left, slider_start, 14);
+ for (size_t i = slider_start + 19; i < slider_end - 19; i+=19) {
+ dc.DrawBitmap(slider_center, i, 14);
+ }
+ dc.DrawBitmap(slider_right, slider_end - 19, 14);
+
+ int selected_index = selected_item < 0 ? 0 : selected_item;
+ int slider_pos = round((double)selected_index/(count - 1) * (slider_end - slider_start)) + slider_start;
+ dc.DrawBitmap(slider_tick, slider_pos - 7, 9); // -7 cause the bitmap is 15 pixels wide
+
+ int selected_text_width;
+ dc.GetTextExtent(capitalize(itemText(selected_index)), &selected_text_width, nullptr);
+ dc.DrawText(capitalize(itemText(selected_index)), slider_pos - selected_text_width/2, 44);
+
+ dc.SetFont(*wxNORMAL_FONT);
+ } else {
+ // If it's not a slider, draw the list of items
+ int y = marginH - visible_start;
+ for (size_t i = 0; i < count; ++i) {
+ drawItem(dc, y, i);
+ y += (int)item_size.height + lineBelow(i);
+ }
}
}
@@ -384,21 +424,35 @@ void DropDownList::onMotion(wxMouseEvent& ev) {
ev.Skip();
return;
}
- // find selected item
- int startY = marginH - visible_start;
+ // find selected item
size_t count = itemCount();
- for (size_t i = 0 ; i < count ; ++i) {
- int endY = startY + (int)item_size.height;
- if (ev.GetY() >= startY && ev.GetY() < endY) {
- if (itemEnabled(i)) {
- showSubMenu(i, startY);
+ if (is_slider) {
+ int first_text_width;
+ GetTextExtent(capitalize(itemText(0)), &first_text_width, nullptr);
+ int last_text_width;
+ GetTextExtent(capitalize(itemText(count - 1)), &last_text_width, nullptr);
+ int slider_start = first_text_width + marginW + 16;
+ int slider_end = cs.x - (last_text_width + marginW + 16);
+ int slider_pos = ev.GetX();
+ if (slider_pos < slider_start) slider_pos = slider_start;
+ if (slider_pos > slider_end) slider_pos = slider_end;
+ int selected_item = round(((double)(slider_pos - slider_start)) / (slider_end - slider_start) * (count - 1));
+ selectItem(selected_item);
+ } else {
+ int startY = marginH - visible_start;
+ for (size_t i = 0; i < count; ++i) {
+ int endY = startY + (int)item_size.height;
+ if (ev.GetY() >= startY && ev.GetY() < endY) {
+ if (itemEnabled(i)) {
+ showSubMenu(i, startY);
+ }
+ selectItem(i);
+ return;
}
- selectItem(i);
- return;
+ startY = endY + lineBelow(i);
}
- startY = endY + lineBelow(i);
+ hideSubMenu();
}
- hideSubMenu();
}
void DropDownList::onMouseLeave(wxMouseEvent& ev) {
@@ -516,4 +570,4 @@ BEGIN_EVENT_TABLE(DropDownList,wxPopupWindow)
EVT_LEAVE_WINDOW (DropDownList::onMouseLeave)
EVT_MOUSEWHEEL (DropDownList::onMouseWheel)
EVT_SCROLLWIN (DropDownList::onScroll)
-END_EVENT_TABLE ()
+END_EVENT_TABLE ()
diff --git a/src/gui/drop_down_list.hpp b/src/gui/drop_down_list.hpp
index 0ab84828..49810267 100644
--- a/src/gui/drop_down_list.hpp
+++ b/src/gui/drop_down_list.hpp
@@ -48,7 +48,10 @@ protected:
virtual void onHide() {}
inline bool isRoot() { return parent_menu == nullptr; }
-
+
+ /// Should the list of choices be displayed as a slider (if all choices are numbers)
+ bool is_slider = false;
+
// --------------------------------------------------- : Selection
static const size_t NO_SELECTION = (size_t)-1;
@@ -79,7 +82,13 @@ protected:
static const int marginW = 0;
static const int marginH = 0;
-
+
+ static bool slider_loaded;
+ static wxBitmap slider_left;
+ static wxBitmap slider_right;
+ static wxBitmap slider_center;
+ static wxBitmap slider_tick;
+
// may be changed by derived class
int text_offset; ///< Vertical distance between top of item and text
RealSize item_size; ///< Size of an item;
diff --git a/src/gui/value/choice.cpp b/src/gui/value/choice.cpp
index 732f4b29..3e0377ab 100644
--- a/src/gui/value/choice.cpp
+++ b/src/gui/value/choice.cpp
@@ -200,7 +200,29 @@ END_EVENT_TABLE()
DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group)
: DropDownChoiceListBase(parent, is_submenu, cve, group)
-{}
+{
+ // determine if slider
+ size_t count = itemCount();
+ if (count > 2) {
+ int value;
+ try {
+ value = std::stoi(itemText(0).ToStdString());
+ value = std::stoi(itemText(1).ToStdString());
+ value = std::stoi(itemText(count - 1).ToStdString());
+ // the choices are numbers, use a slider
+ is_slider = true;
+ // load slider images if needed
+ if (!slider_loaded) {
+ slider_loaded = true;
+ slider_left = load_resource_image(_("slider_left"));
+ slider_right = load_resource_image(_("slider_right"));
+ slider_center = load_resource_image(_("slider_center"));
+ slider_tick = load_resource_image(_("slider_tick"));
+ }
+ }
+ catch (...) {}
+ }
+}
void DropDownChoiceList::onShow() {
DropDownChoiceListBase::onShow();