Commit b534a0c1 authored by twanvl's avatar twanvl

MultipleChoiceValueEditor: implemented drop down list

parent 62b3335a
...@@ -19,7 +19,7 @@ DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP); ...@@ -19,7 +19,7 @@ DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP);
class ChoiceThumbnailRequest : public ThumbnailRequest { class ChoiceThumbnailRequest : public ThumbnailRequest {
public: public:
ChoiceThumbnailRequest(ChoiceValueEditor* cve, int id, bool from_disk); ChoiceThumbnailRequest(ValueViewer* cve, int id, bool from_disk);
virtual Image generate(); virtual Image generate();
virtual void store(const Image&); virtual void store(const Image&);
private: private:
...@@ -27,10 +27,10 @@ class ChoiceThumbnailRequest : public ThumbnailRequest { ...@@ -27,10 +27,10 @@ class ChoiceThumbnailRequest : public ThumbnailRequest {
int id; int id;
}; };
ChoiceThumbnailRequest::ChoiceThumbnailRequest(ChoiceValueEditor* cve, int id, bool from_disk) ChoiceThumbnailRequest::ChoiceThumbnailRequest(ValueViewer* cve, int id, bool from_disk)
: ThumbnailRequest( : ThumbnailRequest(
cve, cve,
cve->viewer.stylesheet->name() + _("/") + cve->field().name + _("/") << id, cve->viewer.stylesheet->name() + _("/") + cve->getField()->name + _("/") << id,
from_disk ? cve->viewer.stylesheet->lastModified() from_disk ? cve->viewer.stylesheet->lastModified()
: wxDateTime::Now() : wxDateTime::Now()
) )
...@@ -82,9 +82,10 @@ void ChoiceThumbnailRequest::store(const Image& img) { ...@@ -82,9 +82,10 @@ void ChoiceThumbnailRequest::store(const Image& img) {
} }
} }
// ----------------------------------------------------------------------------- : DropDownChoiceList // ----------------------------------------------------------------------------- : DropDownChoiceListBase
DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group) DropDownChoiceListBase::DropDownChoiceListBase
(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group)
: DropDownList(parent, is_submenu, is_submenu ? nullptr : &cve) : DropDownList(parent, is_submenu, is_submenu ? nullptr : &cve)
, cve(cve) , cve(cve)
, group(group) , group(group)
...@@ -94,11 +95,11 @@ DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceVa ...@@ -94,11 +95,11 @@ DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceVa
item_size.height = max(16., item_size.height); item_size.height = max(16., item_size.height);
} }
size_t DropDownChoiceList::itemCount() const { size_t DropDownChoiceListBase::itemCount() const {
return group->choices.size() + hasDefault(); return group->choices.size() + hasDefault();
} }
ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const { ChoiceField::ChoiceP DropDownChoiceListBase::getChoice(size_t item) const {
if (isGroupDefault(item)) { if (isGroupDefault(item)) {
return group; return group;
} else { } else {
...@@ -106,7 +107,7 @@ ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const { ...@@ -106,7 +107,7 @@ ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const {
} }
} }
String DropDownChoiceList::itemText(size_t item) const { String DropDownChoiceListBase::itemText(size_t item) const {
if (isFieldDefault(item)) { if (isFieldDefault(item)) {
return field().default_name; return field().default_name;
} else if (isGroupDefault(item)) { } else if (isGroupDefault(item)) {
...@@ -116,10 +117,10 @@ String DropDownChoiceList::itemText(size_t item) const { ...@@ -116,10 +117,10 @@ String DropDownChoiceList::itemText(size_t item) const {
return choice->name; return choice->name;
} }
} }
bool DropDownChoiceList::lineBelow(size_t item) const { bool DropDownChoiceListBase::lineBelow(size_t item) const {
return isDefault(item); return isDefault(item);
} }
DropDownList* DropDownChoiceList::submenu(size_t item) const { DropDownList* DropDownChoiceListBase::submenu(size_t item) const {
if (isDefault(item)) return nullptr; if (isDefault(item)) return nullptr;
item -= hasDefault(); item -= hasDefault();
if (item >= submenus.size()) submenus.resize(item + 1); if (item >= submenus.size()) submenus.resize(item + 1);
...@@ -127,14 +128,14 @@ DropDownList* DropDownChoiceList::submenu(size_t item) const { ...@@ -127,14 +128,14 @@ DropDownList* DropDownChoiceList::submenu(size_t item) const {
ChoiceField::ChoiceP choice = group->choices[item]; ChoiceField::ChoiceP choice = group->choices[item];
if (choice->isGroup()) { if (choice->isGroup()) {
// create submenu // create submenu
submenus[item].reset(new DropDownChoiceList(const_cast<DropDownChoiceList*>(this), true, cve, choice)); submenus[item].reset(createSubMenu(choice));
} }
return submenus[item].get(); return submenus[item].get();
} }
void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { void DropDownChoiceListBase::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
// imagelist to use // imagelist to use
wxImageList* il = cve.style().thumbnails; wxImageList* il = style().thumbnails;
assert(il); assert(il);
// find the image for the item // find the image for the item
int image_id; int image_id;
...@@ -149,29 +150,68 @@ void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool select ...@@ -149,29 +150,68 @@ void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool select
} }
} }
void DropDownChoiceListBase::generateThumbnailImages() {
if (!isRoot()) return;
if (!style().thumbnails) {
style().thumbnails = new wxImageList(16,16);
}
int image_count = style().thumbnails->GetImageCount();
int end = group->lastId();
Context& ctx = cve.viewer.getContext();
for (int i = 0 ; i < end ; ++i) {
String name = cannocial_name_form(group->choiceName(i));
ScriptableImage& img = style().choice_images[name];
bool up_to_date = img.upToDate(ctx, style().thumbnail_age);
if (i >= image_count || !up_to_date) {
// TODO : handle the case where image i was previously skipped
// request this thumbnail
thumbnail_thread.request( new_shared3<ChoiceThumbnailRequest>(&cve, i, up_to_date && !style().invalidated_images) );
}
}
style().thumbnail_age.update();
}
void DropDownChoiceListBase::onIdle(wxIdleEvent& ev) {
if (!isRoot()) return;
if (thumbnail_thread.done(&cve)) {
Refresh(false);
}
}
BEGIN_EVENT_TABLE(DropDownChoiceListBase, DropDownList)
EVT_IDLE(DropDownChoiceListBase::onIdle)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------- : DropDownChoiceList
DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group)
: DropDownChoiceListBase(parent, is_submenu, cve, group)
{}
void DropDownChoiceList::select(size_t item) { void DropDownChoiceList::select(size_t item) {
if (isFieldDefault(item)) { if (isFieldDefault(item)) {
cve.change( Defaultable<String>() ); dynamic_cast<ChoiceValueEditor&>(cve).change( Defaultable<String>() );
} else { } else {
ChoiceField::ChoiceP choice = getChoice(item); ChoiceField::ChoiceP choice = getChoice(item);
cve.change( field().choices->choiceName(choice->first_id) ); dynamic_cast<ChoiceValueEditor&>(cve).change( field().choices->choiceName(choice->first_id) );
} }
} }
size_t DropDownChoiceList::selection() const { size_t DropDownChoiceList::selection() const {
// we need thumbnail images soon // we need thumbnail images soon
const_cast<DropDownChoiceList*>(this)->generateThumbnailImages(); const_cast<DropDownChoiceList*>(this)->generateThumbnailImages();
// selected item // selected item
int id = field().choices->choiceId(cve.value().value()); const Defaultable<String>& value = dynamic_cast<ChoiceValueEditor&>(cve).value().value();
int id = field().choices->choiceId(value);
// id of default item // id of default item
if (hasFieldDefault()) { if (hasFieldDefault()) {
if (cve.value().value.isDefault()) { if (value.isDefault()) {
// default is selected // default is selected
default_id = id; default_id = id;
return 0; return 0;
} else { } else {
// run default script to find out what the default choice would be // run default script to find out what the default choice would be
String default_choice = *cve.field().default_script.invoke( cve.viewer.getContext() ); String default_choice = *field().default_script.invoke( cve.viewer.getContext() );
default_id = group->choiceId(default_choice); default_id = group->choiceId(default_choice);
} }
} }
...@@ -186,38 +226,10 @@ size_t DropDownChoiceList::selection() const { ...@@ -186,38 +226,10 @@ size_t DropDownChoiceList::selection() const {
return NO_SELECTION; return NO_SELECTION;
} }
void DropDownChoiceList::generateThumbnailImages() { DropDownList* DropDownChoiceList::createSubMenu(ChoiceField::ChoiceP group) const {
if (!isRoot()) return; return new DropDownChoiceList(const_cast<DropDownChoiceList*>(this), true, cve, group);
if (!cve.style().thumbnails) {
cve.style().thumbnails = new wxImageList(16,16);
}
int image_count = cve.style().thumbnails->GetImageCount();
int end = group->lastId();
Context& ctx = cve.viewer.getContext();
for (int i = 0 ; i < end ; ++i) {
String name = cannocial_name_form(group->choiceName(i));
ScriptableImage& img = cve.style().choice_images[name];
bool up_to_date = img.upToDate(ctx, cve.style().thumbnail_age);
if (i >= image_count || !up_to_date) {
// TODO : handle the case where image i was previously skipped
// request this thumbnail
thumbnail_thread.request( new_shared3<ChoiceThumbnailRequest>(&cve, i, up_to_date && !cve.style().invalidated_images) );
}
}
cve.style().thumbnail_age.update();
} }
void DropDownChoiceList::onIdle(wxIdleEvent& ev) {
if (!isRoot()) return;
if (thumbnail_thread.done(&cve)) {
Refresh(false);
}
}
BEGIN_EVENT_TABLE(DropDownChoiceList, DropDownList)
EVT_IDLE(DropDownChoiceList::onIdle)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------- : ChoiceValueEditor // ----------------------------------------------------------------------------- : ChoiceValueEditor
IMPLEMENT_VALUE_EDITOR(Choice) IMPLEMENT_VALUE_EDITOR(Choice)
...@@ -230,7 +242,7 @@ ChoiceValueEditor::~ChoiceValueEditor() { ...@@ -230,7 +242,7 @@ ChoiceValueEditor::~ChoiceValueEditor() {
bool ChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { bool ChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) {
//HACK TODO REMOVEME //HACK TODO REMOVEME
thumbnail_thread.abortAll(); //thumbnail_thread.abortAll();
return drop_down->onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook()); return drop_down->onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook());
} }
bool ChoiceValueEditor::onChar(wxKeyEvent& ev) { bool ChoiceValueEditor::onChar(wxKeyEvent& ev) {
......
...@@ -42,10 +42,11 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor { ...@@ -42,10 +42,11 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor {
// ----------------------------------------------------------------------------- : DropDownChoiceList // ----------------------------------------------------------------------------- : DropDownChoiceList
// A drop down list of color choices /// A drop down list of choices
class DropDownChoiceList : public DropDownList { /** This is a base class, used for single and multiple choice fields */
class DropDownChoiceListBase : public DropDownList {
public: public:
DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group); DropDownChoiceListBase(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
protected: protected:
virtual size_t itemCount() const; virtual size_t itemCount() const;
...@@ -54,23 +55,24 @@ class DropDownChoiceList : public DropDownList { ...@@ -54,23 +55,24 @@ class DropDownChoiceList : public DropDownList {
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
virtual DropDownList* submenu(size_t item) const; virtual DropDownList* submenu(size_t item) const;
virtual void select(size_t item); protected:
virtual size_t selection() const; virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const = 0;
private: private:
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
ChoiceValueEditor& cve; ValueViewer& cve; ///< Editor this list belongs to
ChoiceField::ChoiceP group; ///< Group this menu shows ChoiceField::ChoiceP group; ///< Group this menu shows
mutable vector<DropDownListP> submenus; mutable vector<DropDownListP> submenus;
mutable int default_id; ///< Item id for the default item (if !hasFieldDefault()) this is undefined) mutable int default_id; ///< Item id for the default item (if !hasFieldDefault()) this is undefined)
inline const ChoiceField& field() const { return cve.field(); } inline ChoiceField& field() const { return static_cast<ChoiceField&>(*cve.getField()); }
inline ChoiceStyle& style() const { return static_cast<ChoiceStyle&>(*cve.getStyle()); }
inline bool isRoot() const { return group == field().choices; } inline bool isRoot() const { return group == field().choices; }
inline bool hasFieldDefault() const { return isRoot() && field().default_script; } inline bool hasFieldDefault() const { return isRoot() && field().default_script; }
inline bool hasGroupDefault() const { return group->hasDefault(); } inline bool hasGroupDefault() const { return group->hasDefault(); }
inline bool hasDefault() const { return hasFieldDefault() || hasGroupDefault(); } virtual bool hasDefault() const { return hasFieldDefault() || hasGroupDefault(); }
inline bool isFieldDefault(size_t item) const { return item == 0 && hasFieldDefault(); } inline bool isFieldDefault(size_t item) const { return item == 0 && hasFieldDefault(); }
inline bool isGroupDefault(size_t item) const { return item == 0 && hasGroupDefault(); } inline bool isGroupDefault(size_t item) const { return item == 0 && hasGroupDefault(); }
inline bool isDefault (size_t item) const { return item == 0 && hasDefault(); } inline bool isDefault (size_t item) const { return item == 0 && hasDefault(); }
...@@ -82,5 +84,18 @@ class DropDownChoiceList : public DropDownList { ...@@ -82,5 +84,18 @@ class DropDownChoiceList : public DropDownList {
void onIdle(wxIdleEvent&); void onIdle(wxIdleEvent&);
}; };
// ----------------------------------------------------------------------------- : DropDownChoiceList
/// A drop down list of color choices
class DropDownChoiceList : public DropDownChoiceListBase {
public:
DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
protected:
virtual void select(size_t item);
virtual size_t selection() const;
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
};
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -7,12 +7,83 @@ ...@@ -7,12 +7,83 @@
// ----------------------------------------------------------------------------- : Includes // ----------------------------------------------------------------------------- : Includes
#include <gui/value/multiple_choice.hpp> #include <gui/value/multiple_choice.hpp>
#include <gui/thumbnail_thread.hpp>
#include <gui/util.hpp>
#include <data/action/value.hpp> #include <data/action/value.hpp>
// ----------------------------------------------------------------------------- : DropDownMultipleChoiceList
/// A drop down list of color choices
class DropDownMultipleChoiceList : public DropDownChoiceListBase {
public:
DropDownMultipleChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
protected:
virtual void select(size_t item);
virtual size_t selection() const;
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
};
DropDownMultipleChoiceList::DropDownMultipleChoiceList
(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group)
: DropDownChoiceListBase(parent, is_submenu, cve, group)
{
icon_size.width += 16;
}
void DropDownMultipleChoiceList::select(size_t item) {
if (isFieldDefault(item)) {
// should not happen
} else {
ChoiceField::ChoiceP choice = getChoice(item);
dynamic_cast<MultipleChoiceValueEditor&>(cve).toggle(choice->first_id);
}
}
void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
// is this item active?
bool active = false;
if (!isFieldDefault(item)) {
ChoiceField::ChoiceP choice = getChoice(item);
active = dynamic_cast<MultipleChoiceValueEditor&>(cve).active[choice->first_id];
}
// 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);
draw_checkbox(nullptr, dc, rect, active);
// draw icon
DropDownChoiceListBase::drawIcon(dc, x + 16, y, item, selected);
}
size_t DropDownMultipleChoiceList::selection() const {
// we need thumbnail images soon
const_cast<DropDownMultipleChoiceList*>(this)->generateThumbnailImages();
// we don't know the selection
return NO_SELECTION;
}
DropDownList* DropDownMultipleChoiceList::createSubMenu(ChoiceField::ChoiceP group) const {
return new DropDownMultipleChoiceList(const_cast<DropDownMultipleChoiceList*>(this), true, cve, group);
}
// ----------------------------------------------------------------------------- : MultipleChoiceValueEditor // ----------------------------------------------------------------------------- : MultipleChoiceValueEditor
IMPLEMENT_VALUE_EDITOR(MultipleChoice) {} IMPLEMENT_VALUE_EDITOR(MultipleChoice) {}
MultipleChoiceValueEditor::~MultipleChoiceValueEditor() {
thumbnail_thread.abort(this);
}
DropDownList& MultipleChoiceValueEditor::initDropDown() {
if (!drop_down) {
drop_down.reset(new DropDownMultipleChoiceList(&editor(), false, *this, field().choices));
}
return *drop_down;
}
void MultipleChoiceValueEditor::determineSize(bool force_fit) { void MultipleChoiceValueEditor::determineSize(bool force_fit) {
if (!nativeLook()) return; if (!nativeLook()) return;
// item height // item height
...@@ -24,7 +95,7 @@ void MultipleChoiceValueEditor::determineSize(bool force_fit) { ...@@ -24,7 +95,7 @@ void MultipleChoiceValueEditor::determineSize(bool force_fit) {
bool MultipleChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { bool MultipleChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) {
// find item under cursor // find item under cursor
if (style().render_style && RENDER_CHECKLIST) { if (style().render_style & RENDER_CHECKLIST) {
int id = (pos.y - style().top) / item_height; int id = (pos.y - style().top) / item_height;
int end = field().choices->lastId(); int end = field().choices->lastId();
if (id >= 0 && id < end) { if (id >= 0 && id < end) {
...@@ -32,10 +103,39 @@ bool MultipleChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& e ...@@ -32,10 +103,39 @@ bool MultipleChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& e
return true; return true;
} }
} else { } else {
// TODO // open a drop down menu
return initDropDown().onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook());
} }
return false; return false;
} }
bool MultipleChoiceValueEditor::onChar(wxKeyEvent& ev) {
if (style().render_style & RENDER_CHECKLIST) {
// todo;
return false;
} else {
return initDropDown().onCharInParent(ev);
}
}
void MultipleChoiceValueEditor::onLoseFocus() {
if (drop_down) drop_down->hide(false);
}
void MultipleChoiceValueEditor::onValueChange() {
MultipleChoiceValueViewer::onValueChange();
// determine active values
active.clear();
vector<String> selected;
value().get(selected);
vector<String>::iterator select_it = selected.begin();
// for each choice...
int end = field().choices->lastId();
for (int i = 0 ; i < end ; ++i) {
String choice = field().choices->choiceName(i);
bool is_active = select_it != selected.end() && *select_it == choice;
if (is_active) select_it++;
active.push_back(is_active);
}
}
void MultipleChoiceValueEditor::toggle(int id) { void MultipleChoiceValueEditor::toggle(int id) {
String new_value; String new_value;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <gui/value/editor.hpp> #include <gui/value/editor.hpp>
#include <gui/value/choice.hpp>
#include <render/value/multiple_choice.hpp> #include <render/value/multiple_choice.hpp>
// ----------------------------------------------------------------------------- : MultipleChoiceValueEditor // ----------------------------------------------------------------------------- : MultipleChoiceValueEditor
...@@ -19,10 +20,22 @@ ...@@ -19,10 +20,22 @@
class MultipleChoiceValueEditor : public MultipleChoiceValueViewer, public ValueEditor { class MultipleChoiceValueEditor : public MultipleChoiceValueViewer, public ValueEditor {
public: public:
DECLARE_VALUE_EDITOR(MultipleChoice); DECLARE_VALUE_EDITOR(MultipleChoice);
~MultipleChoiceValueEditor();
virtual void onValueChange();
virtual void determineSize(bool force_fit); virtual void determineSize(bool force_fit);
virtual bool onLeftDown (const RealPoint& pos, wxMouseEvent& ev); virtual bool onLeftDown (const RealPoint& pos, wxMouseEvent& ev);
virtual bool onChar(wxKeyEvent& ev);
virtual void onLoseFocus();
private: private:
DropDownListP drop_down;
vector<int> active; ///< Which choices are active? (note: vector<bool> is evil)
friend class DropDownMultipleChoiceList;
/// Initialize the drop down list
DropDownList& initDropDown();
/// Toggle a choice or on or off /// Toggle a choice or on or off
void toggle(int id); void toggle(int id);
}; };
......
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