diff --git a/include/nana/gui/widgets/combox.hpp b/include/nana/gui/widgets/combox.hpp index 7e478557..fc8e4522 100644 --- a/include/nana/gui/widgets/combox.hpp +++ b/include/nana/gui/widgets/combox.hpp @@ -1,7 +1,7 @@ /** * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -68,7 +68,7 @@ namespace nana void key_press(graph_reference, const arg_keyboard&) override; void key_char(graph_reference, const arg_keyboard&) override; private: - drawer_impl * drawer_; + drawer_impl * const drawer_; }; class item_proxy diff --git a/include/nana/gui/widgets/picture.hpp b/include/nana/gui/widgets/picture.hpp index fc8e9c63..9050f8be 100644 --- a/include/nana/gui/widgets/picture.hpp +++ b/include/nana/gui/widgets/picture.hpp @@ -1,7 +1,7 @@ /** * A Picture Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 2c6b54de..e3ffd163 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -56,7 +56,7 @@ namespace nana enum class token { div_start, div_end, splitter, - identifier, dock, fit, fit_s, vert, grid, number, array, reparray, + identifier, dock, fit, hfit, vfit, vert, grid, number, array, reparray, weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, undisplayed, invisible, collapse, parameters, equal, @@ -249,17 +249,22 @@ namespace nana return token::dock; else if ("fit" == idstr_) return token::fit; - else if ("fit_s" == idstr_) - return token::fit_s; else if ("vertical" == idstr_ || "vert" == idstr_) return token::vert; else if ("variable" == idstr_ || "repeated" == idstr_) return ('v' == idstr_[0] ? token::variable : token::repeated); - else if ("arrange" == idstr_ || "gap" == idstr_) + else if ("arrange" == idstr_ || "hfit" == idstr_ || "vfit" == idstr_ || "gap" == idstr_) { auto ch = idstr_[0]; _m_attr_reparray(); - return ('a' == ch ? token::arrange : token::gap); + switch (ch) + { + case 'a': return token::arrange; + case 'h': return token::hfit; + case 'v': return token::vfit; + case 'g': return token::gap; + default: break; + } } else if ("grid" == idstr_ || "margin" == idstr_) { @@ -722,7 +727,8 @@ namespace nana { none, //Doesn't fit the content both, //Fits both width and height of content - single //Fits only width or height of content + horz, //Fits width of content with a specified height + vert //Fits height of content with a specified width }; class place::implement::division @@ -767,6 +773,7 @@ namespace nana std::pair calc_weight_floor() { std::pair floor; + run_.fit_extents.clear(); run_.weight_floor = floor; @@ -788,9 +795,10 @@ namespace nana floor.second += child_floor.second; } } - - auto const vert = (this->div_owner && (this->div_owner->kind_of_division == kind::vertical_arrange)); - double& fv = (vert ? floor.second : floor.first); + + auto const vert_fields = (kind::vertical_arrange == this->kind_of_division); + auto const vert_div = (this->div_owner && (kind::vertical_arrange == this->div_owner->kind_of_division)); + double& fv = (vert_div ? floor.second : floor.first); if((ratio > 0.001) && (fv > 0)) fv /= ratio; @@ -804,34 +812,49 @@ namespace nana { if (fit_policy::none != this->fit) { - unsigned limited_px = 0; - bool limit_width = false; - std::size_t fit_count = 0; + unsigned max_value = 0; + auto const fit_horz = (fit_policy::vert == this->fit); + + std::size_t pos = 0; for (auto & elm : this->field->elements) { - auto extent = API::content_extent(elm.handle, 0, false); + ++pos; + + unsigned edge_px = 0; + if (fit_policy::both != this->fit) + { + auto fit_val = this->fit_parameters.at(pos - 1); + if (fit_val.empty()) + continue; + + edge_px = fit_val.integer(); + } + + auto extent = API::content_extent(elm.handle, edge_px, fit_horz); if (extent) { + run_.fit_extents[elm.handle] = extent->second; ++fit_count; - if (vert) + if (vert_fields) floor.second += extent->second.height; else floor.first += extent->second.width; - - max_value = (std::max)(max_value, (vert ? extent->second.width : extent->second.height)); + + max_value = (std::max)(max_value, (vert_fields ? extent->second.width : extent->second.height)); } } if (max_value) { - if (vert) + if (vert_fields) floor.first = max_value; else floor.second = max_value; } + if (fit_count > 1) { double percent = 0; @@ -1005,6 +1028,7 @@ namespace nana bool display{ true }; bool visible{ true }; fit_policy fit{ fit_policy::none }; + repeated_array fit_parameters; //it is ignored when fit is not fit_policy::horz or fit_policy::vert ::nana::direction dir{::nana::direction::west}; std::string name; std::vector> children; @@ -1022,6 +1046,7 @@ namespace nana struct run_data { std::pair weight_floor; + std::map fit_extents; }run_; };//end class division @@ -1069,6 +1094,7 @@ namespace nana child_px = adjustable_px; child_px = limit_px(child, child_px, area_px); + auto npx = static_cast(child_px); precise_px = child_px - npx; child_px = npx; @@ -1109,9 +1135,23 @@ namespace nana unsigned px = 0; auto move_r = element_r.result(); - if (fit_policy::both == this->fit) + if (fit_policy::none != this->fit) { - auto extent = API::content_extent(el.handle, 0, false); + auto i = run_.fit_extents.find(el.handle); + if (run_.fit_extents.end() != i) + { + move_r.dimension(i->second); + + if (vert) + move_r.x += place_parts::differ(area_margined.width, move_r.width) / 2; + else + move_r.y += place_parts::differ(area_margined.height, move_r.height) / 2; + + px = (vert ? move_r.height : move_r.width); + moved = true; + } + /* + auto extent = API::content_extent(el.handle, 0, false); //deprecated if (extent) { move_r.dimension(extent->second); @@ -1124,6 +1164,7 @@ namespace nana px = (vert ? move_r.height : move_r.width); moved = true; } + */ } if (!moved) @@ -1178,12 +1219,26 @@ namespace nana std::pair result; if (field && (kind_of_division == match_kind)) { + auto const vert = (kind_of_division == kind::vertical_arrange); + //Calculate fixed and adjustable of elements double precise_px = 0; auto count = field->elements.size(); for (decltype(count) i = 0; i < count; ++i) { auto fa = _m_calc_fa(arrange_.at(i), area_px, precise_px); + + //The fit-content element is like a fixed element + if (fit_policy::none != this->fit) + { + auto fi = this->run_.fit_extents.find(field->elements[i].handle); + if (this->run_.fit_extents.cend() != fi) + { + fa.first = (vert ? fi->second.height : fi->second.width); + fa.second = 0; //This isn't an adjustable element + } + } + result.first += fa.first; result.second += fa.second; @@ -2515,6 +2570,7 @@ namespace nana std::unique_ptr div; token div_type = token::eof; auto fit = fit_policy::none; + place_parts::repeated_array fit_parameters; //These variables stand for the new division's attributes std::string name; @@ -2542,8 +2598,10 @@ namespace nana case token::fit: fit = fit_policy::both; break; - case token::fit_s: - fit = fit_policy::single; + case token::hfit: + case token::vfit: + fit = (token::hfit == tk ? fit_policy::horz : fit_policy::vert); + fit_parameters = tknizer.reparray(); break; case token::splitter: //Ignore the splitter when there is not a division. @@ -2809,6 +2867,8 @@ namespace nana div->display = !undisplayed; div->visible = !(undisplayed || invisible); div->fit = fit; + div->fit_parameters = std::move(fit_parameters); + return div; } diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 3e6090e1..16cf441b 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -29,6 +29,10 @@ namespace nana{ namespace drawerbase optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override { + //Button doesn't provide a support of vfit and hfit + if (limit_pixels) + return{}; + wchar_t shortkey; std::string::size_type shortkey_pos; diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 4ec63f52..77f199ab 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -1,7 +1,7 @@ /* * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -17,8 +17,10 @@ #include #include #include +#include #include +#include namespace nana { @@ -80,6 +82,46 @@ namespace nana class drawer_impl { + class content_measurer + : public dev::widget_content_measurer_interface + { + public: + content_measurer(drawer_impl* drwimpl) + : drw_{ drwimpl } + {} + + optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override + { + //Button doesn't provide a support of vfit and hfit + if (limit_pixels) + return{}; + + size content_size; + for (auto i = 0; i < drw_->the_number_of_options(); ++i) + { + auto & m = drw_->at(i); + auto sz = graph.text_extent_size(m.item_text); + + content_size.width = (std::max)(content_size.width, sz.width); + content_size.height = (std::max)(content_size.height, sz.height); + } + + return content_size; + } + + size extension() const override + { + auto text_size = drw_->editor()->text_area(false).dimension(); + auto wdg_size = drw_->widget_ptr()->size(); + + return{ + wdg_size.width > text_size.width ? wdg_size.width - text_size.width : 0, + wdg_size.height > text_size.height ? wdg_size.height - text_size.height : 0 + }; + } + private: + drawer_impl* const drw_; + }; public: using graph_reference = paint::graphics&; using widget_reference = widget&; @@ -92,6 +134,8 @@ namespace nana state_.button_state = element_state::normal; state_.pointer_where = parts::none; state_.lister = nullptr; + + measurer_.reset(new content_measurer{this}); } void renderer(drawerbase::float_listbox::item_renderer* ir) @@ -111,6 +155,8 @@ namespace nana evt_agent_.reset(new event_agent{ static_cast(wd) }); editor_->textbase().set_event_agent(evt_agent_.get()); + + API::dev::set_measurer(wd, measurer_.get()); } void detached() @@ -528,6 +574,8 @@ namespace nana unsigned image_pixels_{ 16 }; widgets::skeletons::text_editor * editor_{ nullptr }; std::unique_ptr evt_agent_; + + std::unique_ptr measurer_; struct state_type { bool focused; @@ -537,188 +585,191 @@ namespace nana nana::float_listbox * lister; std::size_t item_index_before_selection; }state_; - }; + + + }; //end class drawer_impl //class trigger - trigger::trigger() - : drawer_(new drawer_impl) - {} + trigger::trigger() : + drawer_(new drawer_impl) + { + } - trigger::~trigger() - { - delete drawer_; - } + trigger::~trigger() + { + delete drawer_; + } - drawer_impl& trigger::get_drawer_impl() - { - return *drawer_; - } + drawer_impl& trigger::get_drawer_impl() + { + return *drawer_; + } - const drawer_impl& trigger::get_drawer_impl() const - { - return *drawer_; - } + const drawer_impl& trigger::get_drawer_impl() const + { + return *drawer_; + } - void trigger::attached(widget_reference wdg, graph_reference graph) - { - wdg.bgcolor(colors::white); - drawer_->attached(wdg, graph); + void trigger::attached(widget_reference wdg, graph_reference graph) + { + wdg.bgcolor(colors::white); + drawer_->attached(wdg, graph); - API::effects_edge_nimbus(wdg, effects::edge_nimbus::active); - API::effects_edge_nimbus(wdg, effects::edge_nimbus::over); - } + API::effects_edge_nimbus(wdg, effects::edge_nimbus::active); + API::effects_edge_nimbus(wdg, effects::edge_nimbus::over); + } - void trigger::detached() - { - drawer_->detached(); - } + void trigger::detached() + { + drawer_->detached(); + } - void trigger::refresh(graph_reference) + void trigger::refresh(graph_reference) + { + drawer_->draw(); + } + + void trigger::focus(graph_reference, const arg_focus& arg) + { + drawer_->set_focused(arg.getting); + if(drawer_->widget_ptr()->enabled()) { drawer_->draw(); + drawer_->editor()->reset_caret(); + API::dev::lazy_refresh(); } + } - void trigger::focus(graph_reference, const arg_focus& arg) + void trigger::mouse_enter(graph_reference, const arg_mouse&) + { + drawer_->set_button_state(element_state::hovered, true); + if(drawer_->widget_ptr()->enabled()) { - drawer_->set_focused(arg.getting); - if(drawer_->widget_ptr()->enabled()) + drawer_->draw(); + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_leave(graph_reference, const arg_mouse&) + { + drawer_->set_button_state(element_state::normal, true); + drawer_->editor()->mouse_enter(false); + if(drawer_->widget_ptr()->enabled()) + { + drawer_->draw(); + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_down(graph_reference, const arg_mouse& arg) + { + //drawer_->set_mouse_press(true); + drawer_->set_button_state(element_state::pressed, false); + if(drawer_->widget_ptr()->enabled()) + { + auto * editor = drawer_->editor(); + editor->mouse_pressed(arg); + drawer_->open_lister_if_push_button_positioned(); + + drawer_->draw(); + if(editor->attr().editable) + editor->reset_caret(); + + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_up(graph_reference, const arg_mouse& arg) + { + if (drawer_->widget_ptr()->enabled() && !drawer_->has_lister()) + { + drawer_->editor()->mouse_pressed(arg); + drawer_->set_button_state(element_state::hovered, false); + drawer_->draw(); + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) + { + if(drawer_->widget_ptr()->enabled()) + { + bool redraw = drawer_->calc_where(graph, arg.pos.x, arg.pos.y); + redraw |= drawer_->editor()->mouse_move(arg.left_button, arg.pos); + + if(redraw) { drawer_->draw(); drawer_->editor()->reset_caret(); API::dev::lazy_refresh(); } } + } - void trigger::mouse_enter(graph_reference, const arg_mouse&) + void trigger::mouse_wheel(graph_reference, const arg_wheel& arg) + { + if(drawer_->widget_ptr()->enabled()) { - drawer_->set_button_state(element_state::hovered, true); - if(drawer_->widget_ptr()->enabled()) - { - drawer_->draw(); - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_leave(graph_reference, const arg_mouse&) - { - drawer_->set_button_state(element_state::normal, true); - drawer_->editor()->mouse_enter(false); - if(drawer_->widget_ptr()->enabled()) - { - drawer_->draw(); - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_down(graph_reference, const arg_mouse& arg) - { - //drawer_->set_mouse_press(true); - drawer_->set_button_state(element_state::pressed, false); - if(drawer_->widget_ptr()->enabled()) - { - auto * editor = drawer_->editor(); - editor->mouse_pressed(arg); - drawer_->open_lister_if_push_button_positioned(); - - drawer_->draw(); - if(editor->attr().editable) - editor->reset_caret(); - - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_up(graph_reference, const arg_mouse& arg) - { - if (drawer_->widget_ptr()->enabled() && !drawer_->has_lister()) - { - drawer_->editor()->mouse_pressed(arg); - drawer_->set_button_state(element_state::hovered, false); - drawer_->draw(); - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) - { - if(drawer_->widget_ptr()->enabled()) - { - bool redraw = drawer_->calc_where(graph, arg.pos.x, arg.pos.y); - redraw |= drawer_->editor()->mouse_move(arg.left_button, arg.pos); - - if(redraw) - { - drawer_->draw(); - drawer_->editor()->reset_caret(); - API::dev::lazy_refresh(); - } - } - } - - void trigger::mouse_wheel(graph_reference, const arg_wheel& arg) - { - if(drawer_->widget_ptr()->enabled()) - { - if(drawer_->has_lister()) - drawer_->scroll_items(arg.upwards); - else - drawer_->move_items(arg.upwards, false); - } - } - - void trigger::key_press(graph_reference, const arg_keyboard& arg) - { - if(!drawer_->widget_ptr()->enabled()) - return; - - bool call_other_keys = false; - if(drawer_->editable()) - { - bool is_move_up = false; - switch(arg.key) - { - case keyboard::os_arrow_left: - case keyboard::os_arrow_right: - drawer_->editor()->respond_key(arg); - drawer_->editor()->reset_caret(); - break; - case keyboard::os_arrow_up: - is_move_up = true; - case keyboard::os_arrow_down: - drawer_->move_items(is_move_up, true); - break; - default: - call_other_keys = true; - } - } + if(drawer_->has_lister()) + drawer_->scroll_items(arg.upwards); else - { - bool is_move_up = false; - switch(arg.key) - { - case keyboard::os_arrow_left: - case keyboard::os_arrow_up: - is_move_up = true; - case keyboard::os_arrow_right: - case keyboard::os_arrow_down: - drawer_->move_items(is_move_up, true); - break; - default: - call_other_keys = true; - } - } - if (call_other_keys) - drawer_->editor()->respond_key(arg); - - API::dev::lazy_refresh(); + drawer_->move_items(arg.upwards, false); } + } - void trigger::key_char(graph_reference, const arg_keyboard& arg) + void trigger::key_press(graph_reference, const arg_keyboard& arg) + { + if(!drawer_->widget_ptr()->enabled()) + return; + + bool call_other_keys = false; + if(drawer_->editable()) { - if (drawer_->editor()->respond_char(arg)) - API::dev::lazy_refresh(); + bool is_move_up = false; + switch(arg.key) + { + case keyboard::os_arrow_left: + case keyboard::os_arrow_right: + drawer_->editor()->respond_key(arg); + drawer_->editor()->reset_caret(); + break; + case keyboard::os_arrow_up: + is_move_up = true; + case keyboard::os_arrow_down: + drawer_->move_items(is_move_up, true); + break; + default: + call_other_keys = true; + } } + else + { + bool is_move_up = false; + switch(arg.key) + { + case keyboard::os_arrow_left: + case keyboard::os_arrow_up: + is_move_up = true; + case keyboard::os_arrow_right: + case keyboard::os_arrow_down: + drawer_->move_items(is_move_up, true); + break; + default: + call_other_keys = true; + } + } + if (call_other_keys) + drawer_->editor()->respond_key(arg); + + API::dev::lazy_refresh(); + } + + void trigger::key_char(graph_reference, const arg_keyboard& arg) + { + if (drawer_->editor()->respond_char(arg)) + API::dev::lazy_refresh(); + } //end class trigger //class item_proxy diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index ea33dd33..c83a09f5 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -184,10 +184,10 @@ namespace nana rs.text_align = th; rs.text_align_v = tv; - for(auto i = dstream_.begin(), end = dstream_.end(); i != end; ++i) + for(auto & line: dstream_) { rs.pixels.clear(); - unsigned w = _m_line_pixels(*i, def_line_pixels, rs); + unsigned w = _m_line_pixels(line, def_line_pixels, rs); if(limited && (w > limited)) w = limited; @@ -366,7 +366,8 @@ namespace nana sz.height = max_ascent + max_descent; } - if(w + sz.width <= rs.allowed_width) + //Check if the content is displayed in a new line. + if((0 == rs.allowed_width) || (w + sz.width <= rs.allowed_width)) { w += sz.width; @@ -620,7 +621,7 @@ namespace nana widget * wd{nullptr}; paint::graphics * graph{nullptr}; - class measurer * measurer{ nullptr }; + std::unique_ptr msr_ptr{ nullptr }; align text_align{align::left}; align_v text_align_v; @@ -657,9 +658,10 @@ namespace nana optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override { - if (graph) + //Label now doesn't support to measure content with a specified height. + if (graph && ((0 == limit_pixels) || limit_width)) { - + return impl_->renderer.measure(graph, limit_pixels, impl_->text_align, impl_->text_align_v); } return{}; } @@ -674,7 +676,9 @@ namespace nana trigger::trigger() :impl_(new implement) - {} + { + impl_->msr_ptr.reset(new trigger::implement::measurer{impl_}); + } trigger::~trigger() { @@ -690,6 +694,7 @@ namespace nana { impl_->graph = &graph; impl_->wd = &widget; + API::dev::set_measurer(widget, impl_->msr_ptr.get()); } void trigger::mouse_move(graph_reference, const arg_mouse& arg) diff --git a/source/gui/widgets/picture.cpp b/source/gui/widgets/picture.cpp index bf739693..650f434c 100644 --- a/source/gui/widgets/picture.cpp +++ b/source/gui/widgets/picture.cpp @@ -1,7 +1,7 @@ /* * A Picture Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -16,6 +16,7 @@ #include #include #include +#include namespace nana { @@ -23,11 +24,13 @@ namespace nana { namespace picture { + class content_measurer; + struct implement { widget* wdg_ptr{nullptr}; paint::graphics* graph_ptr{nullptr}; - + std::unique_ptr measurer; struct gradual_bground_tag { @@ -47,9 +50,37 @@ namespace nana }backimg; }; + class content_measurer + : public dev::widget_content_measurer_interface + { + public: + content_measurer(implement* impl) + : impl_{impl} + {} + + optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override + { + //Button doesn't provide a support of vfit and hfit + if (!limit_pixels) + { + if (impl_->backimg.valid_area.empty()) + return impl_->backimg.image.size(); + } + return{}; + } + + size extension() const override + { + return{}; + } + private: + implement* const impl_; + }; + //class drawer drawer::drawer() :impl_(new implement) { + impl_->measurer.reset(new content_measurer{impl_}); } drawer::~drawer() @@ -61,6 +92,7 @@ namespace nana { impl_->wdg_ptr = &wdg; impl_->graph_ptr = &graph; + API::dev::set_measurer(wdg, impl_->measurer.get()); } void drawer::refresh(graph_reference graph)