Commit a094c52d authored by twanvl's avatar twanvl

implemented choice editor with drop down list, todo: submenus

parent b1ffd63f
......@@ -38,6 +38,9 @@ IMPLEMENT_REFLECTION(ChoiceField) {
REFLECT_N("default", default_script);
REFLECT(initial);
REFLECT(default_name);
if (tag.reading()) {
choices->initIds();
}
}
// ----------------------------------------------------------------------------- : ChoiceField::Choice
......
......@@ -62,6 +62,8 @@ DropDownList::DropDownList(Window* parent, bool is_submenu, ValueViewer* viewer)
, hider(is_submenu ? nullptr : new DropDownHider(*this))
, viewer(viewer)
, item_size(100,1)
, icon_size(0,0)
, text_offset(0)
{
// determine item height
wxClientDC dc(this);
......@@ -79,10 +81,21 @@ void DropDownList::show(bool in_place, wxPoint pos) {
if (IsShown()) return;
// find selection
selected_item = selection();
// fix size & position
int line_count = 0;
// width
size_t count = itemCount();
if (item_size.width == 100) { // not initialized
wxClientDC dc(this);
dc.SetFont(*wxNORMAL_FONT);
for (size_t i = 0 ; i < count ; ++i) {
int text_width;
dc.GetTextExtent(capitalize(itemText(i)), &text_width, 0);
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
RealSize size(
item_size.width + marginW * 2,
item_size.height * count + marginH * 2 + line_count
......@@ -165,7 +178,7 @@ bool DropDownList::showSubMenu() {
}
}
bool DropDownList::showSubMenu(size_t item, int y) {
DropDownList* sub_menu = item == NO_SELECTION ? nullptr : popup(item);
DropDownList* sub_menu = item == NO_SELECTION ? nullptr : submenu(item);
if (sub_menu == open_sub_menu) return sub_menu; // no change
hideSubMenu();
open_sub_menu = sub_menu;
......@@ -243,7 +256,7 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) {
drawIcon(dc, marginW, y, item, item == selected_item);
dc.DrawText(capitalize(itemText(item)), marginW + icon_size.width, y + text_offset);
// draw popup icon
if (popup(item)) {
if (submenu(item)) {
draw_menu_arrow(this, dc, wxRect(marginW, y, item_size.width, item_size.height), item == selected_item);
}
// draw line below
......@@ -262,7 +275,7 @@ void DropDownList::onLeftDown(wxMouseEvent&) {
void DropDownList::onLeftUp(wxMouseEvent&) {
if (mouse_down) {
// don't hide if there is a child menu
if (selected_item != NO_SELECTION && popup(selected_item)) return;
if (selected_item != NO_SELECTION && submenu(selected_item)) return;
hide(true);
}
}
......
......@@ -56,11 +56,11 @@ class DropDownList : public wxPopupWindow {
/// Draw an icon at the specified location
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const = 0;
/// Is there a line below an item?
virtual bool lineBelow(size_t item) const { return false; }
virtual bool lineBelow(size_t item) const { return false; }
/// Should the item be highlighted?
virtual bool highlightItem(size_t item) const { return false; }
// An extra menu that pops up from an item, or null if there is no popup menu
virtual DropDownList* popup(size_t item) const { return nullptr; }
virtual bool highlightItem(size_t item) const { return false; }
// An extra submenu that pops up from an item, or null if there is no popup menu
virtual DropDownList* submenu(size_t item) const { return nullptr; }
// --------------------------------------------------- : Layout
......
......@@ -7,7 +7,108 @@
// ----------------------------------------------------------------------------- : Includes
#include <gui/value/choice.hpp>
#include <gui/util.hpp>
#include <data/action/value.hpp>
// ----------------------------------------------------------------------------- :
DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP);
IMPLEMENT_VALUE_EDITOR(Choice) {}
// ----------------------------------------------------------------------------- : DropDownChoiceList
DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group)
: DropDownList(parent, is_submenu, is_submenu ? nullptr : &cve)
, group(group)
, cve(cve)
{}
size_t DropDownChoiceList::itemCount() const {
return group->choices.size() + hasDefault();
}
ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const {
if (isGroupDefault(item)) {
return group;
} else {
return group->choices[item - hasDefault()];
}
}
String DropDownChoiceList::itemText(size_t item) const {
if (isFieldDefault(item)) {
return field().default_name;
} else {
ChoiceField::ChoiceP choice = getChoice(item);
return choice->name;
}
}
bool DropDownChoiceList::lineBelow(size_t item) const {
return isDefault(item);
}
DropDownList* DropDownChoiceList::submenu(size_t item) {
if (isDefault(item)) return nullptr;
item -= hasDefault();
if (item < submenus.size()) submenus.resize(item + 1);
if (submenus[item]) return submenus[item].get();
ChoiceField::ChoiceP choice = getChoice(item);
if (choice->isGroup()) {
// create submenu
submenus[item].reset(new DropDownChoiceList(GetParent(), true, cve, choice));
}
return submenus[item].get();
}
void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
// TODO
}
void DropDownChoiceList::select(size_t item) {
if (isFieldDefault(item)) {
cve.change( Defaultable<String>() );
} else {
ChoiceField::ChoiceP choice = getChoice(item);
cve.change( field().choices->choiceName(choice->first_id) );
}
}
size_t DropDownChoiceList::selection() const {
if (hasFieldDefault() && cve.value().value.isDefault()) {
return 0;
}
size_t i = hasDefault();
int id = field().choices->choiceId(cve.value().value());
FOR_EACH(c, group->choices) {
if (id >= c->first_id && id < c->lastId()) {
return i;
}
i++;
}
return NO_SELECTION;
}
// ----------------------------------------------------------------------------- : ChoiceValueEditor
IMPLEMENT_VALUE_EDITOR(Choice)
, drop_down(new DropDownChoiceList(&editor(), false, *this, field().choices))
{}
void ChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) {
drop_down->onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook());
}
void ChoiceValueEditor::onChar(wxKeyEvent& ev) {
drop_down->onCharInParent(ev);
}
void ChoiceValueEditor::onLoseFocus() {
drop_down->hide(false);
}
void ChoiceValueEditor::drawSelection(RotatedDC& dc) {
if (nativeLook()) {
draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown());
}
}
void ChoiceValueEditor::determineSize() {
style().height = max(style().height(), 16.);
}
void ChoiceValueEditor::change(const Defaultable<String>& c) {
getSet().actions.add(value_action(static_pointer_cast<ChoiceValue>(valueP), c));
}
......@@ -11,14 +11,66 @@
#include <util/prec.hpp>
#include <gui/value/editor.hpp>
#include <gui/drop_down_list.hpp>
#include <render/value/choice.hpp>
DECLARE_POINTER_TYPE(DropDownList);
// ----------------------------------------------------------------------------- : ChoiceValueEditor
/// An editor 'control' for editing ChoiceValues
class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor {
public:
DECLARE_VALUE_EDITOR(Choice);
// --------------------------------------------------- : Events
virtual void onLeftDown(const RealPoint& pos, wxMouseEvent& ev);
virtual void onChar(wxKeyEvent& ev);
virtual void onLoseFocus();
virtual void drawSelection(RotatedDC& dc);
virtual void determineSize();
private:
DropDownListP drop_down;
friend class DropDownChoiceList;
/// Change the choice
void change(const Defaultable<String>& c);
};
// ----------------------------------------------------------------------------- : DropDownChoiceList
// A drop down list of color choices
class DropDownChoiceList : public DropDownList {
public:
DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group);
protected:
virtual size_t itemCount() const;
virtual bool lineBelow(size_t item) const;
virtual String itemText(size_t item) const;
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
virtual DropDownList* submenu(size_t item);
virtual void select(size_t item);
virtual size_t selection() const;
private:
ChoiceValueEditor& cve;
ChoiceField::ChoiceP group; ///< Group this menu shows
vector<DropDownListP> submenus;
inline const ChoiceField& field() const { return cve.field(); }
inline bool hasFieldDefault() const { return group == field().choices && field().default_script; }
inline bool hasGroupDefault() const { return group->hasDefault(); }
inline bool hasDefault() const { return hasFieldDefault() || hasGroupDefault(); }
inline bool isFieldDefault(size_t item) const { return item == 0 && hasFieldDefault(); }
inline bool isGroupDefault(size_t item) const { return item == 0 && hasGroupDefault(); }
inline bool isDefault (size_t item) const { return item == 0 && hasDefault(); }
// Find an item in the group of choices
ChoiceField::ChoiceP getChoice(size_t item) const;
};
// ----------------------------------------------------------------------------- : EOF
......
......@@ -35,9 +35,15 @@ class DropDownColorList : public DropDownList {
mutable Color default_color;
inline const ColorField& field() const { return cve.field(); }
// // default, custom item
// default, custom item
bool hasDefault() const { return field().default_script; }
bool hasCustom() const { return field().allow_custom; }
bool isDefault(size_t item) const {
return item == 0 && hasDefault();
}
bool isCustom(size_t item) const {
return item == itemCount() - 1 && hasCustom();
}
};
......@@ -56,13 +62,12 @@ size_t DropDownColorList::itemCount() const {
return cve.field().choices.size() + hasDefault() + hasCustom();
}
bool DropDownColorList::lineBelow(size_t item) const {
return (item == 0 && hasDefault()) // below default item
|| (item == itemCount() - 2 && hasCustom()); // above custom item
return isDefault(item) || isCustom(item + 1); // below default item, above custom item
}
String DropDownColorList::itemText(size_t item) const {
if (item == 0 && hasDefault()) {
if (isDefault(item)) {
return field().default_name;
} else if (item == itemCount()-1 && hasCustom()) {
} else if (isCustom(item)) {
return _("Custom...");
} else {
return field().choices[item - hasDefault()]->name;
......@@ -71,9 +76,9 @@ String DropDownColorList::itemText(size_t item) const {
void DropDownColorList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
Color col;
if (item == 0 && hasDefault()) { // default
if (isDefault(item)) {
col = default_color;
} else if (item == itemCount()-1 && hasCustom()) { // custom color
} else if (isCustom(item)) {
col = cve.value().value();
} else {
col = field().choices[item - hasDefault()]->color;
......@@ -84,6 +89,7 @@ void DropDownColorList::drawIcon(DC& dc, int x, int y, size_t item, bool selecte
dc.DrawRectangle(x+1, y+1, icon_size.width-2, item_size.height-2);
}
size_t DropDownColorList::selection() const {
// find selected color
size_t selection = hasCustom() ? itemCount() - 1 : NO_SELECTION;
......@@ -107,9 +113,9 @@ size_t DropDownColorList::selection() const {
return selection;
}
void DropDownColorList::select(size_t item) {
if (item == 0 && hasDefault()) {
if (isDefault(item)) {
cve.change( Defaultable<Color>());
} else if (item == itemCount() - 1 && hasCustom()) {
} else if (isCustom(item)) {
cve.changeCustom();
} else {
cve.change(field().choices[item - hasDefault()]->color);
......
......@@ -13,6 +13,7 @@
#include <data/card.hpp>
#include <data/field.hpp>
#include <data/settings.hpp>
#include <data/action/value.hpp>
DECLARE_TYPEOF_COLLECTION(ValueViewerP);
typedef IndexMap<FieldP,StyleP> IndexMap_FieldP_StyleP;
......@@ -116,6 +117,15 @@ ValueViewerP DataViewer::makeViewer(const StyleP& style) {
return style->makeViewer(*this, style);
}
void DataViewer::onAction(const Action&, bool undone) {
// TODO
void DataViewer::onAction(const Action& action, bool undone) {
TYPE_CASE(action, ValueAction) {
FOR_EACH(v, viewers) {
if (v->getValue() == action.valueP) {
// refresh the viewer
v->onAction(action, undone);
onChange();
return;
}
}
}
}
......@@ -36,6 +36,8 @@ class ValueViewer {
inline const FieldP& getField() const { return styleP->fieldP; }
/// Return the associated style
inline const StyleP& getStyle() const { return styleP; }
/// Return the associated value
inline const ValueP& getValue() const { return valueP; }
// Draw this value
virtual void draw(RotatedDC& dc) = 0;
......
......@@ -143,6 +143,8 @@ template <> void Reader::handle(ScriptableImage& s) {
if (starts_with(s.script.unparsed, _("script:"))) {
s.script.unparsed = s.script.unparsed.substr(7);
s.script.parse(*this);
} else if (s.script.unparsed.find_first_of('{') != String.npos) {
s.script.parse(*this, true);
} else {
// script is a constant function
s.script.script = new_intrusive<Script>();
......
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