diff --git a/build/codeblocks/nana.cbp b/build/codeblocks/nana.cbp index 973b633f..83985a24 100644 --- a/build/codeblocks/nana.cbp +++ b/build/codeblocks/nana.cbp @@ -91,6 +91,7 @@ + diff --git a/build/codeblocks/nana.layout b/build/codeblocks/nana.layout index 8af72c72..57a19817 100644 --- a/build/codeblocks/nana.layout +++ b/build/codeblocks/nana.layout @@ -1,11 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -21,21 +51,26 @@ - - - - - + + + + + + + + + + @@ -46,29 +81,59 @@ + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -81,74 +146,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj index 89c4ae25..d6b6e976 100644 --- a/build/vc2013/nana.vcxproj +++ b/build/vc2013/nana.vcxproj @@ -139,6 +139,7 @@ + diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters index c4738ee4..f0f82981 100644 --- a/build/vc2013/nana.vcxproj.filters +++ b/build/vc2013/nana.vcxproj.filters @@ -297,5 +297,8 @@ Source Files\nana\gui + + Source Files\nana\gui\widgets + \ No newline at end of file diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 0524db4b..759f8096 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -155,7 +155,7 @@ namespace detail { bool enabled :1; bool dbl_click :1; - bool capture :1; //if mouse button is down, it always receive mouse move even the mouse is out of its rectangle + bool captured :1; //if mouse button is down, it always receive mouse move even the mouse is out of its rectangle bool modal :1; bool take_active:1; //If take_active is false, other.active_window still keeps the focus. bool refreshing :1; diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index b91c728f..2f62fee1 100644 --- a/include/nana/gui/detail/bedrock.hpp +++ b/include/nana/gui/detail/bedrock.hpp @@ -42,7 +42,7 @@ namespace detail ~bedrock(); void pump_event(window, bool is_modal); - void map_thread_root_buffer(core_window_t* ); + void map_thread_root_buffer(core_window_t*, bool forced); static int inc_window(unsigned tid = 0); thread_context* open_thread_context(unsigned tid = 0); thread_context* get_thread_context(unsigned tid = 0); diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index 7b11fc37..dd0c4192 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -110,7 +110,7 @@ namespace nana void key_char(const arg_keyboard&); void key_release(const arg_keyboard&); void shortkey(const arg_keyboard&); - void map(window); //Copy the root buffer to screen + void map(window, bool forced); //Copy the root buffer to screen void refresh(); drawer_trigger* realizer() const; void attached(widget&, drawer_trigger&); diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp index 27ea4ab6..3489dbaf 100644 --- a/include/nana/gui/detail/effects_renderer.hpp +++ b/include/nana/gui/detail/effects_renderer.hpp @@ -28,7 +28,7 @@ namespace nana{ return 2; } - bool render(core_window_t * wd) + bool render(core_window_t * wd, bool forced) { bool rendered = false; core_window_t * root_wd = wd->root_widget; @@ -53,7 +53,7 @@ namespace nana{ rendered = true; //Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered. - if (!action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh)) + if ((forced && (action.window == wd)) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh)) { rd_set.emplace_back(r, action.window); action.rendered = true; diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index baab1232..2e07ac31 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -150,7 +150,7 @@ namespace detail core_window_t* root(native_window_type) const; //Copy the root buffer that wnd specified into DeviceContext - void map(core_window_t*); + void map(core_window_t*, bool forced); bool update(core_window_t*, bool redraw, bool force); void refresh_tree(core_window_t*); diff --git a/include/nana/gui/element.hpp b/include/nana/gui/element.hpp index b0cdc9e1..ee63ecf0 100644 --- a/include/nana/gui/element.hpp +++ b/include/nana/gui/element.hpp @@ -106,6 +106,9 @@ namespace nana void add_arrow(const std::string&, const pat::cloneable>&); arrow_interface* const * keeper_arrow(const std::string&); + + void add_button(const std::string&, const pat::cloneable>&); + element_interface* const* keeper_button(const std::string&); }; class crook; @@ -131,6 +134,14 @@ namespace nana using factory_t = provider::factory; provider().add_arrow(name, pat::cloneable(factory_t())); } + + class button; + template + void add_button(const std::string& name) + { + using factory_t = provider::factory; + provider().add_button(name, pat::cloneable(factory_t())); + } }//end namespace element template class facade; @@ -200,6 +211,20 @@ namespace nana ::nana::direction dir_{::nana::direction::north}; };//end class facade + template<> + class facade + : public element::element_interface + { + public: + facade(const char* name = nullptr); + void switch_to(const char*); + public: + //Implement element_interface + bool draw(graph_reference, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle&, element_state) override; + private: + element::element_interface* const * keeper_; + };//end class facade + namespace element { diff --git a/include/nana/gui/widgets/date_chooser.hpp b/include/nana/gui/widgets/date_chooser.hpp index d6aa3fb8..5c9e3507 100644 --- a/include/nana/gui/widgets/date_chooser.hpp +++ b/include/nana/gui/widgets/date_chooser.hpp @@ -45,7 +45,7 @@ namespace nana void week_name(unsigned index, const nana::string&); void month_name(unsigned index, const nana::string&); private: - where _m_pos_where(graph_reference, int x, int y); + where _m_pos_where(graph_reference, const ::nana::point& pos); void _m_draw(graph_reference); void _m_draw_topbar(graph_reference); void _m_make_drawing_basis(drawing_basis&, graph_reference, const nana::point& refpos); diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp new file mode 100644 index 00000000..dcf0b982 --- /dev/null +++ b/include/nana/gui/widgets/spinbox.hpp @@ -0,0 +1,78 @@ +/* + * A Spin box widget + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/widgets/spanbox.hpp + */ + +#ifndef NANA_GUI_WIDGET_SPINBOX_HPP +#define NANA_GUI_WIDGET_SPINBOX_HPP +#include "widget.hpp" +#include "skeletons/text_editor_scheme.hpp" + +namespace nana +{ + namespace drawerbase + { + namespace spinbox + { + /// Declaration of internal spinbox implementation + class implementation; + + /// Drawer of spinbox + class drawer + : public ::nana::drawer_trigger + { + drawer(const drawer&) = delete; + drawer(drawer&&) = delete; + drawer& operator=(const drawer&) = delete; + drawer& operator=(drawer&&) = delete; + public: + drawer(); + ~drawer(); + implementation * impl() const; + private: + //Overrides drawer_trigger + void attached(widget_reference, graph_reference) override; + void refresh(graph_reference) override; + + void focus(graph_reference, const arg_focus&) override; + void mouse_wheel(graph_reference, const arg_wheel&) override; + void mouse_down(graph_reference, const arg_mouse&) override; + void mouse_move(graph_reference, const arg_mouse&) override; + void mouse_up(graph_reference, const arg_mouse& arg) override; + void mouse_leave(graph_reference, const arg_mouse&) override; + private: + implementation * const impl_; + }; + }; + }//end namespace drawerbase + + /// Spinbox Widget + class spinbox + : public widget_object + { + public: + spinbox(); + spinbox(window, bool visible); + spinbox(window, const nana::rectangle& = {}, bool visible = true); + + void range(int begin, int last, int step); + void range(double begin, double last, double step); + void range(std::initializer_list steps_utf8); + void range(std::initializer_list steps); + + void qualify(std::wstring prefix, std::wstring suffix); + void qualify(const std::string & prefix_utf8, const std::string& suffix_utf8); + private: + ::nana::string _m_caption() const; + void _m_caption(::nana::string&&); + }; +}//end namespace nana + +#endif //NANA_GUI_WIDGET_SPINBOX_HPP \ No newline at end of file diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index a4f78b11..16545934 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -327,7 +327,7 @@ namespace nana } predef_cursor = cursor::arrow; - flags.capture = false; + flags.captured = false; flags.dbl_click = true; flags.enabled = true; flags.modal = false; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 7fddd857..d3722ce2 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -63,7 +63,7 @@ namespace nana } wd_manager.refresh_tree(wd); - wd_manager.map(wd); + wd_manager.map(wd, false); } } diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index 6e5c4673..7270aa7a 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -242,7 +242,7 @@ namespace nana _m_emit(event_code::shortkey, arg, &drawer_trigger::shortkey); } - void drawer::map(window wd) //Copy the root buffer to screen + void drawer::map(window wd, bool forced) //Copy the root buffer to screen { if(wd) { @@ -264,7 +264,7 @@ namespace nana #endif } - if(false == edge_nimbus_renderer_t::instance().render(iwd)) + if (false == edge_nimbus_renderer_t::instance().render(iwd, forced)) { nana::rectangle vr; if(bedrock_type::window_manager_t::wndlayout_type::read_visual_rectangle(iwd, vr)) diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index c9e1ae78..8b55a9b7 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -176,9 +176,9 @@ namespace detail delete impl_; } - void bedrock::map_thread_root_buffer(bedrock::core_window_t* wnd) + void bedrock::map_thread_root_buffer(core_window_t*, bool forced) { - //GUI in X11 is not thread-dependent, so no implementation. + //GUI in X11 is thread-independent, so no implementation. } //inc_window @@ -619,7 +619,8 @@ namespace detail msgwnd = brock.wd_manager.find_window(native_window, xevent.xcrossing.x, xevent.xcrossing.y); if(msgwnd) { - msgwnd->flags.action = mouse_action::over; + if (mouse_action::pressed != msgwnd->flags.action) + msgwnd->flags.action = mouse_action::over; hovered_wd = msgwnd; arg_mouse arg; @@ -870,7 +871,8 @@ namespace detail else { evt_code = event_code::mouse_enter; - msgwnd->flags.action = mouse_action::over; + if (mouse_action::pressed != msgwnd->flags.action) + msgwnd->flags.action = mouse_action::over; } arg_mouse arg; assign_arg(arg, msgwnd, message, xevent); @@ -883,7 +885,10 @@ namespace detail { arg_mouse arg; assign_arg(arg, msgwnd, message, xevent); - msgwnd->flags.action = mouse_action::over; + + if (mouse_action::pressed != msgwnd->flags.action) + msgwnd->flags.action = mouse_action::over; + if (hovered_wd != msgwnd) { hovered_wd = msgwnd; diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp index 74b8cb30..fd1eb903 100644 --- a/source/gui/detail/win32/bedrock.cpp +++ b/source/gui/detail/win32/bedrock.cpp @@ -342,9 +342,9 @@ namespace detail return bedrock_object; } - void bedrock::map_thread_root_buffer(core_window_t* wd) + void bedrock::map_thread_root_buffer(core_window_t* wd, bool forced) { - ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), 0); + ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), static_cast(forced ? TRUE : FALSE)); } void interior_helper_for_menu(MSG& msg, native_window_type menu_window) @@ -596,7 +596,7 @@ namespace detail } return true; case nana::detail::messages::map_thread_root_buffer: - bedrock.wd_manager.map(reinterpret_cast(wParam)); + bedrock.wd_manager.map(reinterpret_cast(wParam), (TRUE == lParam)); ::UpdateWindow(wd); return true; case nana::detail::messages::remote_thread_move_window: @@ -1060,7 +1060,10 @@ namespace detail else { evt_code = event_code::mouse_enter; - msgwnd->flags.action = mouse_action::over; + if (pressed_wd == msgwnd) + msgwnd->flags.action = mouse_action::pressed; + else if (mouse_action::pressed != msgwnd->flags.action) + msgwnd->flags.action = mouse_action::over; } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); @@ -1073,9 +1076,14 @@ namespace detail { arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); - msgwnd->flags.action = mouse_action::over; + if (hovered_wd != msgwnd) { + if (pressed_wd == msgwnd) + msgwnd->flags.action = mouse_action::pressed; + else if (mouse_action::pressed != msgwnd->flags.action) + msgwnd->flags.action = mouse_action::over; + hovered_wd = msgwnd; arg.evt_code = event_code::mouse_enter; brock.emit(event_code::mouse_enter, msgwnd, arg, true, &context); @@ -1105,21 +1113,34 @@ namespace detail auto scrolled_wd = brock.wd_manager.find_window(reinterpret_cast(pointer_wd), scr_pos.x, scr_pos.y); def_window_proc = true; - while (scrolled_wd) + auto evt_wd = scrolled_wd; + while (evt_wd) { - if (scrolled_wd->together.attached_events->mouse_wheel.length() != 0) + if (evt_wd->together.attached_events->mouse_wheel.length() != 0) { def_window_proc = false; nana::point mspos{ scr_pos.x, scr_pos.y }; - brock.wd_manager.calc_window_point(scrolled_wd, mspos); + brock.wd_manager.calc_window_point(evt_wd, mspos); arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); - assign_arg(arg, scrolled_wd, pmdec); - brock.emit(event_code::mouse_wheel, scrolled_wd, arg, true, &context); + assign_arg(arg, evt_wd, pmdec); + brock.emit(event_code::mouse_wheel, evt_wd, arg, true, &context); break; } - scrolled_wd = scrolled_wd->parent; + evt_wd = evt_wd->parent; + } + + if (scrolled_wd && (nullptr == evt_wd)) + { + nana::point mspos{ scr_pos.x, scr_pos.y }; + brock.wd_manager.calc_window_point(scrolled_wd, mspos); + + arg_wheel arg; + arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); + assign_arg(arg, scrolled_wd, pmdec); + brock.emit_drawer(event_code::mouse_wheel, scrolled_wd, arg, &context); + brock.wd_manager.do_lazy_refresh(scrolled_wd, false); } } else diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 990f1090..008c0dc6 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -671,7 +671,7 @@ namespace detail } //Copy the root buffer that wnd specified into DeviceContext - void window_manager::map(core_window_t* wd) + void window_manager::map(core_window_t* wd, bool forced) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -682,9 +682,9 @@ namespace detail wd->drawer.map(reinterpret_cast(wd)); #elif defined(NANA_WINDOWS) if(nana::system::this_thread_id() == wd->thread_id) - wd->drawer.map(reinterpret_cast(wd)); + wd->drawer.map(reinterpret_cast(wd), forced); else - bedrock::instance().map_thread_root_buffer(wd); + bedrock::instance().map_thread_root_buffer(wd, forced); #endif } } @@ -693,7 +693,7 @@ namespace detail //@brief: update is used for displaying the screen-off buffer. // Because of a good efficiency, if it is called in an event procedure and the event procedure window is the // same as update's, update would not map the screen-off buffer and just set the window for lazy refresh - bool window_manager::update(core_window_t* wd, bool redraw, bool force) + bool window_manager::update(core_window_t* wd, bool redraw, bool forced) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -701,10 +701,10 @@ namespace detail if (wd->visible && wd->visible_parents()) { - if(force || (false == wd->belong_to_lazy())) + if(forced || (false == wd->belong_to_lazy())) { wndlayout_type::paint(wd, redraw, false); - this->map(wd); + this->map(wd, forced); } else { @@ -746,13 +746,13 @@ namespace detail if ((wd->other.upd_state == core_window_t::update_state::refresh) || force_copy_to_screen) { wndlayout_type::paint(wd, false, false); - this->map(wd); + this->map(wd, force_copy_to_screen); } else if (effects::edge_nimbus::none != wd->effect.edge_nimbus) { //Update the nimbus effect using nimbus_renderer = detail::edge_nimbus_renderer; - nimbus_renderer::instance().render(wd); + nimbus_renderer::instance().render(wd, force_copy_to_screen); } } else @@ -935,7 +935,7 @@ namespace detail if (impl_->wd_register.available(wd)) { - wd->flags.capture = true; + wd->flags.captured = true; native_interface::capture_window(wd->root, value); auto prev = attr_.capture.window; if(prev && (prev != wd)) @@ -953,6 +953,7 @@ namespace detail else if(wd == attr_.capture.window) { attr_.capture.window = nullptr; + wd->flags.captured = false; if(attr_cap.size()) { std::pair last = attr_cap.back(); @@ -964,6 +965,7 @@ namespace detail attr_.capture.ignore_children = last.second; native_interface::capture_window(last.first->root, true); native_interface::calc_window_point(last.first->root, pos); + last.first->flags.captured = true; attr_.capture.inside = _m_effective(last.first, pos); } } diff --git a/source/gui/element.cpp b/source/gui/element.cpp index 6535f01d..5b6cf192 100644 --- a/source/gui/element.cpp +++ b/source/gui/element.cpp @@ -476,7 +476,57 @@ namespace nana graph.set_pixel(x, y + 5); } } - }; + };//end class arrow_double + + class annex_button + : public element_interface + { + bool draw(graph_reference graph, const ::nana::color& arg_bgcolor, const ::nana::color& fgcolor, const rectangle& r, element_state estate) override + { + auto bgcolor = arg_bgcolor; + + switch (estate) + { + case element_state::hovered: + case element_state::focus_hovered: + bgcolor = arg_bgcolor.blend(colors::white, 0.8); + break; + case element_state::pressed: + bgcolor = arg_bgcolor.blend(colors::black, 0.8); + break; + default: + break; + } + + auto part_px = (r.height - 3) * 5 / 13; + graph.rectangle(r, false, bgcolor.blend(colors::black, 0.6)); + + ::nana::point left_top{ r.x + 1, r.y + 1 }, right_top{r.right() - 2, r.y + 1}; + ::nana::point left_mid{ r.x + 1, r.y + 1 + static_cast(part_px) }, right_mid{ right_top.x, left_mid.y }; + ::nana::point left_bottom{ r.x + 1, r.bottom() - 2 }, right_bottom{ r.right() - 2, r.bottom() - 2 }; + + graph.set_color(bgcolor.blend(colors::white, 0.9)); + graph.line(left_top, left_mid); + graph.line(right_top, right_mid); + + graph.set_color(bgcolor.blend(colors::white, 0.5)); + graph.line(left_top, right_top); + + left_mid.y++; + right_mid.y++; + graph.set_color(bgcolor.blend(colors::black, 0.8)); + graph.line(left_mid, left_bottom); + graph.line(right_mid, right_bottom); + + ::nana::rectangle part_r{ r.x + 2, r.y + 2, r.width - 4, part_px }; + graph.rectangle(part_r, true, bgcolor.blend(colors::white, 0.8)); + + part_r.y += static_cast(part_r.height); + part_r.height = (r.height - 3 - part_r.height); + graph.rectangle(part_r, true, bgcolor); + return true; + } + };//end class annex_button }//end namespace element template @@ -558,10 +608,12 @@ namespace nana element::add_border(""); - element::add_arrow(""); + element::add_arrow(""); //"arrowhead" in default element::add_arrow("double"); element::add_arrow("solid_triangle"); element::add_arrow("hollow_triangle"); + + element::add_button(""); //"annex" in default } return obj; } @@ -595,6 +647,16 @@ namespace nana { return _m_get((name.empty() ? "arrowhead" : name), arrow_).keeper(); } + + void button(const std::string& name, const pat::cloneable>& factory) + { + _m_add((name.empty() ? "annex" : name), button_, factory); + } + + element::element_interface * const * button(const std::string& name) const + { + return _m_get((name.empty() ? "annex" : name), button_).keeper(); + } private: using lock_guard = std::lock_guard; @@ -630,6 +692,7 @@ namespace nana item crook_; item border_; item arrow_; + item button_; }; namespace element @@ -664,6 +727,16 @@ namespace nana { return element_manager::instance().arrow(name); } + + void provider::add_button(const std::string& name, const pat::cloneable>& factory) + { + element_manager::instance().button(name, factory); + } + + element_interface* const* provider::keeper_button(const std::string& name) + { + return element_manager::instance().button(name); + } }//end namespace element //facades @@ -705,7 +778,7 @@ namespace nana void facade::switch_to(const char* name) { - keeper_ = element::provider().keeper_crook(name); + keeper_ = element::provider().keeper_crook(name ? name : ""); } bool facade::draw(graph_reference graph, const ::nana::color& bgcol, const ::nana::color& fgcol, const nana::rectangle& r, element_state es) @@ -721,7 +794,7 @@ namespace nana void facade::switch_to(const char* name) { - keeper_ = element::provider().keeper_border(name); + keeper_ = element::provider().keeper_border(name ? name : ""); } bool facade::draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state es) @@ -754,6 +827,23 @@ namespace nana } //end class facade + //class facade:: + facade::facade(const char* name) + : keeper_(element::provider().keeper_button(name ? name : "")) + {} + + void facade::switch_to(const char* name) + { + keeper_ = element::provider().keeper_button(name ? name : ""); + } + + //Implement element_interface + bool facade::draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate) + { + return (*keeper_)->draw(graph, bgcolor, fgcolor, r, estate); + } + //end class facade + namespace element { void set_bground(const char* name, const pat::cloneable& obj) diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 6dbd0101..5ab4e8d6 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -58,16 +58,15 @@ namespace nana class drawer_impl { public: - typedef nana::paint::graphics & graph_reference; - typedef widget & widget_reference; + using graph_reference = paint::graphics&; + using widget_reference = widget&; enum class where_t{unknown, text, push_button}; - enum class state_t{none, mouse_over, pressed}; drawer_impl() { state_.focused = false; - state_.state = state_t::none; + state_.button_state = element_state::normal; state_.pointer_where = where_t::unknown; state_.lister = nullptr; } @@ -195,13 +194,13 @@ namespace nana void set_mouse_over(bool mo) { - state_.state = mo ? state_t::mouse_over : state_t::none; + state_.button_state = (mo ? element_state::hovered : element_state::normal); state_.pointer_where = where_t::unknown; } void set_mouse_press(bool mp) { - state_.state = (mp ? state_t::pressed : state_t::mouse_over); + state_.button_state = (mp ? element_state::pressed : element_state::hovered); } void set_focused(bool f) @@ -310,8 +309,8 @@ namespace nana { auto pos = API::cursor_position(); API::calc_window_point(widget_->handle(), pos); - if(calc_where(*graph_, pos.x, pos.y)) - state_.state = state_t::none; + if (calc_where(*graph_, pos.x, pos.y)) + state_.button_state = element_state::normal; editor_->text(items_[index]->item_text); _m_draw_push_button(widget_->enabled()); @@ -437,7 +436,8 @@ namespace nana ::nana::rectangle r(graph.size()); auto clr_from = colors::button_face_shadow_start; auto clr_to = colors::button_face_shadow_end; - if(state_.state == state_t::pressed) + + if (element_state::pressed == state_.button_state) { r.pare_off(2); std::swap(clr_from, clr_to); @@ -450,54 +450,30 @@ namespace nana void _m_draw_push_button(bool enabled) { - using namespace nana::paint; + ::nana::rectangle r{graph_->size()}; + r.x = r.right() - 16; + r.y = 1; + r.width = 16; + r.height -= 2; - if (nullptr == graph_) return; - - int left = graph_->width() - 17; - int right = left + 16; - int top = 1; - int bottom = graph_->height() - 2; - int mid = top + (bottom - top) * 5 / 18; - - ::nana::color topcol, topcol_ln, botcol, botcol_ln; - ::nana::color arrow_color{ colors::white }; - if (enabled && items_.size()) + auto estate = state_.button_state; + if (enabled && !items_.empty()) { - double percent = 1; - if (has_lister() || (state_.state == state_t::pressed && state_.pointer_where == where_t::push_button)) - percent = 0.8; - else if (state_.state == state_t::mouse_over) - percent = 0.9; - - topcol_ln = color{ 0x3F, 0x47, 0x6C }.blend(arrow_color, percent); - botcol_ln = color{ 0x03, 0x31, 0x114 }.blend(arrow_color, percent); - topcol = color{ 0x3F, 83, 84 }.blend(arrow_color, percent); - botcol = color{ 0x0c, 0x4a, 0x9a }.blend(arrow_color, percent); + if (has_lister() || (element_state::pressed == estate && state_.pointer_where == where_t::push_button)) + estate = element_state::pressed; } else - { - topcol_ln = { 0x7F, 0x7F, 0x7F }; - botcol_ln = { 0x50, 0x50, 0x50 }; - topcol = { 0xC3, 0xC3, 0xC3 }; - botcol = { 0xA0, 0xA0, 0xA0 }; - } + estate = element_state::disabled; - graph_->set_color(topcol_ln); - graph_->line({ left, top }, { left, mid }); - graph_->line({ right - 1, top }, { right - 1, mid }); - - graph_->set_color(botcol_ln); - graph_->line({ left, mid + 1 }, { left, bottom }); - graph_->line({ right - 1, mid + 1 }, { right - 1, bottom }); - - - graph_->rectangle({ left + 1, top, static_cast(right - left - 2), static_cast(mid - top + 1) }, true, topcol); - graph_->rectangle({ left + 1, mid + 1, static_cast(right - left - 2), static_cast(bottom - mid) }, true, botcol); + facade button; + button.draw(*graph_, ::nana::color{ 3, 65, 140 }, colors::white, r, estate); facade arrow("solid_triangle"); arrow.direction(::nana::direction::south); - arrow.draw(*graph_, {}, arrow_color, { left, top + (bottom - top) / 2 - 7, 16, 16 }, element_state::normal); + + r.y += (r.height / 2) - 7; + r.width = r.height = 16; + arrow.draw(*graph_, {}, colors::white, r, element_state::normal); } void _m_draw_image() @@ -558,7 +534,7 @@ namespace nana struct state_type { bool focused; - state_t state; + element_state button_state; where_t pointer_where; nana::float_listbox * lister; diff --git a/source/gui/widgets/date_chooser.cpp b/source/gui/widgets/date_chooser.cpp index 031ec41b..0d25bccc 100644 --- a/source/gui/widgets/date_chooser.cpp +++ b/source/gui/widgets/date_chooser.cpp @@ -66,25 +66,24 @@ namespace nana this->monthstr_[index] = str; } - trigger::where trigger::_m_pos_where(graph_reference graph, int x, int y) + trigger::where trigger::_m_pos_where(graph_reference graph, const ::nana::point& pos) { int xend = static_cast(graph.width()) - 1; int yend = static_cast(graph.height()) - 1; - if(0 < y && y < static_cast(topbar_height)) + if(0 < pos.y && pos.y < static_cast(topbar_height)) { - if(static_cast(border_size) < x && x < xend) + if(static_cast(border_size) < pos.x && pos.x < xend) { - if(x < border_size + 16) + if(pos.x < border_size + 16) return where::left_button; - else if(xend - border_size - 16 < x) + else if(xend - border_size - 16 < pos.x) return where::right_button; return where::topbar; } } - else if(topbar_height < y && y < yend) + else if(topbar_height < pos.y && pos.y < yend) { - trace_pos_.x = x; - trace_pos_.y = y; + trace_pos_ = pos; return where::textarea; } return where::none; @@ -476,7 +475,7 @@ namespace nana void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { - where pos = _m_pos_where(graph, arg.pos.x, arg.pos.y); + where pos = _m_pos_where(graph, arg.pos); if(pos == pos_ && pos_ != where::textarea) return; pos_ = pos; _m_draw(graph); @@ -494,7 +493,7 @@ namespace nana void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) { bool redraw = true; - where pos = _m_pos_where(graph, arg.pos.x, arg.pos.y); + where pos = _m_pos_where(graph, arg.pos); transform_action tfid = transform_action::none; if(pos == where::topbar) diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp new file mode 100644 index 00000000..c34e88d6 --- /dev/null +++ b/source/gui/widgets/spinbox.cpp @@ -0,0 +1,483 @@ +/* + * A Spin box widget + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/widgets/spanbox.cpp + */ + +#include +#include +#include +#include + +namespace nana +{ + namespace drawerbase + { + namespace spinbox + { + enum class buttons + { + none, increase, decrease + }; + + class range_interface + { + public: + virtual ~range_interface() = default; + + virtual std::wstring value() const = 0; + virtual void spin(bool increase) = 0; + }; + + template + class range_numeric + : public range_interface + { + public: + range_numeric(T vbegin, T vlast, T step) + : begin_{ vbegin }, last_{ vlast }, step_{ step }, value_{ vbegin } + {} + + std::wstring value() const override + { + std::wstringstream ss; + ss << value_; + return ss.str(); + } + + void spin(bool increase) override + { + if (increase) + { + value_ += step_; + if (value_ > last_) + value_ = last_; + } + else + { + value_ -= step_; + if (value_ < begin_) + value_ = begin_; + } + } + private: + T begin_; + T last_; + T step_; + T value_; + }; + + class range_text + : public range_interface + { + public: + range_text(std::initializer_list & initlist) + { + for (auto & s : initlist) + { + texts_.emplace_back(::nana::charset(s, ::nana::unicode::utf8)); + } + } + + range_text(std::initializer_list& initlist) + : texts_(initlist) + {} + + std::wstring value() const override + { + if (texts_.empty()) + return{}; + + return texts_[pos_]; + } + + void spin(bool increase) override + { + if (texts_.empty()) + return; + + if (increase) + { + ++pos_; + if (texts_.size() <= pos_) + pos_ = texts_.size() - 1; + } + else + { + --pos_; + if (texts_.size() <= pos_) + pos_ = 0; + } + } + private: + std::vector texts_; + std::size_t pos_{0}; + }; + + class implementation + { + public: + implementation() + { + //Sets a timer for continous spin when mouse button is pressed. + timer_.elapse([this] + { + range_->spin(buttons::increase == spin_stated_); + _m_text(); + API::update_window(editor_->window_handle()); + + auto intv = timer_.interval(); + if (intv > 50) + timer_.interval(intv / 2); + }); + + timer_.interval(1000); + } + + void attach(::nana::widget& wdg, ::nana::paint::graphics& graph) + { + auto wd = wdg.handle(); + graph_ = &graph; + auto scheme = static_cast<::nana::widgets::skeletons::text_editor_scheme*>(API::dev::get_scheme(wd)); + editor_ = new ::nana::widgets::skeletons::text_editor(wd, graph, scheme); + editor_->multi_lines(false); + + if (!range_) + range_.reset(new range_numeric(0, 100, 1)); + + _m_text(); + + API::tabstop(wd); + API::eat_tabstop(wd, true); + API::effects_edge_nimbus(wd, effects::edge_nimbus::active); + API::effects_edge_nimbus(wd, effects::edge_nimbus::over); + _m_reset_text_area(); + } + + void detach() + { + delete editor_; + editor_ = nullptr; + } + + void set_range(std::unique_ptr ptr) + { + range_.swap(ptr); + + _m_text(); + } + + void qualify(std::wstring&& prefix, std::wstring&& suffix) + { + surround_.prefix = std::move(prefix); + surround_.suffix = std::move(suffix); + + if (editor_) + { + _m_text(); + API::update_window(editor_->window_handle()); + } + } + + void render() + { + editor_->render(API::is_focus_window(editor_->window_handle())); + _m_draw_spins(spin_stated_); + } + + ::nana::widgets::skeletons::text_editor* editor() const + { + return editor_; + } + + void mouse_wheel(bool upwards) + { + range_->spin(!upwards); + _m_text(); + } + + bool mouse_button(const ::nana::arg_mouse& arg, bool pressed) + { + if (!pressed) + { + API::capture_window(editor_->window_handle(), false); + timer_.stop(); + timer_.interval(1000); + } + + if (buttons::none != spin_stated_) + { + //Spins the value when mouse button is released + if (pressed) + { + API::capture_window(editor_->window_handle(), true); + range_->spin(buttons::increase == spin_stated_); + _m_text(); + timer_.start(); + } + _m_draw_spins(spin_stated_); + return true; + } + + + bool refreshed = false; + if (pressed) + refreshed = editor_->mouse_down(arg.left_button, arg.pos); + else + refreshed = editor_->mouse_up(arg.left_button, arg.pos); + + if (refreshed) + _m_draw_spins(buttons::none); + + return refreshed; + } + + bool mouse_move(bool left_button, const ::nana::point& pos) + { + if (editor_->mouse_move(left_button, pos)) + { + editor_->reset_caret(); + render(); + return true; + } + + auto btn = _m_where(pos); + if (buttons::none != btn) + { + spin_stated_ = btn; + _m_draw_spins(btn); + return true; + } + else if (buttons::none != spin_stated_) + { + spin_stated_ = buttons::none; + _m_draw_spins(buttons::none); + return true; + } + + return false; + } + private: + void _m_text() + { + if (editor_) + { + std::wstring text = surround_.prefix + range_->value() + surround_.suffix; + editor_->text(std::move(text)); + _m_draw_spins(spin_stated_); + } + } + + void _m_reset_text_area() + { + auto spins_r = _m_spins_area(); + if (spins_r.x == 0) + editor_->text_area({}); + else + editor_->text_area({ 2, 2, graph_->width() - spins_r.width - 2, spins_r.height - 2 }); + } + + ::nana::rectangle _m_spins_area() const + { + auto size = API::window_size(editor_->window_handle()); + if (size.width > 18) + return{ static_cast(size.width - 16), 0, 16, size.height }; + + return{ 0, 0, size.width, size.height }; + } + + buttons _m_where(const ::nana::point& pos) const + { + auto spins_r = _m_spins_area(); + if (spins_r.is_hit(pos)) + { + if (pos.y < spins_r.y + static_cast(spins_r.height / 2)) + return buttons::increase; + + return buttons::decrease; + } + return buttons::none; + } + + void _m_draw_spins(buttons spins) + { + auto estate = API::element_state(editor_->window_handle()); + + auto spin_r0 = _m_spins_area(); + spin_r0.height /= 2; + + auto spin_r1 = spin_r0; + spin_r1.y += static_cast(spin_r0.height); + spin_r1.height = _m_spins_area().height - spin_r0.height; + + ::nana::color bgcolor{ 3, 65, 140 }; + facade arrow; + facade button; + + auto spin_state = (buttons::increase == spins ? estate : element_state::normal); + button.draw(*graph_, bgcolor, colors::white, spin_r0, spin_state); + spin_r0.x += 5; + arrow.draw(*graph_, bgcolor, colors::white, spin_r0, spin_state); + + spin_state = (buttons::decrease == spins ? estate : element_state::normal); + button.draw(*graph_, bgcolor, colors::white, spin_r1, spin_state); + spin_r1.x += 5; + arrow.direction(direction::south); + arrow.draw(*graph_, bgcolor, colors::white, spin_r1, spin_state); + } + private: + ::nana::paint::graphics * graph_{nullptr}; + ::nana::widgets::skeletons::text_editor * editor_{nullptr}; + buttons spin_stated_{ buttons::none }; + std::unique_ptr range_; + ::nana::timer timer_; + + struct surround_data + { + std::wstring prefix; + std::wstring suffix; + }surround_; + }; + + //class drawer + drawer::drawer() + : impl_(new implementation) + {} + + drawer::~drawer() + { + delete impl_; + } + + implementation* drawer::impl() const + { + return impl_; + } + + //Overrides drawer_trigger + void drawer::attached(widget_reference wdg, graph_reference graph) + { + impl_->attach(wdg, graph); + } + + void drawer::refresh(graph_reference) + { + impl_->render(); + } + + void drawer::focus(graph_reference, const arg_focus&) + { + impl_->render(); + impl_->editor()->reset_caret(); + API::lazy_refresh(); + } + + void drawer::mouse_wheel(graph_reference, const arg_wheel& arg) + { + impl_->mouse_wheel(arg.upwards); + impl_->editor()->reset_caret(); + API::lazy_refresh(); + } + + void drawer::mouse_down(graph_reference, const arg_mouse& arg) + { + if (impl_->mouse_button(arg, true)) + API::lazy_refresh(); + } + + void drawer::mouse_up(graph_reference, const arg_mouse& arg) + { + if (impl_->mouse_button(arg, false)) + API::lazy_refresh(); + } + + void drawer::mouse_move(graph_reference, const arg_mouse& arg) + { + if (impl_->mouse_move(arg.left_button, arg.pos)) + API::lazy_refresh(); + } + + void drawer::mouse_leave(graph_reference, const arg_mouse&) + { + impl_->render(); + API::lazy_refresh(); + } + + } + }//end namespace drawerbase + + spinbox::spinbox() + {} + + spinbox::spinbox(window wd, bool visible) + { + this->create(wd, visible); + } + + spinbox::spinbox(window wd, const nana::rectangle& r, bool visible) + { + this->create(wd, r, visible); + } + + void spinbox::range(int begin, int last, int step) + { + using namespace drawerbase::spinbox; + get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_numeric(begin, last, step))); + API::refresh_window(handle()); + } + + void spinbox::range(double begin, double last, double step) + { + using namespace drawerbase::spinbox; + get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_numeric(begin, last, step))); + API::refresh_window(handle()); + } + + void spinbox::range(std::initializer_list steps_utf8) + { + using namespace drawerbase::spinbox; + get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_text(steps_utf8))); + API::refresh_window(handle()); + } + + void spinbox::range(std::initializer_list steps) + { + using namespace drawerbase::spinbox; + get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_text(steps))); + API::refresh_window(handle()); + } + + 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))); + } + + ::nana::string spinbox::_m_caption() const + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().impl()->editor(); + return (editor ? editor->text() : nana::string()); + } + + void spinbox::_m_caption(::nana::string&& text) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().impl()->editor(); + if (editor) + { + editor->text(std::move(text)); + API::refresh_window(*this); + } + } +}//end namespace nana \ No newline at end of file