GncOptionUIItem from the GncOptionVariant classes to GncOption.

Separating the UI from the data model. Note that the GncOptionVariant
classes still have a GncOptionUIType member to ensure that a
GncOptionUIItem of the right type is attached.
This commit is contained in:
John Ralls 2020-02-11 13:35:27 -08:00
parent 1bea809cec
commit 99c2c5e439
9 changed files with 225 additions and 101 deletions

View File

@ -1,5 +1,5 @@
/********************************************************************\ /********************************************************************\
* gnc-option-impl.hpp -- Application options system * * gnc-option-impl.hpp -- Application options system *
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> * * Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
* * * *
* This program is free software; you can redistribute it and/or * * This program is free software; you can redistribute it and/or *
@ -107,62 +107,12 @@ struct OptionClassifier
std::string m_doc_string; std::string m_doc_string;
}; };
class GncOptionUIItem;
/**
* Holds a pointer to the UI item which will control the option and an enum
* representing the type of the option for dispatch purposes; all of that
* happens in gnucash/gnome-utils/dialog-options and
* gnucash/gnome/business-option-gnome.
*
* This class takes no ownership responsibility, so calling code is responsible
* for ensuring that the UI_Item is alive. For convenience the public
* clear_ui_item function can be used as a weak_ptr's destruction callback to
* ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
*/
class OptionUIItem
{
public:
GncOptionUIType get_ui_type() const { return m_ui_type; }
GncOptionUIItem* const get_ui_item() const {return m_ui_item; }
void clear_ui_item() { m_ui_item = nullptr; }
void set_ui_item(GncOptionUIItem* ui_item)
{
if (m_ui_type == GncOptionUIType::INTERNAL)
{
std::string error{"INTERNAL option, setting the UI item forbidden."};
throw std::logic_error(std::move(error));
}
m_ui_item = ui_item;
}
void make_internal()
{
if (m_ui_item != nullptr)
{
std::string error("Option has a UI Element, can't be INTERNAL.");
throw std::logic_error(std::move(error));
}
m_ui_type = GncOptionUIType::INTERNAL;
}
protected:
OptionUIItem(GncOptionUIType ui_type) :
m_ui_item{nullptr}, m_ui_type{ui_type} {}
OptionUIItem(const OptionUIItem&) = default;
OptionUIItem(OptionUIItem&&) = default;
~OptionUIItem() = default;
OptionUIItem& operator=(const OptionUIItem&) = default;
OptionUIItem& operator=(OptionUIItem&&) = default;
private:
GncOptionUIItem* m_ui_item;
GncOptionUIType m_ui_type;
};
#ifndef SWIG #ifndef SWIG
auto constexpr size_t_max = std::numeric_limits<std::size_t>::max(); auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
#endif #endif
template <typename ValueType> template <typename ValueType>
class GncOptionValue : public OptionClassifier, public OptionUIItem class GncOptionValue : public OptionClassifier
{ {
public: public:
GncOptionValue<ValueType>(const char* section, const char* name, GncOptionValue<ValueType>(const char* section, const char* name,
@ -170,8 +120,7 @@ public:
ValueType value, ValueType value,
GncOptionUIType ui_type = GncOptionUIType::INTERNAL) : GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(ui_type), m_ui_type(ui_type), m_value{value}, m_default_value{value} {}
m_value{value}, m_default_value{value} {}
GncOptionValue<ValueType>(const GncOptionValue<ValueType>&) = default; GncOptionValue<ValueType>(const GncOptionValue<ValueType>&) = default;
GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default; GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default; GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
@ -180,13 +129,16 @@ public:
ValueType get_default_value() const { return m_default_value; } ValueType get_default_value() const { return m_default_value; }
void set_value(ValueType new_value) { m_value = new_value; } void set_value(ValueType new_value) { m_value = new_value; }
bool is_changed() const noexcept { return m_value != m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; }
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private: private:
GncOptionUIType m_ui_type;
ValueType m_value; ValueType m_value;
ValueType m_default_value; ValueType m_default_value;
}; };
template <typename ValueType> template <typename ValueType>
class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem class GncOptionValidatedValue : public OptionClassifier
{ {
public: public:
GncOptionValidatedValue<ValueType>() = delete; GncOptionValidatedValue<ValueType>() = delete;
@ -197,8 +149,8 @@ public:
GncOptionUIType ui_type = GncOptionUIType::INTERNAL GncOptionUIType ui_type = GncOptionUIType::INTERNAL
) : ) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem{ui_type}, m_ui_type{ui_type}, m_value{value}, m_default_value{value},
m_value{value}, m_default_value{value}, m_validator{validator} m_validator{validator}
{ {
if (!this->validate(value)) if (!this->validate(value))
throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
@ -209,7 +161,7 @@ public:
std::function<bool(ValueType)>validator, std::function<bool(ValueType)>validator,
ValueType val_data) : ValueType val_data) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem{GncOptionUIType::INTERNAL}, m_value{value}, m_ui_type{GncOptionUIType::INTERNAL}, m_value{value},
m_default_value{value}, m_validator{validator}, m_validation_data{val_data} m_default_value{value}, m_validator{validator}, m_validation_data{val_data}
{ {
if (!this->validate(value)) if (!this->validate(value))
@ -232,7 +184,10 @@ public:
bool is_changed() const noexcept { return m_value != m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; }
std::ostream& to_scheme(std::ostream&) const; std::ostream& to_scheme(std::ostream&) const;
std::istream& from_scheme(std::istream&); std::istream& from_scheme(std::istream&);
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private: private:
GncOptionUIType m_ui_type;
ValueType m_value; ValueType m_value;
ValueType m_default_value; ValueType m_default_value;
std::function<bool(ValueType)> m_validator; //11 std::function<bool(ValueType)> m_validator; //11
@ -419,8 +374,7 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt)
*/ */
template <typename ValueType> template <typename ValueType>
class GncOptionRangeValue : class GncOptionRangeValue : public OptionClassifier
public OptionClassifier, public OptionUIItem
{ {
public: public:
GncOptionRangeValue<ValueType>(const char* section, const char* name, GncOptionRangeValue<ValueType>(const char* section, const char* name,
@ -428,7 +382,6 @@ public:
ValueType value, ValueType min, ValueType value, ValueType min,
ValueType max, ValueType step) : ValueType max, ValueType step) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(GncOptionUIType::NUMBER_RANGE),
m_value{value >= min && value <= max ? value : min}, m_value{value >= min && value <= max ? value : min},
m_default_value{value >= min && value <= max ? value : min}, m_default_value{value >= min && value <= max ? value : min},
m_min{min}, m_max{max}, m_step{step} {} m_min{min}, m_max{max}, m_step{step} {}
@ -448,7 +401,10 @@ public:
throw std::invalid_argument("Validation failed, value not set."); throw std::invalid_argument("Validation failed, value not set.");
} }
bool is_changed() const noexcept { return m_value != m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; }
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private: private:
GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE;
ValueType m_value; ValueType m_value;
ValueType m_default_value; ValueType m_default_value;
ValueType m_min; ValueType m_min;
@ -471,8 +427,7 @@ using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
* *
*/ */
class GncOptionMultichoiceValue : class GncOptionMultichoiceValue : public OptionClassifier
public OptionClassifier, public OptionUIItem
{ {
public: public:
GncOptionMultichoiceValue(const char* section, const char* name, GncOptionMultichoiceValue(const char* section, const char* name,
@ -481,7 +436,7 @@ public:
GncMultiChoiceOptionChoices&& choices, GncMultiChoiceOptionChoices&& choices,
GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) : GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(ui_type), m_ui_type{ui_type},
m_value{}, m_default_value{}, m_choices{std::move(choices)} { m_value{}, m_default_value{}, m_choices{std::move(choices)} {
if (value) if (value)
{ {
@ -543,6 +498,8 @@ public:
return std::get<2>(m_choices.at(index)); return std::get<2>(m_choices.at(index));
} }
bool is_changed() const noexcept { return m_value != m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; }
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private: private:
std::size_t find_key (const std::string& key) const noexcept std::size_t find_key (const std::string& key) const noexcept
{ {
@ -555,6 +512,7 @@ private:
return size_t_max; return size_t_max;
} }
GncOptionUIType m_ui_type;
std::size_t m_value; std::size_t m_value;
std::size_t m_default_value; std::size_t m_default_value;
GncMultiChoiceOptionChoices m_choices; GncMultiChoiceOptionChoices m_choices;
@ -581,31 +539,28 @@ using GncOptionAccountTypeList = std::vector<GNCAccountType>;
*/ */
class GncOptionAccountValue : class GncOptionAccountValue : public OptionClassifier
public OptionClassifier, public OptionUIItem
{ {
public: public:
GncOptionAccountValue(const char* section, const char* name, GncOptionAccountValue(const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
GncOptionUIType ui_type) : GncOptionUIType ui_type) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {} m_ui_type{ui_type}, m_value{}, m_default_value{}, m_allowed{} {}
GncOptionAccountValue(const char* section, const char* name, GncOptionAccountValue(const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
GncOptionUIType ui_type, GncOptionUIType ui_type,
const GncOptionAccountList& value) : const GncOptionAccountList& value) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(ui_type), m_ui_type{ui_type}, m_value{value},
m_value{value},
m_default_value{std::move(value)}, m_allowed{} {} m_default_value{std::move(value)}, m_allowed{} {}
GncOptionAccountValue(const char* section, const char* name, GncOptionAccountValue(const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
GncOptionUIType ui_type, GncOptionUIType ui_type,
GncOptionAccountTypeList&& allowed) : GncOptionAccountTypeList&& allowed) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(ui_type), m_ui_type{ui_type}, m_value{},
m_value{},
m_default_value{}, m_allowed{std::move(allowed)} {} m_default_value{}, m_allowed{std::move(allowed)} {}
GncOptionAccountValue(const char* section, const char* name, GncOptionAccountValue(const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
@ -613,8 +568,7 @@ public:
const GncOptionAccountList& value, const GncOptionAccountList& value,
GncOptionAccountTypeList&& allowed) : GncOptionAccountTypeList&& allowed) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(ui_type), m_ui_type{ui_type}, m_value{},
m_value{},
m_default_value{}, m_allowed{std::move(allowed)} { m_default_value{}, m_allowed{std::move(allowed)} {
if (!validate(value)) if (!validate(value))
throw std::invalid_argument("Supplied Value not in allowed set."); throw std::invalid_argument("Supplied Value not in allowed set.");
@ -631,7 +585,10 @@ public:
m_value = values; m_value = values;
} }
bool is_changed() const noexcept { return m_value != m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; }
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private: private:
GncOptionUIType m_ui_type;
GncOptionAccountList m_value; GncOptionAccountList m_value;
GncOptionAccountList m_default_value; GncOptionAccountList m_default_value;
GncOptionAccountTypeList m_allowed; GncOptionAccountTypeList m_allowed;
@ -727,13 +684,12 @@ gnc-date-option-absolute-time m_type == DateTyupe::Absolute
gnc-date-option-relative-time m_type != DateTyupe::Absolute gnc-date-option-relative-time m_type != DateTyupe::Absolute
*/ */
class GncOptionDateValue : public OptionClassifier, public OptionUIItem class GncOptionDateValue : public OptionClassifier
{ {
public: public:
GncOptionDateValue(const char* section, const char* name, GncOptionDateValue(const char* section, const char* name,
const char* key, const char* doc_string) : const char* key, const char* doc_string) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(GncOptionUIType::DATE),
m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
m_date{INT64_MAX}, m_date{INT64_MAX},
m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
@ -742,14 +698,12 @@ public:
const char* key, const char* doc_string, const char* key, const char* doc_string,
time64 time) : time64 time) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(GncOptionUIType::DATE),
m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time}, m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time},
m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {} m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {}
GncOptionDateValue(const char* section, const char* name, GncOptionDateValue(const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
const RelativeDatePeriod period) : const RelativeDatePeriod period) :
OptionClassifier{section, name, key, doc_string}, OptionClassifier{section, name, key, doc_string},
OptionUIItem(GncOptionUIType::DATE),
m_period{period}, m_date{INT64_MAX}, m_period{period}, m_date{INT64_MAX},
m_default_period{period}, m_default_date{INT64_MAX} {} m_default_period{period}, m_default_date{INT64_MAX} {}
GncOptionDateValue(const GncOptionDateValue&) = default; GncOptionDateValue(const GncOptionDateValue&) = default;
@ -770,7 +724,10 @@ public:
} }
bool is_changed() const noexcept { return m_period != m_default_period && bool is_changed() const noexcept { return m_period != m_default_period &&
m_date != m_default_date; } m_date != m_default_date; }
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private: private:
GncOptionUIType m_ui_type = GncOptionUIType::DATE;
RelativeDatePeriod m_period; RelativeDatePeriod m_period;
time64 m_date; time64 m_date;
RelativeDatePeriod m_default_period; RelativeDatePeriod m_default_period;

View File

@ -0,0 +1,93 @@
/********************************************************************\
* gnc-option-ui.hpp -- UI association for GncOption *
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
\********************************************************************/
#ifndef GNC_OPTION_UI_HPP_
#define GNC_OPTION_UI_HPP_
#include "gnc-option-uitype.hpp"
template <typename UIType>
class GncUIItem
{
public:
GncUIItem(UIType* widget) : m_widget{widget} {}
UIType* m_widget;
};
class GncUIType;
using OptionUIItem = GncUIItem<GncUIType>;
using OptionSyncFunc = std::function<void(OptionUIItem&, GncOption&)>;
/**
* Holds a pointer to the UI item which will control the option and an enum
* representing the type of the option for dispatch purposes; all of that
* happens in gnucash/gnome-utils/dialog-options and
* gnucash/gnome/business-option-gnome.
*
* This class takes no ownership responsibility, so calling code is responsible
* for ensuring that the UI_Item is alive. For convenience the public
* clear_ui_item function can be used as a weak_ptr's destruction callback to
* ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
*/
class GncOptionUIItem
{
public:
GncOptionUIItem(OptionUIItem&& ui_item, GncOptionUIType type,
OptionSyncFunc to_ui, OptionSyncFunc from_ui) :
m_ui_item{std::move(ui_item)}, m_ui_type{type},
m_set_ui_item_from_option{to_ui}, m_set_option_from_ui_item{from_ui} {}
GncOptionUIItem(GncOptionUIType ui_type) :
m_ui_item{nullptr}, m_ui_type{ui_type} {}
GncOptionUIItem(const GncOptionUIItem&) = default;
GncOptionUIItem(GncOptionUIItem&&) = default;
~GncOptionUIItem() = default;
GncOptionUIItem& operator=(const GncOptionUIItem&) = default;
GncOptionUIItem& operator=(GncOptionUIItem&&) = default;
GncOptionUIType get_ui_type() const { return m_ui_type; }
const OptionUIItem& get_ui_item() const {return m_ui_item; }
void clear_ui_item() { m_ui_item = nullptr; }
void set_ui_item(OptionUIItem&& ui_item)
{
if (m_ui_type == GncOptionUIType::INTERNAL)
{
std::string error{"INTERNAL option, setting the UI item forbidden."};
throw std::logic_error(std::move(error));
}
m_ui_item = std::move(ui_item);
}
void set_ui_item_from_option(GncOption& option)
{
m_set_ui_item_from_option(m_ui_item, option);
}
void set_option_from_ui_item(GncOption& option)
{
m_set_option_from_ui_item(m_ui_item, option);
}
private:
OptionUIItem m_ui_item;
GncOptionUIType m_ui_type;
OptionSyncFunc m_set_ui_item_from_option;
OptionSyncFunc m_set_option_from_ui_item;
};
using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
#endif //GNC_OPTION_UI_HPP__

View File

@ -24,6 +24,14 @@
#include "gnc-option.hpp" #include "gnc-option.hpp"
#include "gnc-option-impl.hpp" #include "gnc-option-impl.hpp"
#include "gnc-option-uitype.hpp" #include "gnc-option-uitype.hpp"
#include "gnc-option-ui.hpp"
static const char* log_module{"gnc.app-utils.gnc-option"};
extern "C"
{
#include <qoflog.h>
}
template <typename ValueType> template <typename ValueType>
GncOption::GncOption(const char* section, const char* name, GncOption::GncOption(const char* section, const char* name,
@ -103,11 +111,21 @@ GncOption::get_docstring() const
} }
void void
GncOption::set_ui_item(GncOptionUIItem* ui_elem) GncOption::set_ui_item(GncOptionUIItemPtr&& ui_item)
{ {
std::visit([ui_elem](auto& option) {
option.set_ui_item(ui_elem); auto opt_ui_type = std::visit([](const auto& option)->GncOptionUIType {
}, *m_option); return option.get_ui_type();
}, *m_option);
if (ui_item->get_ui_type() != opt_ui_type)
{
PERR("Setting option %s:%s UI element failed, mismatched UI types.",
get_section().c_str(), get_name().c_str());
return;
}
m_ui_item = std::move(ui_item);
} }
const GncOptionUIType const GncOptionUIType
@ -118,17 +136,37 @@ GncOption::get_ui_type() const
}, *m_option); }, *m_option);
} }
GncOptionUIItem* const const GncOptionUIItem*
GncOption::get_ui_item() const GncOption::get_ui_item() const
{ {
return std::visit([](const auto& option)->GncOptionUIItem* { return m_ui_item.get();
return option.get_ui_item(); }
}, *m_option);
void
GncOption::set_ui_item_from_option()
{
if (!m_ui_item)
return;
m_ui_item->set_ui_item_from_option(*this);
}
void
GncOption::set_option_from_ui_item()
{
if (!m_ui_item)
return;
m_ui_item->set_option_from_ui_item(*this);
} }
void void
GncOption::make_internal() GncOption::make_internal()
{ {
if (!m_ui_item)
{
PERR("Option %s:%s has a UI Element, can't be INTERNAL.",
get_section().c_str(), get_name().c_str());
return;
}
std::visit([](auto& option) { std::visit([](auto& option) {
option.make_internal(); option.make_internal();
}, *m_option); }, *m_option);

View File

@ -31,6 +31,7 @@
#include "gnc-option-uitype.hpp" #include "gnc-option-uitype.hpp"
class GncOptionUIItem; class GncOptionUIItem;
using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
struct QofInstance_s; struct QofInstance_s;
using QofInstance = QofInstance_s; using QofInstance = QofInstance_s;
template <typename ValueType> class GncOptionValue; template <typename ValueType> class GncOptionValue;
@ -72,9 +73,11 @@ public:
const std::string& get_name() const; const std::string& get_name() const;
const std::string& get_key() const; const std::string& get_key() const;
const std::string& get_docstring() const; const std::string& get_docstring() const;
void set_ui_item(GncOptionUIItem* ui_elem); void set_ui_item(GncOptionUIItemPtr&& ui_elem);
const GncOptionUIType get_ui_type() const; const GncOptionUIType get_ui_type() const;
GncOptionUIItem* const get_ui_item() const; const GncOptionUIItem* get_ui_item() const;
void set_ui_item_from_option();
void set_option_from_ui_item();
void make_internal(); void make_internal();
bool is_changed() const noexcept; bool is_changed() const noexcept;
template <typename ValueType> bool validate(ValueType value) const; template <typename ValueType> bool validate(ValueType value) const;
@ -87,10 +90,11 @@ public:
std::istream& in_stream(std::istream& iss); std::istream& in_stream(std::istream& iss);
std::ostream& to_scheme(std::ostream& oss) const; std::ostream& to_scheme(std::ostream& oss) const;
std::istream& from_scheme(std::istream& iss); std::istream& from_scheme(std::istream& iss);
GncOptionVariantPtr& _get_option() { return m_option; } GncOptionVariant* const _get_option() { return m_option.get(); }
private: private:
inline static const std::string c_empty_string{""}; inline static const std::string c_empty_string{""};
GncOptionVariantPtr m_option; GncOptionVariantPtr m_option;
GncOptionUIItemPtr m_ui_item{nullptr};
}; };
inline std::ostream& inline std::ostream&

View File

@ -27,6 +27,7 @@
#include <kvp-value.hpp> #include <kvp-value.hpp>
#include "gnc-optiondb.hpp" #include "gnc-optiondb.hpp"
#include "gnc-optiondb-impl.hpp" #include "gnc-optiondb-impl.hpp"
#include "gnc-option-ui.hpp"
auto constexpr stream_max = std::numeric_limits<std::streamsize>::max(); auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}

View File

@ -206,4 +206,5 @@ void gnc_register_date_interval_option(const GncOptionDBPtr& db,
const char* section, const char* name, const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
RelativeDatePeriod period); RelativeDatePeriod period);
#endif //GNC_OPTIONDB_HPP_ #endif //GNC_OPTIONDB_HPP_

View File

@ -181,6 +181,7 @@ gnc_option_test_book_destroy(QofBook* book)
%ignore GncOptionDateValue::operator=(GncOptionDateValue&&); %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
%ignore operator<<(std::ostream&, const GncOption&); %ignore operator<<(std::ostream&, const GncOption&);
%ignore operator>>(std::istream&, GncOption&); %ignore operator>>(std::istream&, GncOption&);
%ignore GncOption::_get_option();
%rename(absolute) RelativeDatePeriod::ABSOLUTE; %rename(absolute) RelativeDatePeriod::ABSOLUTE;
%rename(today) RelativeDatePeriod::TODAY; %rename(today) RelativeDatePeriod::TODAY;
@ -305,6 +306,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
%include "gnc-option-impl.hpp" %include "gnc-option-impl.hpp"
%include "gnc-optiondb.hpp" %include "gnc-optiondb.hpp"
%include "gnc-optiondb-impl.hpp" %include "gnc-optiondb-impl.hpp"
%inline %{
#include "gnc-option-ui.hpp"
%}
%extend GncOption { %extend GncOption {
SCM get_scm_value() SCM get_scm_value()

View File

@ -24,6 +24,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gnc-option.hpp> #include <gnc-option.hpp>
#include <gnc-option-impl.hpp> #include <gnc-option-impl.hpp>
#include <gnc-option-ui.hpp>
#include <guid.hpp> #include <guid.hpp>
extern "C" extern "C"
{ {
@ -505,7 +506,7 @@ TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>()); EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
} }
class GncUIItem class GncUIType
{ {
public: public:
void set_value(const std::string& value) { m_value = value; } void set_value(const std::string& value) { m_value = value; }
@ -514,20 +515,29 @@ private:
std::string m_value; std::string m_value;
}; };
class GncOptionUIItem using OptionUIItem = GncUIItem<GncUIType>;
{
public:
GncOptionUIItem(GncUIItem* widget) : m_widget{widget} {}
GncUIItem* m_widget;
};
class GncOptionUITest : public ::testing::Test class GncOptionUITest : public ::testing::Test
{ {
protected: protected:
GncOptionUITest() : GncOptionUITest() :
m_widget{},
m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"},
GncOptionUIType::STRING} {} GncOptionUIType::STRING}
{
auto to_ui = [](OptionUIItem& ui, GncOption& opt) {
ui.m_widget->set_value(opt.get_value<std::string>());
};
auto from_ui = [](OptionUIItem& ui, GncOption& opt) {
opt.set_value<std::string>(ui.m_widget->get_value());
};
auto ui_item{std::make_unique<GncOptionUIItem>(
OptionUIItem{&m_widget},
GncOptionUIType::STRING,
to_ui, from_ui)};
m_option.set_ui_item(std::move(ui_item));
}
GncUIType m_widget;
GncOption m_option; GncOption m_option;
}; };
@ -540,10 +550,24 @@ TEST_F(GncOptionUI, test_option_ui_type)
TEST_F(GncOptionUI, test_set_option_ui_item) TEST_F(GncOptionUI, test_set_option_ui_item)
{ {
GncUIItem ui_item; EXPECT_EQ(&m_widget, m_option.get_ui_item()->get_ui_item().m_widget);
GncOptionUIItem option_ui_item{&ui_item}; }
m_option.set_ui_item(&option_ui_item);
EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget); TEST_F(GncOptionUI, test_ui_value_from_option)
{
const char* value{"waldo"};
m_option.set_value(value);
m_option.set_ui_item_from_option();
EXPECT_STREQ(value, m_widget.get_value().c_str());
}
TEST_F(GncOptionUI, test_option_value_from_ui)
{
const char* value{"pepper"};
m_widget.set_value(value);
m_option.set_option_from_ui_item();
EXPECT_STREQ(value, m_option.get_value<std::string>().c_str());
} }
class GncOptionRangeTest : public ::testing::Test class GncOptionRangeTest : public ::testing::Test

View File

@ -24,6 +24,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gnc-optiondb.hpp> #include <gnc-optiondb.hpp>
#include <gnc-optiondb-impl.hpp> #include <gnc-optiondb-impl.hpp>
#include <gnc-option-ui.hpp>
#include <kvp-value.hpp> #include <kvp-value.hpp>
extern "C" extern "C"
{ {
@ -205,6 +206,7 @@ TEST_F(GncOptionDBTest, test_register_date_interval_option)
ASSERT_TRUE(m_db->set_option("foo", "bar", time)); ASSERT_TRUE(m_db->set_option("foo", "bar", time));
EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>()); EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>());
} }
class GncOptionDBIOTest : public ::testing::Test class GncOptionDBIOTest : public ::testing::Test
{ {
protected: protected: