diff --git a/.travis.yml b/.travis.yml index 170ef287..4543e6b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ matrix: before_install: - git clone --depth=1 --branch=develop https://github.com/qPCR4vir/nana-demo.git ../nana-demo - export PATH="$HOME/bin:$PATH" - - mkdir ~/bin + #- mkdir ~/bin #it seemd that a bin already exists from 20170901 - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.4/cmake-3.4.0-rc3-Linux-x86_64.sh || true - chmod -R +x /tmp/tools diff --git a/build/makefile/readme.md b/build/makefile/readme.md index 38ce2141..c61050ea 100644 --- a/build/makefile/readme.md +++ b/build/makefile/readme.md @@ -14,7 +14,7 @@ NANAINC = $(NANAPATH)/include NANALIB = $(NANAPATH)/build/bin INCS = -I$(NANAINC) -LIBS = -L$(NANALIB) -lnana -lX11 -lpthread -lrt -lXft -lpng -lasound +LIBS = -L$(NANALIB) -lnana -lX11 -lpthread -lrt -lXft -lpng -lasound -lfontconfig LINKOBJ = $(SOURCES:.cpp=.o) diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index 5462857d..0db691b3 100644 --- a/include/nana/gui/detail/bedrock.hpp +++ b/include/nana/gui/detail/bedrock.hpp @@ -91,9 +91,10 @@ namespace detail void manage_form_loader(core_window_t*, bool insert_or_remove); public: - bool emit(event_code, core_window_t*, const event_arg&, bool ask_update, thread_context*); + // if 'bForce__EmitInternal', then ONLY internal (widget's) events are processed (even through explicit filtering) + bool emit(event_code, core_window_t*, const event_arg&, bool ask_update, thread_context*, const bool bForce__EmitInternal = false); private: - void _m_emit_core(event_code, core_window_t*, bool draw_only, const event_arg&); + void _m_emit_core(event_code, core_window_t*, bool draw_only, const event_arg&, const bool bForce__EmitInternal); void _m_event_filter(event_code, core_window_t*, thread_context*); private: static bedrock bedrock_object; diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index 5a202438..63cea67f 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -17,6 +17,7 @@ #include "general_events.hpp" #include #include +#include namespace nana { @@ -27,6 +28,25 @@ namespace nana class drawer; } + class drawer_trigger; + class event_filter_status + { + public: + event_filter_status(); + event_filter_status(const event_filter_status& rOther); + event_filter_status(const unsigned evt_disabled_); + const event_filter_status& operator=(const event_filter_status& rOther); + const event_filter_status& operator=(const unsigned evt_disabled_); + + bool operator[](const nana::event_code evt_code) const; + bool operator==(const event_filter_status& rOther) const; + bool operator!=(const event_filter_status& rOther) const; + + private: + unsigned evt_disabled_; + friend class drawer_trigger; + }; + class drawer_trigger { friend class detail::drawer; @@ -70,11 +90,19 @@ namespace nana virtual void key_release(graph_reference, const arg_keyboard&); virtual void shortkey(graph_reference, const arg_keyboard&); + void filter_event(const event_code evt_code, const bool bDisabled); + void filter_event(const std::vector evt_codes, const bool bDisabled); + void filter_event(const event_filter_status& evt_all_states); + bool filter_event(const event_code evt_code); + event_filter_status filter_event(); + void clear_filter(); + private: void _m_reset_overrided(); bool _m_overrided(event_code) const; private: unsigned overrided_{ 0xFFFFFFFF }; + unsigned evt_disabled_{ 0 }; // bit set if event is filtered }; namespace detail @@ -99,23 +127,23 @@ namespace nana void bind(basic_window*); void typeface_changed(); - void click(const arg_click&); - void dbl_click(const arg_mouse&); - void mouse_enter(const arg_mouse&); - void mouse_move(const arg_mouse&); - void mouse_leave(const arg_mouse&); - void mouse_down(const arg_mouse&); - void mouse_up(const arg_mouse&); - void mouse_wheel(const arg_wheel&); - void mouse_dropfiles(const arg_dropfiles&); - void resizing(const arg_resizing&); - void resized(const arg_resized&); - void move(const arg_move&); - void focus(const arg_focus&); - void key_press(const arg_keyboard&); - void key_char(const arg_keyboard&); - void key_release(const arg_keyboard&); - void shortkey(const arg_keyboard&); + void click(const arg_click&, const bool); + void dbl_click(const arg_mouse&, const bool); + void mouse_enter(const arg_mouse&, const bool); + void mouse_move(const arg_mouse&, const bool); + void mouse_leave(const arg_mouse&, const bool); + void mouse_down(const arg_mouse&, const bool); + void mouse_up(const arg_mouse&, const bool); + void mouse_wheel(const arg_wheel&, const bool); + void mouse_dropfiles(const arg_dropfiles&, const bool); + void resizing(const arg_resizing&, const bool); + void resized(const arg_resized&, const bool); + void move(const arg_move&, const bool); + void focus(const arg_focus&, const bool); + void key_press(const arg_keyboard&, const bool); + void key_char(const arg_keyboard&, const bool); + void key_release(const arg_keyboard&, const bool); + void shortkey(const arg_keyboard&, const bool); void map(window, bool forced, const rectangle* update_area = nullptr); //Copy the root buffer to screen void refresh(); drawer_trigger* realizer() const; @@ -130,7 +158,7 @@ namespace nana method_state& _m_mth_state(int pos); template - void _m_emit(event_code evt_code, const Arg& arg, Mfptr mfptr) + void _m_emit(event_code evt_code, const Arg& arg, Mfptr mfptr, const bool bForce__EmitInternal) { const int pos = static_cast(evt_code); @@ -139,17 +167,22 @@ namespace nana if (realizer && (method_state::not_overrided != mth_state)) { + const bool bFiltered = !bForce__EmitInternal && realizer->filter_event(evt_code); if (method_state::pending == mth_state) { - (realizer->*mfptr)(graphics, arg); - + if (!bFiltered) + (realizer->*mfptr)(graphics, arg); + //Check realizer, when the window is closed in that event handler, the drawer will be //detached and realizer will be a nullptr if (realizer) mth_state = (realizer->_m_overrided(evt_code) ? method_state::overrided : method_state::not_overrided); } else - (realizer->*mfptr)(graphics, arg); + { + if (!bFiltered) + (realizer->*mfptr)(graphics, arg); + } _m_effect_bground_subsequent(); } diff --git a/include/nana/gui/msgbox.hpp b/include/nana/gui/msgbox.hpp index a53e2264..67840f1a 100644 --- a/include/nana/gui/msgbox.hpp +++ b/include/nana/gui/msgbox.hpp @@ -46,7 +46,7 @@ namespace nana /// Construct that creates a message box with a specified title and default button. msgbox(const ::std::string&); - /// Construct that creates a message box with an owner windoow, a specified title and buttons. + /// Construct that creates a message box with an owner windoow, a specified title and buttons. msgbox(window, const ::std::string&, button_t = ok); /// Sets an icon for informing user. @@ -108,6 +108,7 @@ namespace nana virtual window create(window, unsigned label_px) = 0; virtual unsigned fixed_pixels() const; }; + public: class boolean : public abstract_content @@ -240,7 +241,6 @@ namespace nana { std::vector contents; _m_fetch_args(contents, std::forward(args)...); - if (contents.empty()) return false; @@ -261,6 +261,14 @@ namespace nana /// Sets a verifier to verify the user input. void verify(std::function verifier); + + /** Sets the minimum width for the entry fields + @param[in] pixels new minimum width + + If not called, the default is 100 pixels + */ + void min_width_entry_field( unsigned pixels ); + private: void _m_fetch_args(std::vector&); @@ -279,6 +287,7 @@ namespace nana std::function verifier_; ::nana::paint::image images_[4]; ::nana::rectangle valid_areas_[4]; + unsigned min_width_entry_field_pixels_; }; }//end namespace nana #include diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 0a1d55d8..8c0d85fe 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -106,6 +106,8 @@ namespace API * This function will copy the drawer surface into system window after the event process finished. */ void lazy_refresh(); + + void draw_shortkey_underline(paint::graphics&, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color&); }//end namespace dev /// Returns the widget pointer of the specified window. @@ -118,8 +120,13 @@ namespace API namespace detail { general_events* get_general_events(window); + + // emits both internal and external event (internal event can be filtered) bool emit_event(event_code, window, const ::nana::event_arg&); + // explicitly emits internal event (internal event not to be filtered) + bool emit_internal_event(event_code, window, const ::nana::event_arg&); + class enum_widgets_function_base { public: @@ -253,6 +260,12 @@ namespace API return detail::emit_event(evt_code, wd, arg); } + template::value>::type* = nullptr> + bool emit_internal_event(event_code evt_code, window wd, const EventArg& arg) + { + return detail::emit_internal_event(evt_code, wd, arg); + } + void umake_event(event_handle); template diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 212be2f7..71280b7e 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -535,7 +535,7 @@ namespace nana throw std::invalid_argument("invalid listbox model container type"); if (nullptr == p->pointer()) - throw std::runtime_error("the modal is immutable"); + throw std::runtime_error("the modal is immutable, please declare model_guard with const"); return *static_cast(p->pointer()); } @@ -1250,8 +1250,9 @@ By \a clicking on one header the list get \a reordered, first up, and then down } return false; } - listbox.anyobj(0, 0, 10); //the type of customer's object is int. - listbox.anyobj(0, 0, 20); + auto cat = listbox.at(0); + cat.at(0).value(10); //10 is custom data. + cat.at(1).value(20); //20 is custom data. 5. listbox is a widget_object, with template parameters drawerbase::listbox::trigger and drawerbase::listbox::scheme amon others. That means that listbox have a member trigger_ constructed first and accecible with get_drawer_trigger() and @@ -1402,7 +1403,9 @@ the nana::detail::basic_window member pointer scheme /// Scrolls the view to the first or last item of a specified category void scroll(bool to_bottom, size_type cat_pos = ::nana::npos); - void scroll(bool to_bottom, const index_pair& pos); + + /// Scrolls the view to show an item sepcified by absolute position at top/bottom of the listbox. + void scroll(bool to_bottom, const index_pair& abs_pos); /// Appends a new column with a header text and the specified width at the end, and return it position size_type append_header(std::string text_utf8, unsigned width = 120); diff --git a/include/nana/gui/widgets/menu.hpp b/include/nana/gui/widgets/menu.hpp index f8ec1167..b2315ac0 100644 --- a/include/nana/gui/widgets/menu.hpp +++ b/include/nana/gui/widgets/menu.hpp @@ -181,6 +181,11 @@ namespace nana void renderer(const pat::cloneable&); ///< Sets a user-defined renderer. const pat::cloneable& renderer() const; + /// Returns the handle of menu window + /** + * @return handle of menu window, nullptr if the menu hasn't been popped up. + */ + window handle() const; private: void _m_popup(window, const point& position, bool called_by_menubar); private: diff --git a/include/nana/gui/widgets/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index a76b21bb..24c965b5 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -1,7 +1,7 @@ /* * A Menubar implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2009-2014 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2009-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -57,13 +57,11 @@ namespace nana class trigger : public drawer_trigger { - class itembase; + struct essence; public: trigger(); ~trigger(); - nana::menu* push_back(const std::string&); - nana::menu* at(size_t) const; - std::size_t size() const; + essence& ess() const; private: void attached(widget_reference, graph_reference) override; void refresh(graph_reference) override; @@ -76,38 +74,9 @@ namespace nana void key_release(graph_reference, const arg_keyboard&) override; void shortkey(graph_reference, const arg_keyboard&) override; private: - void _m_move(bool to_left); - bool _m_popup_menu(); - void _m_total_close(); - bool _m_close_menu(); - std::size_t _m_item_by_pos(const ::nana::point&); - bool _m_track_mouse(const ::nana::point&); + void _m_move(graph_reference, bool to_left); private: - widget *widget_; - paint::graphics *graph_; - - itembase* items_; - - struct state_type - { - enum behavior_t - { - behavior_none, behavior_focus, behavior_menu, - }; - - state_type(); - - std::size_t active; - behavior_t behavior; - - bool menu_active; - bool passive_close; - - bool nullify_mouse; - - nana::menu *menu; - nana::point mouse_pos; - }state_; + essence * const ess_; }; }//end namespace menubar }//end namespace drawerbase @@ -126,6 +95,16 @@ namespace nana menu& push_back(const std::string&); ///< Appends a new (empty) menu. menu& at(size_t index) const; ///< Gets the menu specified by index. std::size_t length() const; ///< Number of menus. + + /// Deselects the menu + /** + * If a menu is popped up, the menu deselects the item and close the popuped menu. + * @return true if an item is deselected, false otherwise. + */ + bool cancel(); + + /// Determines the mouse is hovered on the menubar or its popped menu. + bool hovered() const; private: ::nana::event_handle evt_resized_{nullptr}; };//end class menubar diff --git a/include/nana/gui/widgets/progress.hpp b/include/nana/gui/widgets/progress.hpp index b4f692db..f43da854 100644 --- a/include/nana/gui/widgets/progress.hpp +++ b/include/nana/gui/widgets/progress.hpp @@ -1,7 +1,7 @@ /** * A Progress Indicator 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 @@ -20,42 +20,37 @@ namespace nana { namespace progress { - class trigger: public drawer_trigger + struct scheme + : public widget_geometrics + { + scheme(); + + color_proxy gradient_bgcolor{ colors::button_face_shadow_start }; + color_proxy gradient_fgcolor{ static_cast(0x6FFFA8) }; + }; + + class substance; + + class trigger + : public drawer_trigger { public: - unsigned value() const; - unsigned value(unsigned); - unsigned inc(); - unsigned Max() const; - unsigned Max(unsigned); - void unknown(bool); - bool unknown() const; - bool stop(bool s = true); - bool stopped() const; - private: - void attached(widget_reference, graph_reference) override; - void refresh(graph_reference) override; - private: - void _m_draw_box(graph_reference); - void _m_draw_progress(graph_reference); - bool _m_check_changing(unsigned) const; - private: - static const unsigned border = 2; + trigger(); + ~trigger(); - widget * widget_{nullptr}; - nana::paint::graphics* graph_{nullptr}; - unsigned draw_width_{static_cast(-1)}; - bool unknown_{false}; - bool stop_{false}; - unsigned max_{100}; - unsigned value_{0}; - }; //end class drawer + substance* progress() const; + private: + void attached(widget_reference, graph_reference) override; + void refresh(graph_reference) override; + private: + substance* const progress_; + }; } }//end namespace drawerbase /// \brief A progressbar widget with two styles: know, and unknow amount value (goal). /// In unknow style the amount is ignored and the bar is scrolled when value change. - class progress - : public widget_object + class progress + : public widget_object { public: progress(); @@ -69,8 +64,6 @@ namespace nana unsigned amount(unsigned value); void unknown(bool); bool unknown() const; - bool stop(bool s=true); ///< request stop or cancel and return previus stop status - bool stopped() const; }; }//end namespace nana #endif diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 922fbe3b..0ee37703 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -206,7 +206,7 @@ namespace nana API::dev::attach_drawer(*this, trigger_); if(visible) API::show_window(handle_, true); - + this->_m_complete_creation(); } return (this->empty() == false); @@ -227,6 +227,39 @@ namespace nana { return *scheme_; } + + // disables or re-enables internal handling of event within base-widget + void filter_event(const event_code evt_code, const bool bDisabled) + { + trigger_.filter_event(evt_code, bDisabled); + } + + void filter_event(const std::vector evt_codes, const bool bDisabled) + { + trigger_.filter_event(evt_codes, bDisabled); + } + + void filter_event(const event_filter_status& evt_all_states) + { + trigger_.filter_event(evt_all_states); + } + + void clear_filter() + { + trigger_.clear_filter(); + } + + // reads status of if event is filtered + bool filter_event(const event_code evt_code) + { + return trigger_.filter_event(evt_code); + } + + event_filter_status filter_event() + { + return trigger_.filter_event(); + } + protected: DrawerTrigger& get_drawer_trigger() { diff --git a/source/audio/detail/buffer_preparation.cpp b/source/audio/detail/buffer_preparation.cpp index 82942846..febe3a77 100644 --- a/source/audio/detail/buffer_preparation.cpp +++ b/source/audio/detail/buffer_preparation.cpp @@ -78,7 +78,7 @@ namespace nana{ namespace audio void buffer_preparation::revert(meta * m) { std::lock_guard lock(token_prepared_); - bool if_signal = prepared_.empty(); + auto const if_signal = prepared_.empty(); prepared_.emplace_back(m); if(if_signal) cond_prepared_.notify_one(); diff --git a/source/detail/mswin/platform_spec.hpp b/source/detail/mswin/platform_spec.hpp index ed4b0d9a..30bc8b84 100644 --- a/source/detail/mswin/platform_spec.hpp +++ b/source/detail/mswin/platform_spec.hpp @@ -100,37 +100,6 @@ namespace detail font_type font; - struct pen_spec - { - HPEN handle; - unsigned color; - int style; - int width; - - void set(HDC context, int style, int width,unsigned color); - }pen; - - struct brush_spec - { - enum t{Solid, HatchBDiagonal}; - - HBRUSH handle; - t style; - unsigned color; - - void set(HDC context, t style, unsigned color); - }brush; - - struct round_region_spec - { - HRGN handle; - nana::rectangle r; - unsigned radius_x; - unsigned radius_y; - - void set(const nana::rectangle& r, unsigned radius_x, unsigned radius_y); - }round_region; - struct string_spec { unsigned tab_length; @@ -148,9 +117,6 @@ namespace detail unsigned get_text_color() const; void set_color(const ::nana::color&); void set_text_color(const ::nana::color&); - - void update_pen(); - void update_brush(); private: unsigned color_{ 0xffffffff }; unsigned text_color_{0xffffffff}; diff --git a/source/detail/platform_spec_windows.cpp b/source/detail/platform_spec_windows.cpp index 4bc09546..a3dc0b2a 100644 --- a/source/detail/platform_spec_windows.cpp +++ b/source/detail/platform_spec_windows.cpp @@ -1,7 +1,7 @@ /** * Platform Specification 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 @@ -31,18 +31,6 @@ namespace detail { drawable_impl_type::drawable_impl_type() { - pen.handle = nullptr; - pen.color = 0xffffffff; - pen.style = -1; - pen.width = -1; - - brush.handle = nullptr; - brush.style = brush_spec::Solid; - brush.color = 0xffffffff; - - round_region.handle = nullptr; - round_region.radius_x = round_region.radius_y = 0; - string.tab_length = 4; string.tab_pixels = 0; string.whitespace_pixels = 0; @@ -52,9 +40,6 @@ namespace detail { ::DeleteDC(context); ::DeleteObject(pixmap); - ::DeleteObject(pen.handle); - ::DeleteObject(brush.handle); - ::DeleteObject(round_region.handle); } unsigned drawable_impl_type::get_color() const @@ -82,68 +67,6 @@ namespace detail } } - void drawable_impl_type::update_pen() - { - if (pen.color != color_) - { - pen.handle = ::CreatePen(PS_SOLID, 1, NANA_RGB(color_)); - ::DeleteObject(::SelectObject(context, pen.handle)); - pen.color = color_; - } - } - - void drawable_impl_type::update_brush() - { - if (brush.color != color_) - brush.set(context, brush.style, color_); - } - - void drawable_impl_type::pen_spec::set(HDC context, int style, int width, unsigned clr) - { - if (this->color != clr || this->width != width || this->style != style) - { - this->color = clr; - this->width = width; - this->style = style; - this->handle = ::CreatePen(style, width, NANA_RGB(clr)); - ::DeleteObject(::SelectObject(context, this->handle)); - } - } - - void drawable_impl_type::brush_spec::set(HDC context, drawable_impl_type::brush_spec::t style, unsigned rgb) - { - if (this->color != rgb || this->style != style) - { - this->color = rgb; - this->style = style; - switch(style) - { - case brush_spec::HatchBDiagonal: - this->handle = ::CreateHatchBrush(HS_BDIAGONAL, NANA_RGB(rgb)); - break; - case brush_spec::Solid: - default: - this->style = brush_spec::Solid; - this->handle = ::CreateSolidBrush(NANA_RGB(color)); - break; - } - ::DeleteObject(::SelectObject(context, this->handle)); - } - } - - void drawable_impl_type::round_region_spec::set(const nana::rectangle& r, unsigned radius_x, unsigned radius_y) - { - if(this->r != r || this->radius_x != radius_x || this->radius_y != radius_y) - { - if(handle) - ::DeleteObject(this->handle); - this->r = r; - this->radius_x = radius_x; - this->radius_y = radius_y; - handle = ::CreateRoundRectRgn(r.x, r.y, r.x + static_cast(r.width) + 1, r.y + static_cast(r.height) + 1, static_cast(radius_x + 1), static_cast(radius_y + 1)); - } - } - //class platform_spec platform_spec::co_initializer::co_initializer() : ole32_(::LoadLibrary(L"OLE32.DLL")) diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 294c56f9..ec27ac07 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -379,11 +379,13 @@ namespace nana return pi_data_->scheme; } - void bedrock::_m_emit_core(event_code evt_code, core_window_t* wd, bool draw_only, const ::nana::event_arg& event_arg) + void bedrock::_m_emit_core(event_code evt_code, core_window_t* wd, bool draw_only, const ::nana::event_arg& event_arg, const bool bForce__EmitInternal) { auto retain = wd->annex.events_ptr; auto evts_ptr = retain.get(); + // if 'bForce__EmitInternal', omit user defined events + const bool bProcess__External_event = !draw_only && !bForce__EmitInternal; switch (evt_code) { case event_code::click: @@ -394,9 +396,9 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - wd->drawer.click(*arg); + wd->drawer.click(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evts_ptr->click.emit(*arg, reinterpret_cast(wd)); } } @@ -412,7 +414,7 @@ namespace nana if (nullptr == arg) return; - void(::nana::detail::drawer::*drawer_event_fn)(const arg_mouse&); + void(::nana::detail::drawer::*drawer_event_fn)(const arg_mouse&, const bool); ::nana::basic_event* evt_addr; switch (evt_code) @@ -448,10 +450,10 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - (wd->drawer.*drawer_event_fn)(*arg); + (wd->drawer.*drawer_event_fn)(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evt_addr->emit(*arg, reinterpret_cast(wd)); break; } @@ -463,10 +465,10 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - wd->drawer.mouse_wheel(*arg); + wd->drawer.mouse_wheel(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evts_ptr->mouse_wheel.emit(*arg, reinterpret_cast(wd)); } break; @@ -480,7 +482,7 @@ namespace nana if (nullptr == arg) return; - void(::nana::detail::drawer::*drawer_event_fn)(const arg_keyboard&); + void(::nana::detail::drawer::*drawer_event_fn)(const arg_keyboard&, const bool); ::nana::basic_event* evt_addr; switch (evt_code) @@ -507,15 +509,15 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - (wd->drawer.*drawer_event_fn)(*arg); + (wd->drawer.*drawer_event_fn)(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evt_addr->emit(*arg, reinterpret_cast(wd)); break; } case event_code::expose: - if (!draw_only) + if (bProcess__External_event) { auto arg = dynamic_cast(&event_arg); if (arg) @@ -530,9 +532,9 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - wd->drawer.focus(*arg); + wd->drawer.focus(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evts_ptr->focus.emit(*arg, reinterpret_cast(wd)); } break; @@ -545,9 +547,9 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - wd->drawer.move(*arg); + wd->drawer.move(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evts_ptr->move.emit(*arg, reinterpret_cast(wd)); } break; @@ -560,9 +562,9 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - wd->drawer.resizing(*arg); + wd->drawer.resizing(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evts_ptr->resizing.emit(*arg, reinterpret_cast(wd)); } break; @@ -575,15 +577,15 @@ namespace nana { //enable refreshing flag, this is a RAII class for exception-safe flag_guard fguard(this, wd); - wd->drawer.resized(*arg); + wd->drawer.resized(*arg, bForce__EmitInternal); } - if (!draw_only) + if (bProcess__External_event) evts_ptr->resized.emit(*arg, reinterpret_cast(wd)); } break; } case event_code::unload: - if (!draw_only) + if (bProcess__External_event) { auto arg = dynamic_cast(&event_arg); if (arg && (wd->other.category == category::flags::root)) @@ -595,7 +597,7 @@ namespace nana } break; case event_code::destroy: - if (!draw_only) + if (bProcess__External_event) { auto arg = dynamic_cast(&event_arg); if (arg) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 55c39b58..9b0e24e0 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -232,7 +232,7 @@ namespace detail //No implementation for Linux } - bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd) + bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) { if(wd_manager().available(wd) == false) return false; @@ -248,7 +248,7 @@ namespace detail if(wd->other.upd_state == core_window_t::update_state::none) wd->other.upd_state = core_window_t::update_state::lazy; - _m_emit_core(evt_code, wd, false, arg); + _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); //A child of wd may not be drawn if it was out of wd's range before wd resized, //so refresh all children of wd when a resized occurs. @@ -396,7 +396,7 @@ namespace detail } template - void draw_invoker(void(::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) + void draw_invoker(void(::nana::detail::drawer::*event_ptr)(const Arg&, const bool), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) { if(bedrock::instance().wd_manager().available(wd) == false) return; @@ -410,7 +410,7 @@ namespace detail if(wd->other.upd_state == basic_window::update_state::none) wd->other.upd_state = basic_window::update_state::lazy; - (wd->drawer.*event_ptr)(arg); + (wd->drawer.*event_ptr)(arg, false); if(thrd) thrd->event_window = pre_wd; } diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 68d996b3..ebf32705 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -755,7 +755,7 @@ namespace detail } template - void draw_invoker(void (::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) + void draw_invoker(void (::nana::detail::drawer::*event_ptr)(const Arg&, const bool), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) { if (bedrock::instance().wd_manager().available(wd) == false) return; @@ -770,7 +770,7 @@ namespace detail if (wd->other.upd_state == basic_window::update_state::none) wd->other.upd_state = basic_window::update_state::lazy; - (wd->drawer.*event_ptr)(arg); + (wd->drawer.*event_ptr)(arg, false); if (thrd) thrd->event_window = prev_event_wd; } @@ -1626,7 +1626,7 @@ namespace detail } } - bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd) + bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) { if (wd_manager().available(wd) == false) return false; @@ -1642,7 +1642,7 @@ namespace detail if (wd->other.upd_state == core_window_t::update_state::none) wd->other.upd_state = core_window_t::update_state::lazy; - _m_emit_core(evt_code, wd, false, arg); + _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); bool good_wd = false; if (wd_manager().available(wd)) diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index f73cb3ee..e6b9108a 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -128,8 +128,88 @@ namespace nana return 0 != (overrided_ & (1 << static_cast(evt_code))); } + void drawer_trigger::filter_event(const event_code evt_code, const bool bDisabled) + { + if (bDisabled) + evt_disabled_ |= 1 << static_cast(evt_code); // set + else + evt_disabled_ &= ~(1 << static_cast(evt_code)); // clear + } + + void drawer_trigger::filter_event(const std::vector evt_codes, const bool bDisabled) + { + const auto it_end = evt_codes.end(); + for (auto it = evt_codes.begin(); it != it_end; it++) + filter_event(*it, bDisabled); + } + + void drawer_trigger::filter_event(const event_filter_status& evt_all_states) + { + evt_disabled_ = evt_all_states.evt_disabled_; + } + + bool drawer_trigger::filter_event(const event_code evt_code) + { + return static_cast((evt_disabled_ >> static_cast(evt_code)) & 1); + } + + event_filter_status drawer_trigger::filter_event() + { + return event_filter_status(evt_disabled_); + } + + void drawer_trigger::clear_filter() + { + for (int i = 0; i < static_cast(nana::event_code::end); i++) + filter_event(static_cast(i), false); + } + //end class drawer_trigger + //class event_filter_status + event_filter_status::event_filter_status() + { + evt_disabled_ = 0; + } + + event_filter_status::event_filter_status(const event_filter_status& rOther) + { + this->evt_disabled_ = rOther.evt_disabled_; + } + + event_filter_status::event_filter_status(const unsigned evt_disabled_) + { + this->evt_disabled_ = evt_disabled_; + } + + bool event_filter_status::operator[](const nana::event_code evt_code) const + { + return static_cast((evt_disabled_ >> static_cast(evt_code)) & 1); + } + + bool event_filter_status::operator==(const event_filter_status& rOther) const + { + return evt_disabled_ == rOther.evt_disabled_; + } + + bool event_filter_status::operator!=(const event_filter_status& rOther) const + { + return evt_disabled_ != rOther.evt_disabled_; + } + + const event_filter_status& event_filter_status::operator=(const event_filter_status& rOther) + { + evt_disabled_ = rOther.evt_disabled_; + return *this; + } + + const event_filter_status& event_filter_status::operator=(const unsigned evt_disabled_) + { + this->evt_disabled_ = evt_disabled_; + return *this; + } + //end of class event_filter_status + namespace detail { typedef bedrock bedrock_type; @@ -174,89 +254,89 @@ namespace nana data_impl_->realizer->typeface_changed(graphics); } - void drawer::click(const arg_click& arg) + void drawer::click(const arg_click& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::click, arg, &drawer_trigger::click); + _m_emit(event_code::click, arg, &drawer_trigger::click, bForce__EmitInternal); } - void drawer::dbl_click(const arg_mouse& arg) + void drawer::dbl_click(const arg_mouse& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::dbl_click, arg, &drawer_trigger::dbl_click); + _m_emit(event_code::dbl_click, arg, &drawer_trigger::dbl_click, bForce__EmitInternal); } - void drawer::mouse_enter(const arg_mouse& arg) + void drawer::mouse_enter(const arg_mouse& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_enter, arg, &drawer_trigger::mouse_enter); + _m_emit(event_code::mouse_enter, arg, &drawer_trigger::mouse_enter, bForce__EmitInternal); } - void drawer::mouse_move(const arg_mouse& arg) + void drawer::mouse_move(const arg_mouse& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_move, arg, &drawer_trigger::mouse_move); + _m_emit(event_code::mouse_move, arg, &drawer_trigger::mouse_move, bForce__EmitInternal); } - void drawer::mouse_leave(const arg_mouse& arg) + void drawer::mouse_leave(const arg_mouse& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_leave, arg, &drawer_trigger::mouse_leave); + _m_emit(event_code::mouse_leave, arg, &drawer_trigger::mouse_leave, bForce__EmitInternal); } - void drawer::mouse_down(const arg_mouse& arg) + void drawer::mouse_down(const arg_mouse& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_down, arg, &drawer_trigger::mouse_down); + _m_emit(event_code::mouse_down, arg, &drawer_trigger::mouse_down, bForce__EmitInternal); } - void drawer::mouse_up(const arg_mouse& arg) + void drawer::mouse_up(const arg_mouse& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_up, arg, &drawer_trigger::mouse_up); + _m_emit(event_code::mouse_up, arg, &drawer_trigger::mouse_up, bForce__EmitInternal); } - void drawer::mouse_wheel(const arg_wheel& arg) + void drawer::mouse_wheel(const arg_wheel& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_wheel, arg, &drawer_trigger::mouse_wheel); + _m_emit(event_code::mouse_wheel, arg, &drawer_trigger::mouse_wheel, bForce__EmitInternal); } - void drawer::mouse_dropfiles(const arg_dropfiles& arg) + void drawer::mouse_dropfiles(const arg_dropfiles& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::mouse_drop, arg, &drawer_trigger::mouse_dropfiles); + _m_emit(event_code::mouse_drop, arg, &drawer_trigger::mouse_dropfiles, bForce__EmitInternal); } - void drawer::resizing(const arg_resizing& arg) + void drawer::resizing(const arg_resizing& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::resizing, arg, &drawer_trigger::resizing); + _m_emit(event_code::resizing, arg, &drawer_trigger::resizing, bForce__EmitInternal); } - void drawer::resized(const arg_resized& arg) + void drawer::resized(const arg_resized& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::resized, arg, &drawer_trigger::resized); + _m_emit(event_code::resized, arg, &drawer_trigger::resized, bForce__EmitInternal); } - void drawer::move(const arg_move& arg) + void drawer::move(const arg_move& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::move, arg, &drawer_trigger::move); + _m_emit(event_code::move, arg, &drawer_trigger::move, bForce__EmitInternal); } - void drawer::focus(const arg_focus& arg) + void drawer::focus(const arg_focus& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::focus, arg, &drawer_trigger::focus); + _m_emit(event_code::focus, arg, &drawer_trigger::focus, bForce__EmitInternal); } - void drawer::key_press(const arg_keyboard& arg) + void drawer::key_press(const arg_keyboard& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::key_press, arg, &drawer_trigger::key_press); + _m_emit(event_code::key_press, arg, &drawer_trigger::key_press, bForce__EmitInternal); } - void drawer::key_char(const arg_keyboard& arg) + void drawer::key_char(const arg_keyboard& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::key_char, arg, &drawer_trigger::key_char); + _m_emit(event_code::key_char, arg, &drawer_trigger::key_char, bForce__EmitInternal); } - void drawer::key_release(const arg_keyboard& arg) + void drawer::key_release(const arg_keyboard& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::key_release, arg, &drawer_trigger::key_release); + _m_emit(event_code::key_release, arg, &drawer_trigger::key_release, bForce__EmitInternal); } - void drawer::shortkey(const arg_keyboard& arg) + void drawer::shortkey(const arg_keyboard& arg, const bool bForce__EmitInternal) { - _m_emit(event_code::shortkey, arg, &drawer_trigger::shortkey); + _m_emit(event_code::shortkey, arg, &drawer_trigger::shortkey, bForce__EmitInternal); } void drawer::map(window wd, bool forced, const rectangle* update_area) //Copy the root buffer to screen diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index 72195e66..6c3b7578 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -114,8 +114,30 @@ namespace nana return false; } - for (auto* parent = wd->parent; parent; parent = parent->parent) + for (auto parent = wd->parent; parent; parent = parent->parent) { + if (category::flags::root == parent->other.category) + { + //visual rectangle of wd's parent + rectangle vrt_parent{parent->pos_root, parent->dimension}; + + point pos_root; + while (parent->parent) + { + pos_root -= native_interface::window_position(parent->root); + + if (!overlap(rectangle{ pos_root, parent->parent->root_widget->dimension }, vrt_parent, vrt_parent)) + return false; + + parent = parent->parent->root_widget; + } + + if (!overlap(vrt_parent, visual, visual)) + return false; + + return true; + } + if (!overlap(rectangle{ parent->pos_root, parent->dimension }, visual, visual)) return false; } diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 6d1ce3ca..701eb530 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -733,30 +733,6 @@ namespace detail } } - void sync_child_root_display(window_manager::core_window_t* wd) - { - for (auto & child : wd->children) - { - if (category::flags::root != child->other.category) - { - sync_child_root_display(child); - continue; - } - - auto const vs_parents = child->visible_parents(); - - if (vs_parents != child->visible) - { - native_interface::show_window(child->root, vs_parents, false); - } - else - { - if (child->visible != native_interface::is_window_visible(child->root)) - native_interface::show_window(child->root, child->visible, false); - } - } - } - //show //@brief: show or hide a window bool window_manager::show(core_window_t* wd, bool visible) @@ -792,14 +768,7 @@ namespace detail bedrock::instance().event_expose(wd, visible); if (nv) - { - if (visible && !wd->visible_parents()) - return true; - native_interface::show_window(nv, visible, wd->flags.take_active); - } - - sync_child_root_display(wd); } return true; } @@ -1851,6 +1820,9 @@ namespace detail if(wd->other.category != category::flags::root) //Not a root window impl_->wd_register.remove(wd); + + //Release graphics immediately. + wd->drawer.graphics.release(); } void window_manager::_m_move_core(core_window_t* wd, const point& delta) diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index f978bf1a..75ce6a23 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -8,6 +8,8 @@ * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/msgbox.hpp + * @Contributors + * James Bremner */ #include @@ -685,7 +687,7 @@ namespace nana { return impl_->empty_label_text; } - + window inputbox::boolean::create(window owner, unsigned label_px) { auto impl = impl_.get(); @@ -1258,7 +1260,8 @@ namespace nana inputbox::inputbox(window owner, ::std::string desc, ::std::string title) : owner_{ owner }, description_(std::move(desc)), - title_(std::move(title)) + title_(std::move(title)), + min_width_entry_field_pixels_( 100 ) {} void inputbox::image(::nana::paint::image img, bool is_left, const rectangle& valid_area) @@ -1280,6 +1283,17 @@ namespace nana verifier_ = std::move(verifier); } + //Inputbox set minimum width entry field(https://github.com/cnjinhao/nana/pull/234) + //Contributed by James Bremner + void inputbox::min_width_entry_field( unsigned pixels ) + { + // don't let the entry fields vanish entirely + if( pixels < 10 ) + pixels = 10; + + min_width_entry_field_pixels_ = pixels; + } + void inputbox::_m_fetch_args(std::vector&) {} @@ -1304,9 +1318,9 @@ namespace nana each_pixels.push_back(px.height); } - //Adjust the fixed_px for good looking - if (has_0_fixed_px && (fixed_px < 100)) - fixed_px = 100; + //Ensure that the entry fields are at least as wide as the minimum + if (has_0_fixed_px && (fixed_px < min_width_entry_field_pixels_ )) + fixed_px = min_width_entry_field_pixels_; inputbox_window input_wd(owner_, images_, valid_areas_, description_, title_, contents.size(), label_px + 10 + fixed_px, each_pixels); diff --git a/source/gui/place.cpp b/source/gui/place.cpp index f1b4a5ff..257e4656 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -3058,7 +3058,7 @@ namespace nana throw std::runtime_error("place.bind: it has already bound to a window."); impl_->window_handle = wd; - impl_->event_size_handle = API::events(wd).resized.connect([this](const arg_resized& arg) + impl_->event_size_handle = API::events(wd).resized.connect_unignorable([this](const arg_resized& arg) { if (impl_->root_division) { diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 4511dc95..b17be9cd 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -260,7 +260,7 @@ namespace nana notifier_->request_close(); }); - this->events().resized.connect([this](const arg_resized& arg) + this->events().resized.connect_unignorable([this](const arg_resized& arg) { rectangle r{ 0, 0, arg.width, 20 }; caption_.move(r); @@ -360,7 +360,7 @@ namespace nana API::set_parent_window(handle(), container_->handle()); this->move({ 1, 1 }); - container_->events().resized.connect([this](const arg_resized& arg) + container_->events().resized.connect_unignorable([this](const arg_resized& arg) { this->size({arg.width - 2, arg.height - 2}); }); diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 6f91756e..1fd7d158 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -62,7 +62,12 @@ namespace API bool emit_event(event_code evt_code, window wd, const ::nana::event_arg& arg) { - return restrict::bedrock.emit(evt_code, reinterpret_cast<::nana::detail::basic_window*>(wd), arg, true, restrict::bedrock.get_thread_context()); + return restrict::bedrock.emit(evt_code, reinterpret_cast<::nana::detail::basic_window*>(wd), arg, true, restrict::bedrock.get_thread_context(), false); + } + + bool emit_internal_event(event_code evt_code, window wd, const ::nana::event_arg& arg) + { + return restrict::bedrock.emit(evt_code, reinterpret_cast<::nana::detail::basic_window*>(wd), arg, true, restrict::bedrock.get_thread_context(), true); } void enum_widgets_function_base::enum_widgets(window wd, bool recursive) @@ -365,6 +370,24 @@ namespace API { restrict::bedrock.thread_context_lazy_refresh(); } + + void draw_shortkey_underline(paint::graphics& graph, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color& line_color) + { + if (shortkey) + { + auto off_x = (shortkey_position ? graph.text_extent_size(text.c_str(), shortkey_position).width : 0); + auto key_px = static_cast(graph.text_extent_size(&shortkey, 1).width); + + unsigned ascent, descent, inleading; + graph.text_metrics(ascent, descent, inleading); + + int x = text_pos.x + static_cast(off_x); + int y = text_pos.y + static_cast(ascent + 2); + + graph.line({ x, y }, {x + key_px - 1, y}, line_color); + + } + } }//end namespace dev diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 01de4f7c..a01d001b 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -34,12 +34,7 @@ namespace nana{ namespace drawerbase return{}; wchar_t shortkey; - std::string::size_type shortkey_pos; - - auto str = to_wstring(API::transform_shortkey_text(trigger_->wdg_->caption(), shortkey, &shortkey_pos)); - auto text_sz = graph.text_extent_size(str); - - return size{ text_sz.width, text_sz.height }; + return graph.text_extent_size(API::transform_shortkey_text(trigger_->wdg_->caption(), shortkey, nullptr)); } size extension() const override @@ -261,28 +256,15 @@ namespace nana{ namespace drawerbase ++pos.y; } - graph.palette(true, attr_.focus_color && attr_.focused ? ::nana::color(colors::blue) : attr_.fgcolor); + auto text_color = (attr_.focus_color && attr_.focused ? ::nana::color(colors::blue) : attr_.fgcolor); + graph.palette(true, text_color); if (attr_.omitted) tr.render(pos, txtptr, txtlen, omitted_pixels, true); else graph.bidi_string(pos, txtptr, txtlen); - if(shortkey) - { - unsigned off_w = (shortkey_pos ? graph.text_extent_size(mbstr.c_str(), shortkey_pos).width : 0); - - wchar_t keystr[2] = {nana::utf::char_at(mbstr.c_str() + shortkey_pos, 0, 0), 0}; - auto shortkey_size = graph.text_extent_size(keystr, 1); - - unsigned ascent, descent, inleading; - graph.text_metrics(ascent, descent, inleading); - - pos.x += off_w; - pos.y += static_cast(ascent + 2); - - graph.line(pos, point{ pos.x + static_cast(shortkey_size.width) - 1, pos.y }, colors::black); - } + API::dev::draw_shortkey_underline(graph, mbstr, shortkey, shortkey_pos, pos, text_color); } else { diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 4204dbe8..c9b6311d 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -143,6 +143,7 @@ namespace nana auto scheme = dynamic_cast< ::nana::widgets::skeletons::text_editor_scheme*>(API::dev::get_scheme(wd)); editor_ = new widgets::skeletons::text_editor(widget_->handle(), graph, scheme); + _m_text_area(graph.size()); editor_->multi_lines(false); editable(false); graph_ = &graph; @@ -177,21 +178,6 @@ namespace nana return any_ptr.get(); } - void text_area(const nana::size& s) - { - auto extension = measurer_->extension(); - - nana::rectangle r(2, 2, s.width > extension.width ? s.width - extension.width : 0, s.height > extension.height ? s.height - extension.height : 0); - if(image_enabled_) - { - unsigned place = image_pixels_ + 2; - r.x += place; - if(r.width > place) r.width -= place; - } - editor_->text_area(r); - editor_->render(state_.focused); - } - widgets::skeletons::text_editor * editor() const { return editor_; @@ -364,12 +350,13 @@ namespace nana void draw() { bool enb = widget_->enabled(); - if(editor_) - { - text_area(widget_->size()); - } + + _m_text_area(widget_->size()); + editor_->render(state_.focused); + _m_draw_push_button(enb); _m_draw_image(); + } std::size_t the_number_of_options() const @@ -496,6 +483,20 @@ namespace nana return true; } private: + void _m_text_area(const nana::size& s) + { + auto extension = measurer_->extension(); + + nana::rectangle r(2, 2, s.width > extension.width ? s.width - extension.width : 0, s.height > extension.height ? s.height - extension.height : 0); + if (image_enabled_) + { + unsigned place = image_pixels_ + 2; + r.x += place; + if (r.width > place) r.width -= place; + } + editor_->text_area(r); + } + void _m_draw_push_button(bool enabled) { ::nana::rectangle r{graph_->size()}; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 5bb523a9..c49841c5 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -433,8 +433,16 @@ namespace nana { check_range(pos, cont_.size()); + //The order of cont_'s elements is the display order. if (!disp_order) - pos = this->cast(pos, false); + { + /// It always match the item with pos, otherwise a bug occurs. + for (auto & m : cont_) + { + if (m.index == pos) + return m; + } + } return cont_[pos]; } @@ -963,7 +971,7 @@ namespace nana return prstatus; } - void scroll(const index_pair& pos, bool to_bottom); + void scroll(const index_pair& abs_pos, bool to_bottom); /// Append a new category with a specified name and return a pointer to it. category_t* create_cat(native_string_type&& text) @@ -2840,16 +2848,16 @@ namespace nana std::vector> panes_; }; - void es_lister::scroll(const index_pair& pos, bool to_bottom) + void es_lister::scroll(const index_pair& abs_pos, bool to_bottom) { - auto& cat = *get(pos.cat); + auto& cat = *get(abs_pos.cat); - if ((pos.item != nana::npos) && (pos.item >= cat.items.size())) + if ((abs_pos.item != nana::npos) && (abs_pos.item >= cat.items.size())) throw std::invalid_argument("listbox: invalid pos to scroll"); if (!cat.expand) { - this->expand(pos.cat, true); + this->expand(abs_pos.cat, true); ess_->calc_content_size(); } else if (!ess_->auto_draw) @@ -2862,7 +2870,7 @@ namespace nana auto origin = ess_->content_view->origin(); origin.y = 0; - auto off = this->distance(this->first(), pos) * ess_->item_height(); + auto off = this->distance(this->first(), this->index_cast(abs_pos, false)) * ess_->item_height(); auto screen_px = ess_->content_view->view_area().height; @@ -3661,6 +3669,7 @@ namespace nana rectangle bground_r{ content_r.x + static_cast(essence_->header.margin()), coord.y, show_w, essence_->item_height() }; auto const state_bgcolor = this->_m_draw_item_bground(bground_r, bgcolor, {}, state, item); + //The position of column in x-axis. int column_x = coord.x; for (size_type display_order{ 0 }; display_order < seqs.size(); ++display_order) // get the cell (column) index in the order headers are displayed @@ -3670,7 +3679,9 @@ namespace nana if (col.width_px > essence_->scheme_ptr->text_margin) { + //The column text position, it is a offset to column_x. int content_pos = 0; + element_state estate = element_state::normal; nana::rectangle img_r; @@ -3721,7 +3732,7 @@ namespace nana //Make sure the user-define inline widgets is in the right visible rectangle. rectangle pane_r; - const auto wdg_x = coord.x + content_pos; + const auto wdg_x = column_x + content_pos; const auto wdg_w = col.width_px - static_cast(content_pos); bool visible_state = true; @@ -5219,9 +5230,9 @@ namespace nana ess.update(); } - void listbox::scroll(bool to_bottom, const index_pair& pos) + void listbox::scroll(bool to_bottom, const index_pair& abs_pos) { - _m_ess().lister.scroll(pos, to_bottom); + _m_ess().lister.scroll(abs_pos, to_bottom); _m_ess().update(); } diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index 61e6a610..26b21812 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -441,23 +441,9 @@ namespace nana renderer->item_text(graph, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr); - if (hotkey) - { - item_ptr->hotkey = hotkey; - if (item_ptr->flags.enabled) - { - auto off_px = (hotkey_pos ? graph.text_extent_size(text.c_str(), hotkey_pos).width : 0); - auto hotkey_px = graph.text_extent_size(text.c_str() + hotkey_pos, 1).width; - - unsigned ascent, descent, inleading; - graph.text_metrics(ascent, descent, inleading); - - int x = item_r.x + 40 + off_px; - int y = item_r.y + text_top_off + ascent + 1; - - graph_->line({ x, y }, { x + static_cast(hotkey_px)-1, y }, colors::black); - } - } + item_ptr->hotkey = hotkey; + if (hotkey && item_ptr->flags.enabled) + API::dev::draw_shortkey_underline(*graph_, text, hotkey, hotkey_pos, {item_r.x + 40, item_r.y + text_top_off}, colors::black); if (item_ptr->linked.menu_ptr) renderer->sub_arrow(graph, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr); @@ -624,10 +610,14 @@ namespace nana } else if (item_ptr->flags.enabled) { + //A pointer refers to a menu object which owns the menu window. + //After fn_close_tree_(), *this is an invalid object. + auto owner = menu_->owner; + fn_close_tree_(); if (item_ptr->event_handler) { - item_proxy ip{ index, menu_->owner }; + item_proxy ip{ index, owner }; item_ptr->event_handler.operator()(ip); } return 1; @@ -1019,13 +1009,18 @@ namespace nana close(); break; default: - if (2 != send_shortkey(arg.key)) + switch (send_shortkey(arg.key)) { - if (API::empty_window(*this) == false) + case 0: + if (this->empty() == false) close(); - } - else + break; + case 1: //The menu has been closed + break; + case 2: this->submenu(true); + break; + } } } @@ -1360,6 +1355,11 @@ namespace nana impl_->mbuilder.renderer(rd); } + window menu::handle() const + { + return (impl_->window_ptr ? impl_->window_ptr->handle() : nullptr); + } + void menu::_m_popup(window wd, const point& pos, bool called_by_menubar) { if (impl_->mbuilder.data().items.size()) diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 8ad5e35b..a83c3d86 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -30,44 +30,155 @@ namespace nana { struct item_type { - item_type(const native_string_type text, unsigned long shortkey) - : text(text), shortkey(shortkey) + item_type(std::string&& text, wchar_t shortkey, std::size_t shortkey_pos): + text(std::move(text)), + shortkey(shortkey), + shortkey_pos(shortkey_pos) {} - native_string_type text; - unsigned long shortkey; + std::string text; ///< Transformed text, the shortkey charactor has been proccessed. + wchar_t shortkey; + std::size_t shortkey_pos; ::nana::menu menu_obj; ::nana::point pos; ::nana::size size; }; - class trigger::itembase + struct trigger::essence { - public: - using container = std::vector; - - ~itembase() + enum class behavior { - for(auto i : cont_) - delete i; + none, focus, menu, + }; + + + widget *widget_ptr{ nullptr }; + std::vector items; + + struct state_type + { + std::size_t active{ nana::npos }; + behavior behave{ behavior::none }; + + bool menu_active{ false }; + bool passive_close{ true }; + + bool nullify_mouse{ false }; + + nana::menu *menu{ nullptr }; + nana::point mouse_pos; + }state; + + //functions + + ~essence() + { + for (auto p : items) + delete p; } - void append(const native_string_type& text, unsigned long shortkey) + nana::menu& push_back(const std::string& text) { - if(shortkey && shortkey < 0x61) shortkey += (0x61 - 0x41); - cont_.emplace_back(new item_type(text, shortkey)); + wchar_t shortkey; + std::size_t shortkey_pos; + auto transformed_text = API::transform_shortkey_text(text, shortkey, &shortkey_pos); + + if (shortkey) + API::register_shortkey(*widget_ptr, shortkey); + + + if (shortkey && shortkey < 0x61) + shortkey += (0x61 - 0x41); + items.emplace_back(new item_type{ std::move(transformed_text), shortkey, shortkey_pos }); + + API::refresh_window(*widget_ptr); + + return this->items.back()->menu_obj; + } + + bool cancel() + { + if (nana::npos == state.active) + return false; + + this->close_menu(); + state.behave = behavior::none; + + auto pos = API::cursor_position(); + API::calc_window_point(widget_ptr->handle(), pos); + state.active = find(pos); + + return true; } - std::size_t find(unsigned long shortkey) const + + bool open_menu(bool activate_menu = false) { - if(shortkey) + if (activate_menu) + state.menu_active = true; + + auto pos = state.active; + + if (pos >= items.size()) + return false; + + auto item_ptr = items[pos]; + + if (state.menu_active && (state.menu != &(item_ptr->menu_obj))) { - if(shortkey < 0x61) shortkey += (0x61 - 0x41); + API::dev::delay_restore(true); + this->close_menu(); + API::dev::delay_restore(false); + state.active = pos; + + //Resets this flag, because close_menu() deactivates the menu + state.menu_active = true; + + state.menu = &(item_ptr->menu_obj); + state.menu->destroy_answer([this] + { + state.menu = nullptr; + if (state.passive_close) + { + cancel(); + API::refresh_window(*widget_ptr); + } + }); + + if (API::focus_window() != this->widget_ptr->handle()) + API::focus_window(widget_ptr->handle()); + menu_accessor::popup(*state.menu, *widget_ptr, item_ptr->pos.x, item_ptr->pos.y + static_cast(item_ptr->size.height)); + return true; + } + return false; + } + + bool close_menu() + { + state.menu_active = false; + + if (state.menu) + { + state.passive_close = false; + state.menu->close(); + state.passive_close = true; + state.menu = nullptr; + return true; + } + return false; + } + + + std::size_t find(wchar_t shortkey) const + { + if (shortkey) + { + if (shortkey < 0x61) shortkey += (0x61 - 0x41); std::size_t index = 0; - for(auto i : cont_) + for (auto item_ptr : items) { - if(i->shortkey == shortkey) + if (item_ptr->shortkey == shortkey) return index; ++index; } @@ -75,12 +186,25 @@ namespace nana return npos; } - const container& cont() const + std::size_t find(const ::nana::point& pos) { - return cont_; + if ((2 <= pos.x) && (2 <= pos.y) && (pos.y < 25)) + { + int item_x = 2; + std::size_t index = 0; + + for (auto item_ptr : items) + { + if ((item_x <= pos.x) && (pos.x < item_x + static_cast(item_ptr->size.width))) + return index; + + item_x += static_cast(item_ptr->size.width); + ++index; + } + } + + return npos; } - private: - container cont_; }; //class item_renderer @@ -91,19 +215,17 @@ namespace nana void item_renderer::background(const nana::point& pos, const nana::size& size, state item_state) { auto bground = scheme_ptr_->text_fgcolor; - ::nana::color border, body, corner; + ::nana::color border, body; switch (item_state) { case state::highlighted: border = scheme_ptr_->border_highlight; body = scheme_ptr_->body_highlight; - corner = body.blend(bground, 0.5); break; case state::selected: border = scheme_ptr_->border_selected; body = scheme_ptr_->body_selected; - corner = body.blend(bground, 0.5); break; default: //Don't process other states. return; @@ -112,7 +234,7 @@ namespace nana nana::rectangle r(pos, size); graph_.rectangle(r, false, border); - graph_.palette(false, corner); + graph_.palette(false, body.blend(bground, 0.5)); paint::draw{ graph_ }.corner(r, 1); graph_.rectangle(r.pare_off(1), true, body); @@ -127,77 +249,47 @@ namespace nana //class trigger trigger::trigger() - : widget_(nullptr) - , graph_(nullptr) - , items_(new itembase) {} + : ess_(new essence) + {} trigger::~trigger() { - delete items_; + delete ess_; } - nana::menu* trigger::push_back(const std::string& text) + auto trigger::ess() const -> essence& { - wchar_t shkey; - API::transform_shortkey_text(text, shkey, nullptr); - - if(shkey) - API::register_shortkey(widget_->handle(), shkey); - - auto pos = items_->cont().size(); - items_->append(to_nstring(text), shkey); - refresh(*graph_); - API::update_window(*widget_); - - return at(pos); + return *ess_; } - nana::menu* trigger::at(std::size_t pos) const + void trigger::attached(widget_reference widget, graph_reference) { - if (pos < items_->cont().size()) - return &(items_->cont()[pos]->menu_obj); - - return nullptr; - } - - std::size_t trigger::size() const - { - return items_->cont().size(); - } - - void trigger::attached(widget_reference widget, graph_reference graph) - { - graph_ = &graph; - widget_ = &widget; + ess_->widget_ptr = &widget; } void trigger::refresh(graph_reference graph) { - auto bgcolor = API::bgcolor(*widget_); - graph_->rectangle(true, bgcolor); + auto bgcolor = API::bgcolor(*ess_->widget_ptr); - item_renderer ird(*widget_, graph); + graph.rectangle(true, bgcolor); + + item_renderer ird{ *ess_->widget_ptr, graph }; nana::point item_pos(2, 2); nana::size item_s(0, 23); unsigned long index = 0; - for (auto i : items_->cont()) + + for (auto pm : ess_->items) { - //Transform the text if it contains the hotkey character - wchar_t hotkey; - ::std::wstring::size_type hotkey_pos; - auto text = API::transform_shortkey_text(to_utf8(i->text), hotkey, &hotkey_pos); - - nana::size text_s = graph.text_extent_size(text); - + auto text_s = graph.text_extent_size(pm->text); item_s.width = text_s.width + 16; - i->pos = item_pos; - i->size = item_s; + pm->pos = item_pos; + pm->size = item_s; using state = item_renderer::state; - state item_state = (index != state_.active ? state::normal : (state_.menu_active ? state::selected : state::highlighted)); + state item_state = (index != ess_->state.active ? state::normal : (ess_->state.menu_active ? state::selected : state::highlighted)); ird.background(item_pos, item_s, item_state); if (state::selected == item_state) @@ -210,73 +302,66 @@ namespace nana //Draw text, the text is transformed from orignal for hotkey character int text_top_off = (item_s.height - text_s.height) / 2; - ird.caption({ item_pos.x + 8, item_pos.y + text_top_off }, to_nstring(text)); + ird.caption({ item_pos.x + 8, item_pos.y + text_top_off }, to_nstring(pm->text)); - if (hotkey) - { - unsigned off_w = (hotkey_pos ? graph.text_extent_size(text.c_str(), hotkey_pos).width : 0); - auto hotkey_size = graph.text_extent_size(text.c_str() + hotkey_pos, 1); + API::dev::draw_shortkey_underline(graph, pm->text, pm->shortkey, pm->shortkey_pos, { item_pos.x + 8, item_pos.y + text_top_off }, ird.scheme_ptr()->text_fgcolor); - unsigned ascent, descent, inleading; - graph.text_metrics(ascent, descent, inleading); - int x = item_pos.x + 8 + off_w; - int y = item_pos.y + text_top_off + ascent + 1; - graph.line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }, ird.scheme_ptr()->text_fgcolor); - } - - item_pos.x += i->size.width; + item_pos.x += pm->size.width; ++index; } } void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { - if (arg.pos != state_.mouse_pos) - state_.nullify_mouse = false; + + if (arg.pos != ess_->state.mouse_pos) + ess_->state.nullify_mouse = false; bool popup = false; - if(state_.behavior == state_type::behavior_focus) + if(essence::behavior::focus == ess_->state.behave) { - auto index = _m_item_by_pos(arg.pos); - if(index != npos && state_.active != index) + auto index = ess_->find(arg.pos); + if(index != npos && ess_->state.active != index) { - state_.active = index; + ess_->state.active = index; + popup = true; + } + } + else if (!ess_->state.nullify_mouse) + { + auto which = ess_->find(arg.pos); + if ((which != ess_->state.active) && (which != npos || (false == ess_->state.menu_active))) + { + ess_->state.active = which; popup = true; } } - else - popup = _m_track_mouse(arg.pos); if(popup) { - _m_popup_menu(); + ess_->open_menu(); refresh(graph); API::dev::lazy_refresh(); } - state_.mouse_pos = arg.pos; + ess_->state.mouse_pos = arg.pos; } void trigger::mouse_leave(graph_reference graph, const arg_mouse& arg) { - state_.nullify_mouse = false; + ess_->state.nullify_mouse = false; mouse_move(graph, arg); } void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) { - state_.nullify_mouse = false; - state_.active = _m_item_by_pos(arg.pos); + ess_->state.nullify_mouse = false; + ess_->state.active = ess_->find(arg.pos); - if (npos != state_.active) - { - if (!state_.menu_active) - state_.menu_active = true; - - _m_popup_menu(); - } + if (npos != ess_->state.active) + ess_->open_menu(true); else - _m_total_close(); + ess_->cancel(); refresh(graph); API::dev::lazy_refresh(); @@ -284,17 +369,17 @@ namespace nana void trigger::mouse_up(graph_reference graph, const arg_mouse&) { - state_.nullify_mouse = false; + ess_->state.nullify_mouse = false; - if(state_.behavior != state_.behavior_menu) + if(ess_->state.behave != essence::behavior::menu) { - if(state_.menu_active) - state_.behavior = state_.behavior_menu; + if(ess_->state.menu_active) + ess_->state.behave = essence::behavior::menu; } else { - state_.behavior = state_.behavior_none; - _m_total_close(); + ess_->state.behave = essence::behavior::none; + ess_->cancel(); refresh(graph); API::dev::lazy_refresh(); } @@ -302,13 +387,12 @@ namespace nana void trigger::focus(graph_reference graph, const arg_focus& arg) { - if((arg.getting == false) && (state_.active != npos)) + if((arg.getting == false) && (ess_->state.active != npos)) { - state_.behavior = state_type::behavior_none; - state_.nullify_mouse = true; - state_.menu_active = false; - _m_close_menu(); - state_.active = npos; + ess_->state.behave = essence::behavior::none; + ess_->state.nullify_mouse = true; + ess_->close_menu(); + ess_->state.active = npos; refresh(graph); API::dev::lazy_refresh(); } @@ -316,53 +400,54 @@ namespace nana void trigger::key_press(graph_reference graph, const arg_keyboard& arg) { - state_.nullify_mouse = true; - if(state_.menu) + ess_->state.nullify_mouse = true; + + auto & menu_ptr = ess_->state.menu; + if(ess_->state.menu) { switch(arg.key) { case keyboard::os_arrow_down: case keyboard::backspace: case keyboard::os_arrow_up: - state_.menu->goto_next(keyboard::os_arrow_down == arg.key); + menu_ptr->goto_next(keyboard::os_arrow_down == arg.key); break; case keyboard::os_arrow_right: - if(state_.menu->goto_submen() == false) - _m_move(false); + if(menu_ptr->goto_submen() == false) + _m_move(graph, false); break; case keyboard::os_arrow_left: - if(state_.menu->exit_submenu() == false) - _m_move(true); + if(menu_ptr->exit_submenu() == false) + _m_move(graph, true); break; case keyboard::escape: - if(state_.menu->exit_submenu() == false) + if(menu_ptr->exit_submenu() == false) { - _m_close_menu(); - state_.behavior = state_.behavior_focus; - state_.menu_active = false; + ess_->close_menu(); + ess_->state.behave = essence::behavior::focus; } break; case keyboard::enter: - state_.menu->pick(); + menu_ptr->pick(); break; default: //Katsuhisa Yuasa: menubar key_press improvements //send_shortkey has 3 states, 0 = UNKNOWN KEY, 1 = ITEM, 2 = GOTO SUBMENU - int sk_state = state_.menu->send_shortkey(arg.key); + int sk_state = menu_ptr->send_shortkey(arg.key); switch(sk_state) { case 0: //UNKNOWN KEY break; case 1: //ITEM - if (state_.active != npos) + if (ess_->state.active != npos) { - _m_total_close(); + ess_->cancel(); if (arg.key == 18) //ALT - state_.behavior = state_.behavior_focus; + ess_->state.behave = essence::behavior::focus; } break; case 2: //GOTO SUBMENU - state_.menu->goto_submen(); + menu_ptr->goto_submen(); break; default: break; } @@ -376,30 +461,28 @@ namespace nana case keyboard::os_arrow_right: case keyboard::backspace: case keyboard::os_arrow_left: - _m_move(keyboard::os_arrow_right != arg.key); + _m_move(graph, keyboard::os_arrow_right != arg.key); break; case keyboard::os_arrow_up: case keyboard::os_arrow_down: case keyboard::enter: - state_.menu_active = true; - if(_m_popup_menu()) - state_.menu->goto_next(true); + if(ess_->open_menu(true)) + menu_ptr->goto_next(true); break; case keyboard::escape: - if(state_.behavior == state_.behavior_focus) + if(essence::behavior::focus == ess_->state.behave) { - state_.active= npos; - state_.behavior = state_.behavior_none; + ess_->state.active= npos; + ess_->state.behave = essence::behavior::none; } break; default: - auto index = items_->find(arg.key); + auto index = ess_->find(arg.key); if(index != npos) { - state_.active = index; - state_.menu_active = true; - if(_m_popup_menu()) - state_.menu->goto_next(true); + ess_->state.active = index; + if(ess_->open_menu(true)) + menu_ptr->goto_next(true); } break; } @@ -413,20 +496,20 @@ namespace nana { if(arg.key == 18) { - if(state_.behavior == state_type::behavior_none) + if(essence::behavior::none == ess_->state.behave) { - state_.behavior = state_type::behavior_focus; - state_.active = 0; + ess_->state.behave = essence::behavior::focus; + ess_->state.active = 0; } else { - state_.behavior = state_type::behavior_none; + ess_->state.behave = essence::behavior::none; auto pos = API::cursor_position(); - API::calc_window_point(widget_->handle(), pos); - state_.active = _m_item_by_pos(pos); + API::calc_window_point(ess_->widget_ptr->handle(), pos); + ess_->state.active = ess_->find(pos); } - state_.menu_active = false; + ess_->state.menu_active = false; refresh(graph); API::dev::lazy_refresh(); } @@ -434,31 +517,31 @@ namespace nana void trigger::shortkey(graph_reference graph, const arg_keyboard& arg) { - API::focus_window(widget_->handle()); + API::focus_window(ess_->widget_ptr->handle()); - auto index = items_->find(arg.key); - if(index != npos && (index != state_.active || nullptr == state_.menu)) + auto index = ess_->find(arg.key); + if(index != npos && (index != ess_->state.active || nullptr == ess_->state.menu)) { - _m_close_menu(); - state_.menu_active = true; - state_.nullify_mouse = true; - state_.active = index; + ess_->close_menu(); + ess_->state.nullify_mouse = true; + ess_->state.active = index; - if(_m_popup_menu()) - state_.menu->goto_next(true); + if(ess_->open_menu(true)) + ess_->state.menu->goto_next(true); refresh(graph); API::dev::lazy_refresh(); - state_.behavior = state_.behavior_menu; + ess_->state.behave = essence::behavior::menu; } } - void trigger::_m_move(bool to_left) + void trigger::_m_move(graph_reference graph, bool to_left) { - if(items_->cont().empty()) return; + if (ess_->items.empty()) return; - const std::size_t last_pos = items_->cont().size() - 1; - auto index = state_.active; + const std::size_t last_pos = ess_->items.size() - 1; + + auto index = ess_->state.active; if(to_left) { --index; @@ -472,121 +555,16 @@ namespace nana index = 0; } - if(index != state_.active) + if(index != ess_->state.active) { - state_.active = index; - refresh(*graph_); + ess_->state.active = index; + refresh(graph); API::dev::lazy_refresh(); - if(_m_popup_menu()) - state_.menu->goto_next(true); + if(ess_->open_menu()) + ess_->state.menu->goto_next(true); } } - - bool trigger::_m_popup_menu() - { - auto& items = items_->cont(); - - auto pos = state_.active; - if (pos >= items.size()) - return false; - - if(state_.menu_active && (state_.menu != &(items[pos]->menu_obj))) - { - API::dev::delay_restore(true); - _m_close_menu(); - API::dev::delay_restore(false); - state_.active = pos; - - auto & m = items[pos]; - state_.menu = &(m->menu_obj); - state_.menu->destroy_answer([this] - { - state_.menu = nullptr; - if (state_.passive_close) - { - _m_total_close(); - - refresh(*graph_); - API::update_window(widget_->handle()); - } - }); - - if (API::focus_window() != this->widget_->handle()) - API::focus_window(widget_->handle()); - menu_accessor::popup(*state_.menu, *widget_, m->pos.x, m->pos.y + static_cast(m->size.height)); - return true; - } - return false; - } - - void trigger::_m_total_close() - { - _m_close_menu(); - state_.menu_active = false; - state_.behavior = state_.behavior_none; - - auto pos = API::cursor_position(); - API::calc_window_point(widget_->handle(), pos); - state_.active = _m_item_by_pos(pos); - } - - bool trigger::_m_close_menu() - { - if(state_.menu) - { - state_.passive_close = false; - state_.menu->close(); - state_.passive_close = true; - state_.menu = nullptr; - return true; - } - return false; - } - - std::size_t trigger::_m_item_by_pos(const ::nana::point& pos) - { - if((2 <= pos.x) && (2 <= pos.y) && (pos.y < 25)) - { - int item_x = 2; - std::size_t index = 0; - for(auto i : items_->cont()) - { - if(item_x <= pos.x && pos.x < item_x + static_cast(i->size.width)) - return index; - - item_x += i->size.width; - ++index; - } - } - - return npos; - } - - bool trigger::_m_track_mouse(const ::nana::point& pos) - { - if(state_.nullify_mouse == false) - { - auto which = _m_item_by_pos(pos); - if((which != state_.active) && (which != npos || (false == state_.menu_active))) - { - state_.active = which; - return true; - } - } - return false; - } - - //struct state_type - trigger::state_type::state_type() - : active(npos), - behavior(behavior_none), - menu_active(false), - passive_close(true), - nullify_mouse(false), - menu(nullptr) - {} - //end struct state_type //end class trigger }//end namespace menubar }//end namespace drawerbase @@ -609,7 +587,7 @@ namespace nana ::create(wd, rectangle(nana::size(API::window_size(wd).width, 28))); API::dev::set_menubar(handle(), true); - evt_resized_ = API::events(wd).resized.connect([this](const ::nana::arg_resized& arg) + evt_resized_ = API::events(wd).resized.connect_unignorable([this](const ::nana::arg_resized& arg) { auto sz = this->size(); sz.width = arg.width; @@ -619,20 +597,49 @@ namespace nana menu& menubar::push_back(const std::string& text) { - return *(get_drawer_trigger().push_back(text)); + return get_drawer_trigger().ess().push_back(text); } - menu& menubar::at(std::size_t index) const + menu& menubar::at(std::size_t pos) const { - auto p = get_drawer_trigger().at(index); - if(nullptr == p) - throw std::out_of_range("menubar::at, out of range"); - return *p; + return get_drawer_trigger().ess().items.at(pos)->menu_obj; } std::size_t menubar::length() const { - return get_drawer_trigger().size(); + return get_drawer_trigger().ess().items.size(); + } + + bool menubar::cancel() + { + return get_drawer_trigger().ess().cancel(); + } + + bool menubar::hovered() const + { + auto const native_handle = API::root(this->handle()); + if (native_handle) + { + auto wd = API::find_window(API::cursor_position()); + if (wd == this->handle()) + return true; + + auto & items = get_drawer_trigger().ess().items; + while (wd) + { + auto owner = API::get_owner_window(wd); + if (API::root(owner) == native_handle) + { + for (auto p : items) + { + if (p->menu_obj.handle() == wd) + return true; + } + } + wd = owner; + } + } + return false; } //end class menubar }//end namespace nana diff --git a/source/gui/widgets/progress.cpp b/source/gui/widgets/progress.cpp index 9f0bcf57..20b46ddc 100644 --- a/source/gui/widgets/progress.cpp +++ b/source/gui/widgets/progress.cpp @@ -1,6 +1,6 @@ /* * A Progress Indicator Implementation - * Copyright(C) 2003-2013 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,136 +17,185 @@ namespace nana { namespace progress { - //class trigger - void trigger::attached(widget_reference wd, graph_reference graph) + scheme::scheme() { - widget_ = &wd; - graph_ = &graph; + foreground = static_cast(0x107515); } - unsigned trigger::value() const + class substance { - return value_; - } + public: + static const unsigned border_px = 1; - unsigned trigger::value(unsigned v) - { - internal_scope_guard isg; - if(false == unknown_) + void set_widget(widget& wdg) { - if(value_ != v) - value_ = v > max_?max_:v; + widget_ = static_cast(&wdg); } - else - value_ += (v?10:v); - if(_m_check_changing(value_)) + nana::progress* widget_ptr() const { - refresh(*graph_); - API::update_window(widget_->handle()); + return widget_; } - return v; - } - unsigned trigger::inc() - { - internal_scope_guard isg; - if(false == unknown_) + unsigned inc() { - if(value_ < max_) - ++value_; + auto val = value(nullptr) + 1; + return value(&val); } - else - value_ += 5; - if(_m_check_changing(value_)) - API::refresh_window(widget_->handle()); - return value_; + unsigned value(const unsigned* value_ptr) + { + //Sets new value if value_ptr is not a nullptr + if (value_ptr) + { + if (unknown_) + value_ += (*value_ptr ? 5 : 0); + else + value_ = (std::min)(max_, *value_ptr); + + _m_try_refresh(); + } + return value_; + } + + void reset_value() + { + value_ = 0; + } + + unsigned maximum(const unsigned * value_ptr) + { + //Sets new maximum if value_ptr is not a nullptr + if (value_ptr) + { + max_ = (*value_ptr > 0 ? *value_ptr : 1); + _m_try_refresh(); + } + return max_; + } + + bool unknown(const bool* state_ptr) + { + if (state_ptr) + { + unknown_ = *state_ptr; + if (unknown_) + value_px_ = 0; + else + value_ = (std::min)(value_, max_); + } + return unknown_; + } + + unsigned value_px() const + { + return value_px_; + } + private: + void _m_try_refresh() + { + if (nullptr == widget_) + return; + + auto value_px = (widget_->size().width - border_px * 2) * value_ / max_; + + if (value_px != value_px_) + { + value_px_ = value_px; + API::refresh_window(*widget_); + } + } + private: + nana::progress * widget_{ nullptr }; + unsigned max_{ 100 }; + unsigned value_{ 0 }; + unsigned value_px_{ 0 }; + bool unknown_{ false }; + }; + + trigger::trigger() + : progress_(new substance) + {} + + trigger::~trigger() + { + delete progress_; } - unsigned trigger::Max() const + substance* trigger::progress() const { - return max_; + return progress_; } - - unsigned trigger::Max(unsigned value) + + void trigger::attached(widget_reference wdg, graph_reference) { - max_ = value; - if(max_ == 0) ++max_; - - API::refresh_window(widget_->handle()); - return max_; - } - - void trigger::unknown(bool enb) - { - unknown_ = enb; - if(enb) - draw_width_ = static_cast(-1); - } - - bool trigger::unknown() const - { - return unknown_; - } - bool trigger::stopped() const - { - return stop_; - } - bool trigger::stop(bool s) - { - std::swap(s,stop_); - return s; + progress_->set_widget(wdg); } void trigger::refresh(graph_reference graph) - { - if (false == unknown_) - draw_width_ = static_cast((graph.width() - border * 2) * (double(value_) / max_)); + { + const unsigned border_px = substance::border_px; - _m_draw_box(graph); - _m_draw_progress(graph); - } + rectangle rt_val{ graph.size() }; + auto const width = rt_val.width - border_px * 2; - void trigger::_m_draw_box(graph_reference graph) - { - rectangle r{ graph.size() }; - graph.gradual_rectangle(r, colors::button_face_shadow_end, colors::button_face_shadow_start, true); - ::nana::color lt{ colors::gray }, rb{colors::white}; - graph.frame_rectangle(r, lt, lt, rb, rb); - } + rt_val.pare_off(static_cast(border_px)); - void trigger::_m_draw_progress(graph_reference graph) - { - unsigned width = graph.width() - border * 2; - unsigned height = graph.height() - border * 2; - - if(false == unknown_) + auto rt_bground = rt_val; + if (false == progress_->unknown(nullptr)) { - if(draw_width_) - graph.gradual_rectangle({ static_cast(border), static_cast(border), draw_width_, height }, { 0x6F, 0xFF, 0xA8 }, { 0x10, 0x75, 0x15 }, true); + rt_bground.x = static_cast(progress_->value_px()) + static_cast(border_px); + rt_bground.width -= progress_->value_px(); + + rt_val.width = progress_->value_px(); } else { - unsigned block = width / 3; + auto const block = width / 3; - int left = (value_ < block ? 0 : value_ - block) + border; - int right = (value_ >= width - 1 + border? width - 1 + border: value_); + auto const value = progress_->value(nullptr); - if(right >= left) - graph.gradual_rectangle({ left, static_cast(border), static_cast(right - left + 1), height }, { 0x6F, 0xFF, 0xA8 }, { 0x10, 0x75, 0x15 }, true); + auto left = (std::max)(0, static_cast(value - block)) + static_cast(border_px); + auto right = static_cast((std::min)(value, width + border_px -1)); - if(value_ >= width + block) value_ = 0; + if (right > left) + { + rt_val.x = left; + rt_val.width = static_cast(right - left + 1); + } + else + rt_val.width = 0; + + if (value >= width + block) + progress_->reset_value(); } - } - bool trigger::_m_check_changing(unsigned newvalue) const - { - if(graph_) - return (((graph_->width() - border * 2) * newvalue / max_) != draw_width_); - return false; + auto & sch = progress_->widget_ptr()->scheme(); + + //Draw the gradient background if gradient_bgcolor is available. + + auto bgcolor = sch.background.get_color(); + if (bgcolor.invisible()) + bgcolor = colors::button_face; + + if (sch.gradient_bgcolor.get_color().invisible()) + graph.rectangle(rt_bground, true, bgcolor); + else + graph.gradual_rectangle(rt_bground, bgcolor, sch.gradient_bgcolor.get_color(), true); + + //Draw the gradient fgcolor if gradient_fgcolor is available. + + auto fgcolor = sch.foreground.get_color(); + if (fgcolor.invisible()) + fgcolor = static_cast(0x107515); + + if (sch.gradient_fgcolor.get_color().invisible()) + graph.rectangle(rt_val, true, fgcolor); + else + graph.gradual_rectangle(rt_val, sch.gradient_fgcolor.get_color(), fgcolor, true); + + graph.frame_rectangle(rectangle{ graph.size() }, colors::gray, colors::gray, colors::white, colors::white); } - //end class drawer }//end namespace progress }//end namespace drawerbase @@ -165,49 +214,42 @@ namespace nana unsigned progress::value() const { - return get_drawer_trigger().value(); + return get_drawer_trigger().progress()->value(nullptr); } unsigned progress::value(unsigned val) { - internal_scope_guard isg; + internal_scope_guard lock; if(API::empty_window(this->handle()) == false) - return get_drawer_trigger().value(val); + return get_drawer_trigger().progress()->value(&val); return 0; } unsigned progress::inc() { - internal_scope_guard isg; - return get_drawer_trigger().inc(); + internal_scope_guard lock; + return get_drawer_trigger().progress()->inc(); } unsigned progress::amount() const { - return get_drawer_trigger().Max(); + return get_drawer_trigger().progress()->maximum(nullptr); } unsigned progress::amount(unsigned value) { - return get_drawer_trigger().Max(value); + return get_drawer_trigger().progress()->maximum(&value); } void progress::unknown(bool enb) { - get_drawer_trigger().unknown(enb); + internal_scope_guard lock; + get_drawer_trigger().progress()->unknown(&enb); } bool progress::unknown() const { - return get_drawer_trigger().unknown(); - } - bool progress::stop(bool s) - { - return get_drawer_trigger().stop(s); - } - bool progress::stopped() const - { - return get_drawer_trigger().stopped(); + return get_drawer_trigger().progress()->unknown(nullptr); } //end class progress }//end namespace nana diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index bbfed031..f39638b2 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -887,6 +887,7 @@ namespace nana{ namespace widgets return (pos < linemtr_.size() ? linemtr_[pos].take_lines : 0); } private: + /// Split a text into multiple sections, a section indicates an english word or a CKJ character void _m_text_section(const std::wstring& str, std::vector& tsec) { if (str.empty()) @@ -947,11 +948,13 @@ namespace nana{ namespace widgets class text_editor::keyword_parser { public: - void parse(const std::wstring& text, const implementation::inner_keywords& keywords) + void parse(const wchar_t* c_str, std::size_t len, implementation::inner_keywords& keywords) //need string_view { - if ( keywords.base.empty() || text.empty() ) + if ( keywords.base.empty() || (0 == len) || (*c_str == 0) ) return; + std::wstring text{ c_str, len }; + using index = std::wstring::size_type; std::vector entities; @@ -986,7 +989,7 @@ namespace nana{ namespace widgets { entities.emplace_back(); auto & last = entities.back(); - last.begin = text.c_str() + pos; + last.begin = c_str + pos; last.end = last.begin + ds.text.size(); last.scheme = ki->second.get(); } @@ -1025,14 +1028,14 @@ namespace nana{ namespace widgets if (pos) { auto chr = text[pos - 1]; - if ((std::iswalpha(chr) && !std::isspace(chr)) || chr == '_') + if ((std::iswalpha(chr) && !std::iswspace(chr)) || chr == '_') return false; } if (pos + len < text.size()) { auto chr = text[pos + len]; - if ((std::iswalpha(chr) && !std::isspace(chr)) || chr == '_') + if ((std::iswalpha(chr) && !std::iswspace(chr)) || chr == '_') return false; } @@ -1596,18 +1599,6 @@ namespace nana{ namespace widgets if(!attributes_.enable_caret) return false; - auto is_whitespace = [](wchar_t c) { - switch (c) { - case L' ': - case L'\t': - case L'\n': - case L'\r': - return true; - default: - return false; - } - }; - // Set caret pos by screen point and get the caret pos. mouse_caret(arg.pos, true); @@ -1616,11 +1607,11 @@ namespace nana{ namespace widgets const auto& line = impl_->textbase.getline(select_.b.y); // Expand the selection forward to the word's end. - while (select_.b.x < line.size() && !is_whitespace(line[select_.b.x])) + while (select_.b.x < line.size() && !std::iswspace(line[select_.b.x])) ++select_.b.x; // Expand the selection backward to the word's start. - while (select_.a.x > 0 && !is_whitespace(line[select_.a.x - 1])) + while (select_.a.x > 0 && !std::iswspace(line[select_.a.x - 1])) --select_.a.x; select_.mode_selection = selection::mode::method_selected; @@ -2876,7 +2867,6 @@ namespace nana{ namespace widgets return ('0' <= ch && ch <= '9'); } - ::nana::color text_editor::_m_bgcolor() const { return (!API::window_enabled(window_) ? static_cast(0xE0E0E0) : API::bgcolor(window_)); @@ -3423,7 +3413,7 @@ namespace nana{ namespace widgets //Parse highlight keywords keyword_parser parser; - parser.parse({ text_ptr, text_len }, impl_->keywords); + parser.parse(text_ptr, text_len, impl_->keywords); const auto line_h_pixels = line_height(); diff --git a/source/gui/widgets/slider.cpp b/source/gui/widgets/slider.cpp index b6fdfc09..20109237 100644 --- a/source/gui/widgets/slider.cpp +++ b/source/gui/widgets/slider.cpp @@ -524,14 +524,16 @@ namespace nana if((::nana::mouse_action::pressed == slider_state_.mouse_state) && (API::capture_window() == this->other_.wd)) return false; + auto state_changed = ((slider_state_.mouse_state != ::nana::mouse_action::normal) + || (attr_.adorn_pos != attr_.slider.pos)); + slider_state_.mouse_state = ::nana::mouse_action::normal; attr_.is_draw_adorn = false; - if(attr_.adorn_pos != attr_.slider.pos) - { - attr_.adorn_pos = attr_.slider.pos; - return true; - } - return false; + + attr_.adorn_pos = attr_.slider.pos; + slider_state_.mouse_state = ::nana::mouse_action::normal; + + return state_changed; } private: diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 7fdadcea..b7dd3426 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -332,7 +332,6 @@ namespace paint dw->context = cdc; dw->pixmap = bmp; ::SetBkMode(cdc, TRANSPARENT); - dw->brush.set(cdc, dw->brush.Solid, 0xFFFFFF); } else { @@ -1075,13 +1074,16 @@ namespace paint { if (!impl_->handle) return; #if defined(NANA_WINDOWS) - impl_->handle->update_pen(); if (pos1 != pos2) { + auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, NANA_RGB(impl_->handle->get_color()))); + ::MoveToEx(impl_->handle->context, pos1.x, pos1.y, 0); ::LineTo(impl_->handle->context, pos2.x, pos2.y); + + ::DeleteObject(::SelectObject(impl_->handle->context, prv_pen)); } - ::SetPixel(impl_->handle->context, pos2.x, pos2.y, NANA_RGB(impl_->handle->pen.color)); + ::SetPixel(impl_->handle->context, pos2.x, pos2.y, NANA_RGB(impl_->handle->get_color())); #elif defined(NANA_X11) Display* disp = nana::detail::platform_spec::instance().open_display(); impl_->handle->update_color(); @@ -1107,8 +1109,11 @@ namespace paint { if (!impl_->handle) return; #if defined(NANA_WINDOWS) - impl_->handle->update_pen(); + auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, NANA_RGB(impl_->handle->get_color()))); + ::LineTo(impl_->handle->context, pos.x, pos.y); + + ::DeleteObject(::SelectObject(impl_->handle->context, prv_pen)); #elif defined(NANA_X11) Display* disp = nana::detail::platform_spec::instance().open_display(); impl_->handle->update_color(); @@ -1136,9 +1141,14 @@ namespace paint if (r.width && r.height && impl_->handle && r.right() > 0 && r.bottom() > 0) { #if defined(NANA_WINDOWS) + + auto brush = ::CreateSolidBrush(NANA_RGB(impl_->handle->get_color())); + ::RECT native_r = { r.x, r.y, r.right(), r.bottom()}; - impl_->handle->update_brush(); - (solid ? ::FillRect : ::FrameRect)(impl_->handle->context, &native_r, impl_->handle->brush.handle); + + (solid ? ::FillRect : ::FrameRect)(impl_->handle->context, &native_r, brush); + + ::DeleteObject(brush); #elif defined(NANA_X11) Display* disp = nana::detail::platform_spec::instance().open_display(); impl_->handle->update_color(); @@ -1263,17 +1273,27 @@ namespace paint { #if defined(NANA_WINDOWS) impl_->handle->set_color(clr); + if (solid) { - impl_->handle->update_pen(); - impl_->handle->brush.set(impl_->handle->context, impl_->handle->brush.Solid, solid_clr.px_color().value); + auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, NANA_RGB(impl_->handle->get_color()))); + auto prv_brush = ::SelectObject(impl_->handle->context, ::CreateSolidBrush(NANA_RGB(solid_clr.px_color().value))); + ::RoundRect(impl_->handle->context, r.x, r.y, r.right(), r.bottom(), static_cast(radius_x * 2), static_cast(radius_y * 2)); + + ::DeleteObject(::SelectObject(impl_->handle->context, prv_brush)); + ::DeleteObject(::SelectObject(impl_->handle->context, prv_pen)); } else { - impl_->handle->update_brush(); - impl_->handle->round_region.set(r, radius_x, radius_y); - ::FrameRgn(impl_->handle->context, impl_->handle->round_region.handle, impl_->handle->brush.handle, 1, 1); + auto brush = ::CreateSolidBrush(NANA_RGB(impl_->handle->get_color())); + + auto region = ::CreateRoundRectRgn(r.x, r.y, r.x + static_cast(r.width) + 1, r.y + static_cast(r.height) + 1, static_cast(radius_x + 1), static_cast(radius_y + 1)); + + ::FrameRgn(impl_->handle->context, region, brush, 1, 1); + + ::DeleteObject(region); + ::DeleteObject(brush); } if (impl_->changed == false) impl_->changed = true;