diff --git a/include/nana/gui/msgbox.hpp b/include/nana/gui/msgbox.hpp index ea60bcf6..fe6ae1ff 100644 --- a/include/nana/gui/msgbox.hpp +++ b/include/nana/gui/msgbox.hpp @@ -206,6 +206,9 @@ namespace nana return _m_open(contents, true); } + + /// Sets a verifier to verify the user input. + void verify(std::function verifier); private: void _m_fetch_args(std::vector&); @@ -221,6 +224,7 @@ namespace nana window owner_; ::nana::string description_; ::nana::string title_; + std::function verifier_; }; }//end namespace nana diff --git a/include/nana/gui/widgets/label.hpp b/include/nana/gui/widgets/label.hpp index dc79eb1c..c6488e5b 100644 --- a/include/nana/gui/widgets/label.hpp +++ b/include/nana/gui/widgets/label.hpp @@ -69,6 +69,8 @@ namespace nana /// "corrected" size that changes lines to fit the text into the specified width nana::size measure(unsigned allowed_width_in_pixel) const; + static ::nana::size measure(::nana::paint::graphics&, const ::nana::string&, unsigned allowed_width_in_pixel, bool format_enabled, align h_align, align_v v_align); + label& text_align(align horizontal_align, align_v vertical_align= align_v::top); private: //Overrides widget's virtual function diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp index f752c880..a1a85021 100644 --- a/include/nana/gui/widgets/spinbox.hpp +++ b/include/nana/gui/widgets/spinbox.hpp @@ -93,8 +93,8 @@ namespace nana void range(double begin, double last, double step); /// Sets the string spin values. - void range(std::initializer_list steps_utf8); - void range(std::initializer_list steps); + void range(std::initializer_list values_utf8); + void range(std::initializer_list values); /// Gets the spined value ::nana::string value() const; @@ -102,22 +102,9 @@ namespace nana int to_int() const; double to_double() const; - /// Sets a predicator that determines whether accepts the current user input. - /// @param pred Predicator to determines the input. - void set_accept(std::function pred); - - /// Sets the spinbox that only accepts integer input. - void set_accept_integer(); - - /// Sets the spinbox that only accepts real number input. - void set_accept_real(); - - /// Removes the accept excluding predicate accept. - void remove_accept(); - - /// Sets the qualifications - void qualify(std::wstring prefix, std::wstring suffix); - void qualify(const std::string & prefix_utf8, const std::string& suffix_utf8); + /// Sets the modifiers + void modifier(std::wstring prefix, std::wstring suffix); + void modifier(const std::string & prefix_utf8, const std::string& suffix_utf8); private: ::nana::string _m_caption() const; void _m_caption(::nana::string&&); diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 27806ee4..f594e6a0 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -321,7 +321,7 @@ namespace detail //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(parent) == false) - return nullptr; + throw std::invalid_argument("invalid parent/owner handle"); core_window_t * wd; if(is_lite) diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index e74a0b33..a13f3c82 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #if defined(NANA_WINDOWS) #include #elif defined(NANA_X11) @@ -473,7 +474,7 @@ namespace nana : public ::nana::form { public: - inputbox_window(window owner, const ::nana::string & desc, const ::nana::string& title, std::size_t contents, unsigned fixed_pixels) + inputbox_window(window owner, const ::nana::string & desc, const ::nana::string& title, std::size_t contents, unsigned fixed_pixels, const std::vector& each_height) : form(owner, API::make_center(owner, 500, 300), appear::decorate<>()) { desc_.create(*this); @@ -483,6 +484,10 @@ namespace nana btn_ok_.create(*this); btn_ok_.i18n(i18n_eval("OK")); btn_ok_.events().click.connect_unignorable([this]{ + + if (verifier_ && !verifier_(handle())) + return; + close(); valid_input_ = true; }); @@ -500,8 +505,15 @@ namespace nana ss << "margin=10 vert "; - height += 28 * contents; + { + unsigned px = 27; + if (each_height[i] > 27) + px = each_height[i]; + + ss << ""; + + height += px + 1; + } ss << ">>"; @@ -520,8 +532,10 @@ namespace nana caption(title); } - void set_input(const std::vector& inputs) + void set_input(const std::vector& inputs, std::function verifier) { + verifier_ = std::move(verifier); + std::size_t index = 0; for (auto wd : inputs) { @@ -543,6 +557,7 @@ namespace nana ::nana::button btn_cancel_; bool valid_input_{ false }; ::nana::place place_; + std::function verifier_; }; //class integer @@ -603,7 +618,7 @@ namespace nana impl->spinbox.create(impl->dock, rectangle{ static_cast(label_px + 10), 0, value_px, 0 }); impl->spinbox.range(impl->begin, impl->last, impl->step); - impl->spinbox.set_accept_integer(); + //impl->spinbox.set_accept_integer(); //deprecated //Workaround for no implementation of std::to_wstring by MinGW. ss.str(L""); @@ -613,8 +628,8 @@ namespace nana impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized& arg) { - impl->label.size({ label_px, arg.height }); - impl->spinbox.size({ value_px, arg.height }); + impl->label.size({ label_px, 24 }); + impl->spinbox.size({ value_px, 24 }); }); impl->spinbox.events().destroy.connect_unignorable([impl] @@ -690,7 +705,7 @@ namespace nana impl->spinbox.create(impl->dock, rectangle{ static_cast(label_px + 10), 0, value_px, 0 }); impl->spinbox.range(impl->begin, impl->last, impl->step); - impl->spinbox.set_accept_real(); + //impl->spinbox.set_accept_real(); //deprecated //Workaround for no implementation of std::to_wstring by MinGW. ss.str(L""); @@ -700,8 +715,8 @@ namespace nana impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized& arg) { - impl->label.size({ label_px, arg.height }); - impl->spinbox.size({ value_px, arg.height }); + impl->label.size({ label_px, 24 }); + impl->spinbox.size({ value_px, 24 }); }); impl->spinbox.events().destroy.connect_unignorable([impl] @@ -790,15 +805,17 @@ namespace nana for (auto & s : impl->options) impl->combox.push_back(s); + + impl->combox.option(0); } impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized& arg) { impl->label.size({ label_px, arg.height }); if (value_px) - impl->combox.size({ value_px, arg.height }); + impl->combox.size({ value_px, 24 }); else - impl->textbox.size({arg.width - label_px - 10, arg.height}); + impl->textbox.size({arg.width - label_px - 10, 24}); }); auto & wdg = (value_px ? static_cast(impl->combox) : static_cast(impl->textbox)); @@ -890,12 +907,12 @@ namespace nana left += 104; impl->wdg_day.create(impl->dock, rectangle{ left, 0, 38, 0 }); impl->wdg_day.range(1, ::nana::date::month_days(today.year, today.month), 1); - impl->wdg_day.set_accept_integer(); + //impl->wdg_day.set_accept_integer(); //deprecated left += 48; impl->wdg_year.create(impl->dock, rectangle{left, 0, 50, 0}); impl->wdg_year.range(1601, 9999, 1); - impl->wdg_year.set_accept_integer(); + //impl->wdg_year.set_accept_integer(); //deprecated impl->wdg_month.option(today.month - 1); @@ -911,15 +928,15 @@ namespace nana { impl->label.size({ label_px, arg.height }); auto sz = impl->wdg_month.size(); - sz.height = arg.height; + sz.height = 24; impl->wdg_month.size(sz); sz = impl->wdg_day.size(); - sz.height = arg.height; + sz.height = 24; impl->wdg_day.size(sz); sz = impl->wdg_year.size(); - sz.height = arg.height; + sz.height = 24; impl->wdg_year.size(sz); }); @@ -970,35 +987,40 @@ namespace nana title_(std::move(title)) {} + void inputbox::verify(std::function verifier) + { + verifier_ = std::move(verifier); + } + void inputbox::_m_fetch_args(std::vector&) {} bool inputbox::_m_open(std::vector& contents, bool modal) { + std::vector each_pixels; unsigned label_px = 0, fixed_px = 0; paint::graphics graph({ 5, 5 }); for (auto p : contents) { - auto px = graph.text_extent_size(p->label()).width; - if (px > label_px) - label_px = px; + auto px = label::measure(graph, p->label(), 150, true, align::right, align_v::center); + if (px.width > label_px) + label_px = px.width; - px = p->fixed_pixels(); - if (px > fixed_px) - fixed_px = px; + px.width = p->fixed_pixels(); + if (px.width > fixed_px) + fixed_px = px.width; + + each_pixels.push_back(px.height); } - if (label_px > 120) - label_px = 120; - - inputbox_window input_wd(owner_, description_, title_, contents.size(), label_px + 10 + fixed_px); + inputbox_window input_wd(owner_, description_, title_, contents.size(), label_px + 10 + fixed_px, each_pixels); std::vector inputs; for (auto p : contents) inputs.push_back(p->create(input_wd, label_px)); - input_wd.set_input(inputs); + input_wd.set_input(inputs, verifier_); if (modal) input_wd.modality(); diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index e6969835..8c534c67 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -837,6 +837,14 @@ namespace nana return impl->renderer.measure(*graph_ptr, limited, impl->text_align, impl->text_align_v); } + ::nana::size label::measure(paint::graphics& graph, const ::nana::string& str, unsigned allowed_width_in_pixel, bool format_enabled, align h_align, align_v v_align) + { + drawerbase::label::renderer rd; + rd.format(format_enabled); + rd.parse(str); + return rd.measure(graph, allowed_width_in_pixel, h_align, v_align); + } + label& label::text_align(align th, align_v tv) { internal_scope_guard isg; diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 5981d0ea..94a11715 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -53,7 +53,11 @@ namespace nana virtual ~range_interface() = default; virtual std::wstring value() const = 0; - virtual bool value(const std::wstring&) = 0; + + //sets a new value, the diff indicates whether the new value is different from the current value. + //returns true if the new value is acceptable. + virtual bool value(const std::wstring& new_value, bool& diff) = 0; + virtual bool check_value(const std::wstring&) const = 0; virtual void spin(bool increase) = 0; }; @@ -74,29 +78,57 @@ namespace nana return ss.str(); } - bool value(const std::wstring& value_str) override + bool value(const std::wstring& value_str, bool & diff) override { std::wstringstream ss; ss << value_str; T v; ss >> v; - if (begin_ <= v && v <= last_) - { - value_ = v; - return true; - } - return false; + if (v < begin_ || last_ < v) + return false; + + diff = (value_ != v); + value_ = v; + return true; } - bool check_value(const std::wstring& value_str) const override + bool check_value(const std::wstring& str) const override { - std::wstringstream ss; - ss << value_str; + if (str.empty()) + return true; - T v; - ss >> v; - return (begin_ <= v && v <= last_); + auto size = str.size(); + std::size_t pos = 0; + if (str[0] == '+' || str[0] == '-') + pos = 1; + + if (std::is_same::value) + { + for (; pos < size; ++pos) + { + auto ch = str[pos]; + if (ch < '0' || '9' < ch) + return false; + } + } + else + { + bool dot = false; + for (; pos < size; ++pos) + { + auto ch = str[pos]; + if (('.' == ch) && (!dot)) + { + dot = true; + continue; + } + + if (ch < '0' || '9' < ch) + return false; + } + } + return true; } void spin(bool increase) override @@ -145,11 +177,12 @@ namespace nana return texts_[pos_]; } - bool value(const std::wstring& value_str) override + bool value(const std::wstring& value_str, bool & diff) override { auto i = std::find(texts_.cbegin(), texts_.cend(), value_str); if (i != texts_.cend()) { + diff = (*i == value_str); pos_ = i - texts_.cbegin(); return true; } @@ -158,12 +191,15 @@ namespace nana bool check_value(const std::wstring& str) const override { - for (auto & s : texts_) + if (str.empty()) + return true; + + auto i = std::find_if(texts_.cbegin(), texts_.cend(), [&str](const std::wstring& value) { - if (s.find(str) != s.npos) - return true; - } - return false; + return (value.find(str) != value.npos); + }); + + return (i != texts_.cend()); } void spin(bool increase) override @@ -198,7 +234,7 @@ namespace nana timer_.elapse([this] { range_->spin(buttons::increase == spin_stated_); - _m_text(); + reset_text(); API::update_window(editor_->window_handle()); auto intv = timer_.interval(); @@ -219,7 +255,15 @@ namespace nana editor_->set_accept([this](::nana::char_t ch) { auto str = editor_->text(); - str += ch; + auto pos = editor_->caret().x; + if (ch == '\b') + { + if (pos > 0) + str.erase(pos - 1, 1); + } + else + str.insert(pos, 1, ch); + return range_->check_value(str); }); @@ -229,7 +273,7 @@ namespace nana if (!range_) range_.reset(new range_numeric(0, 100, 1)); - _m_text(); + reset_text(); API::tabstop(wd); API::eat_tabstop(wd, true); @@ -251,10 +295,12 @@ namespace nana bool value(const ::nana::string& value_str) { - if (!range_->value(value_str)) + bool diff; + if (!range_->value(value_str, diff)) return false; - - _m_text(); + + if (diff) + reset_text(); return true; } @@ -262,17 +308,17 @@ namespace nana { range_.swap(ptr); - _m_text(); + reset_text(); } - void qualify(std::wstring&& prefix, std::wstring&& suffix) + void modifier(std::wstring&& prefix, std::wstring&& suffix) { - surround_.prefix = std::move(prefix); - surround_.suffix = std::move(suffix); + modifier_.prefix = std::move(prefix); + modifier_.suffix = std::move(suffix); if (editor_) { - _m_text(); + reset_text(); API::update_window(editor_->window_handle()); } } @@ -296,7 +342,7 @@ namespace nana void mouse_wheel(bool upwards) { range_->spin(!upwards); - _m_text(); + reset_text(); } bool mouse_button(const ::nana::arg_mouse& arg, bool pressed) @@ -315,10 +361,11 @@ namespace nana { API::capture_window(editor_->window_handle(), true); range_->spin(buttons::increase == spin_stated_); - _m_text(); + reset_text(); timer_.start(); } - _m_draw_spins(spin_stated_); + else + _m_draw_spins(spin_stated_); return true; } @@ -369,16 +416,20 @@ namespace nana else editor_->text_area({ 2, 2, graph_->width() - spins_r.width - 2, spins_r.height - 2 }); } - private: - void _m_text() + + void reset_text() { - if (editor_) - { - std::wstring text = surround_.prefix + range_->value() + surround_.suffix; - editor_->text(std::move(text)); - _m_draw_spins(spin_stated_); - } + if (!editor_) + return; + + if (API::is_focus_window(editor_->window_handle())) + editor_->text(range_->value()); + else + editor_->text(modifier_.prefix + range_->value() + modifier_.suffix); + + _m_draw_spins(spin_stated_); } + private: ::nana::rectangle _m_spins_area() const { @@ -436,11 +487,11 @@ namespace nana std::unique_ptr range_; ::nana::timer timer_; - struct surround_data + struct modifiers { std::wstring prefix; std::wstring suffix; - }surround_; + }modifier_; }; //class drawer @@ -469,8 +520,9 @@ namespace nana impl_->render(); } - void drawer::focus(graph_reference, const arg_focus&) + void drawer::focus(graph_reference, const arg_focus& arg) { + impl_->reset_text(); impl_->render(); impl_->editor()->reset_caret(); API::lazy_refresh(); @@ -521,7 +573,9 @@ namespace nana { if (impl_->editor()->respone_keyboard(arg.key)) { - impl_->draw_spins(); + if (!impl_->value(impl_->editor()->text())) + impl_->draw_spins(); + API::lazy_refresh(); } } @@ -619,46 +673,14 @@ namespace nana return ::nana::stod(value()); } - void spinbox::set_accept(std::function pred) + void spinbox::modifier(std::wstring prefix, std::wstring suffix) { - internal_scope_guard lock; - auto editor = get_drawer_trigger().impl()->editor(); - if (editor) - editor->set_accept(std::move(pred)); + get_drawer_trigger().impl()->modifier(std::move(prefix), std::move(suffix)); } - void spinbox::set_accept_integer() + void spinbox::modifier(const std::string & prefix_utf8, const std::string& suffix_utf8) { - using accepts = ::nana::widgets::skeletons::text_editor::accepts; - auto editor = get_drawer_trigger().impl()->editor(); - if (editor) - editor->set_accept(accepts::integer); - } - - void spinbox::set_accept_real() - { - using accepts = ::nana::widgets::skeletons::text_editor::accepts; - auto editor = get_drawer_trigger().impl()->editor(); - if (editor) - editor->set_accept(accepts::real); - } - - void spinbox::remove_accept() - { - using accepts = ::nana::widgets::skeletons::text_editor::accepts; - auto editor = get_drawer_trigger().impl()->editor(); - if (editor) - editor->set_accept(accepts::no_restrict); - } - - void spinbox::qualify(std::wstring prefix, std::wstring suffix) - { - get_drawer_trigger().impl()->qualify(std::move(prefix), std::move(suffix)); - } - - void spinbox::qualify(const std::string & prefix_utf8, const std::string& suffix_utf8) - { - qualify(static_cast(::nana::charset(prefix_utf8, ::nana::unicode::utf8)), static_cast(::nana::charset(suffix_utf8, ::nana::unicode::utf8))); + modifier(static_cast(::nana::charset(prefix_utf8, ::nana::unicode::utf8)), static_cast(::nana::charset(suffix_utf8, ::nana::unicode::utf8))); } ::nana::string spinbox::_m_caption() const