From 460490040e903d96979133626989ef9652f2fb7a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 20 Sep 2015 23:21:44 +0800 Subject: [PATCH] improve dockable place add a new API::at_safe_place() --- include/nana/gui/detail/window_manager.hpp | 3 + include/nana/gui/programming_interface.hpp | 2 + include/nana/gui/widgets/tabbar.hpp | 3 + source/gui/detail/linux_X11/bedrock.cpp | 5 +- source/gui/detail/win32/bedrock.cpp | 2 + source/gui/detail/window_manager.cpp | 32 +++++++++++ source/gui/place.cpp | 16 +++++- source/gui/place_parts.hpp | 67 +++++++++++++++++++++- source/gui/programming_interface.cpp | 5 ++ source/gui/widgets/tabbar.cpp | 59 ++++++++++++++++--- 10 files changed, 181 insertions(+), 13 deletions(-) diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index ec91fe1f..857d69e7 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -161,6 +161,9 @@ namespace detail std::vector> shortkeys(core_window_t*, bool with_children); core_window_t* find_shortkey(native_window_type, unsigned long key); + + void set_safe_place(core_window_t* wd, std::function&& fn); + void call_safe_place(unsigned thread_id); private: void _m_disengage(core_window_t*, core_window_t* for_new); void _m_destroy(core_window_t*); diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index bfbcd138..932e35c8 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -298,6 +298,8 @@ namespace API bool ignore_mouse_focus(window, bool ignore); ///< Enables/disables the mouse focus, it returns the previous state bool ignore_mouse_focus(window); ///< Determines whether the mouse focus is enabled + + void at_safe_place(window, std::function); }//end namespace API }//end namespace nana diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index a788dc29..86556cec 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -379,6 +379,9 @@ namespace nana void push_back(std::string text, ::nana::any par = {}); void push_front(std::string text, ::nana::any par = {}); + + std::size_t selected() const; + void erase(std::size_t pos, bool close_attached = true); }; } diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index 3cba6d2b..1260067b 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -1207,8 +1207,11 @@ namespace detail if(context) context->event_window = pre_event_window; } + auto thread_id = ::nana::system::this_thread_id() + brock.wd_manager.call_safe_place(thread_id); + if(msgwnd) - brock.wd_manager.remove_trash_handle(::nana::system::this_thread_id()); + brock.wd_manager.remove_trash_handle(thread_id); } } diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp index d8ac543c..e1095345 100644 --- a/source/gui/detail/win32/bedrock.cpp +++ b/source/gui/detail/win32/bedrock.cpp @@ -413,6 +413,7 @@ namespace detail ::DispatchMessage(&msg); } + wd_manager.call_safe_place(tid); wd_manager.remove_trash_handle(tid); if (msg.message == WM_DESTROY && msg.hwnd == native_handle) break; @@ -432,6 +433,7 @@ namespace detail ::DispatchMessage(&msg); } + wd_manager.call_safe_place(tid); wd_manager.remove_trash_handle(tid); }//end while diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index e9581fd1..e272801b 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -44,6 +44,8 @@ namespace detail handle_manager wd_register; paint::image default_icon_big; paint::image default_icon_small; + + std::map>> safe_place; }; //end struct wdm_private_impl @@ -1192,6 +1194,36 @@ namespace detail return nullptr; } + void window_manager::set_safe_place(core_window_t* wd, std::function&& fn) + { + if (fn) + { + std::lock_guard lock(mutex_); + if (!available(wd)) + return; + + impl_->safe_place[wd].emplace_back(std::move(fn)); + } + } + + void window_manager::call_safe_place(unsigned thread_id) + { + std::lock_guard lock(mutex_); + + for (auto i = impl_->safe_place.begin(); i != impl_->safe_place.end();) + { + if (i->first->thread_id == thread_id) + { + for (auto & fn : i->second) + fn(); + + i = impl_->safe_place.erase(i); + } + else + ++i; + } + } + bool check_tree(basic_window* wd, basic_window* const cond) { if (wd == cond) return true; diff --git a/source/gui/place.cpp b/source/gui/place.cpp index ce0351c3..9d9e9698 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1717,13 +1717,27 @@ namespace nana } - void notify_move_stopped() + void notify_move_stopped() override { if (_m_dockable() && dockable_field && dockable_field->dockarea) dockable_field->dockarea->dock(); indicator_.docker.reset(); } + + void request_close() override + { + auto window_handle = dockable_field->dockarea->handle(); + + //a workaround for capture + auto ptr = dockable_field->dockarea.release(); + API::at_safe_place(window_handle, [this, ptr] + { + decltype(dockable_field->dockarea) del(ptr); + }); + + API::close_window(window_handle); + } private: bool _m_indicator() const { diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 621b93aa..62bd8753 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -57,12 +57,19 @@ namespace nana virtual void notify_dock() = 0; virtual void notify_move() = 0; virtual void notify_move_stopped() = 0; + + //a dockarea requests to close the dockpane + virtual void request_close() = 0; }; class dockcaption_dtrigger : public drawer_trigger { public: + void on_close(std::function&& fn) + { + close_fn_ = std::move(fn); + } private: virtual void attached(widget_reference wdg, graph_reference graph) override { @@ -84,10 +91,18 @@ namespace nana //draw x button auto r = _m_button_area(); if (x_pointed_) - graph.rectangle(r, true, colors::red); + { + color xclr = colors::red; + + if(x_state_ == ::nana::mouse_action::pressed) + xclr = xclr.blend(colors::white, 0.8); + + graph.rectangle(r, true, xclr); + } r.x += (r.width - 16) / 2; r.y = (r.height - 16) / 2; + x_icon_.draw(graph, colors::red, colors::white, r, element_state::normal); } @@ -105,6 +120,29 @@ namespace nana refresh(graph); API::lazy_refresh(); } + + void mouse_down(graph_reference graph, const arg_mouse&) override + { + if (!x_pointed_) + return; + + x_state_ = ::nana::mouse_action::pressed; + + refresh(graph); + API::lazy_refresh(); + } + + void mouse_up(graph_reference graph, const arg_mouse&) override + { + if (!x_pointed_) + return; + + x_state_ = ::nana::mouse_action::over; + refresh(graph); + API::lazy_refresh(); + + close_fn_(); + } private: ::nana::rectangle _m_button_area() const { @@ -118,12 +156,21 @@ namespace nana window window_handle_; std::unique_ptr text_rd_; bool x_pointed_{ false }; + ::nana::mouse_action x_state_{ ::nana::mouse_action::normal }; facade x_icon_; + + std::function close_fn_; }; class dockarea_caption : public widget_object < category::widget_tag, dockcaption_dtrigger > - {}; + { + public: + void on_close(std::function fn) + { + get_drawer_trigger().on_close(std::move(fn)); + } + }; class dock_page : public form @@ -158,6 +205,22 @@ namespace nana base_type::create(parent, true); this->caption("dockarea"); caption_.create(*this, true); + caption_.on_close([this] + { + bool destroy_dockarea = false; + try + { + tabbar_->erase(tabbar_->selected()); + destroy_dockarea = (0 == tabbar_->length()); + } + catch (std::out_of_range&) + { + destroy_dockarea = true; + } + + if (destroy_dockarea) + notifier_->request_close(); + }); this->events().resized([this](const arg_resized& arg) { diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 94ca4fe4..5d4901b7 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -1314,5 +1314,10 @@ namespace API internal_scope_guard lock; return (restrict::window_manager.available(iwd) ? iwd->flags.ignore_mouse_focus : false); } + + void at_safe_place(window wd, std::function fn) + { + restrict::window_manager.set_safe_place(reinterpret_cast(wd), std::move(fn)); + } }//end namespace API }//end namespace nana diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 3796a1ac..cb6eb878 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -1555,15 +1555,7 @@ namespace nana { auto& items = get_drawer_trigger().get_model()->items(); internal_scope_guard lock; - - std::size_t off = 0; - auto i = items.cbegin(), end = items.cend(); - while (i != end) - { - ++i; - ++off; - } - return off; + return static_cast(std::distance(items.cbegin(), items.cend())); } //modifiers @@ -1613,5 +1605,54 @@ namespace nana items.emplace_front(std::move(text), std::move(any)); API::refresh_window(handle()); } + + std::size_t tabbar_lite::selected() const + { + auto model = get_drawer_trigger().get_model(); + internal_scope_guard lock; + + return model->get_indexes().active_pos; + } + + void tabbar_lite::erase(std::size_t pos, bool close_attached) + { + auto model = get_drawer_trigger().get_model(); + internal_scope_guard lock; + + const auto len = length(); + + if (len <= pos) + throw std::out_of_range("tabbar_lite: out of range"); + + auto active_pos = model->get_indexes().active_pos; + + if (pos == active_pos) + { + if (active_pos + 1 == len) + { + if (active_pos) + --active_pos; + else + active_pos = npos; + } + } + else if (pos < active_pos) + --active_pos; + + model->get_indexes().active_pos = active_pos; + + auto i = model->items().cbefore_begin(); + std::advance(i, pos); + + auto attached_wd = std::next(i)->attached_window; + + model->items().erase_after(i); + + model->show_attached_window(); + API::refresh_window(handle()); + + if (close_attached && attached_wd) + API::close_window(attached_wd); + } //end class tabbar }//end namespace nana