From b13f0a4ce507069f4e2f486e7234d3764e9585f7 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 27 Aug 2016 05:23:25 +0800 Subject: [PATCH 01/14] fix textbox caret_pos issue --- include/nana/gui/widgets/skeletons/text_editor.hpp | 6 +++++- source/gui/widgets/skeletons/text_editor.cpp | 5 ++++- source/gui/widgets/textbox.cpp | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index d2a10a35..4ebee6ec 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -215,7 +215,11 @@ namespace nana{ namespace widgets std::wstring text() const; /// Sets caret position through text coordinate. - void move_caret(const upoint&); + /** + * @param pos the text position + * @param reset indicates whether to reset the text position by the pos. If this parameter is true, the text position is set by pos. If the parameter is false, it only moves the UI caret to the specified position. + */ + void move_caret(const upoint& pos, bool reset = false); void move_caret_end(); void reset_caret_pixels() const; void reset_caret(); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index e30248a8..b7a4cda0 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1840,8 +1840,11 @@ namespace nana{ namespace widgets //move_caret //Set caret position through text coordinate - void text_editor::move_caret(const upoint& crtpos) + void text_editor::move_caret(const upoint& crtpos, bool reset_caret) { + if (reset_caret) + points_.caret = crtpos; + if (!API::is_focus_ready(window_)) return; diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 12f42872..38d0d1c7 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -345,7 +345,7 @@ namespace drawerbase { auto editor = get_drawer_trigger().editor(); internal_scope_guard lock; if (editor) - editor->move_caret(pos); + editor->move_caret(pos, true); return *this; } From cf995519247e81a92f7c12c851dcdbe1aed8e118 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 27 Aug 2016 09:40:49 +0800 Subject: [PATCH 02/14] fix bug when an invalid pos for textbox.move_caret --- .../gui/widgets/skeletons/text_editor.hpp | 2 +- source/gui/widgets/skeletons/text_editor.cpp | 29 ++++++++++++++----- source/gui/widgets/textbox.cpp | 4 +-- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 4ebee6ec..22e099e4 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -219,7 +219,7 @@ namespace nana{ namespace widgets * @param pos the text position * @param reset indicates whether to reset the text position by the pos. If this parameter is true, the text position is set by pos. If the parameter is false, it only moves the UI caret to the specified position. */ - void move_caret(const upoint& pos, bool reset = false); + bool move_caret(const upoint& pos, bool reset = false); void move_caret_end(); void reset_caret_pixels() const; void reset_caret(); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index b7a4cda0..a030471a 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -341,6 +341,9 @@ namespace nana{ namespace widgets text_ptr = &mask_str; } + if (pos.x > text_ptr->size()) + pos.x = text_ptr->size(); + pos.x = editor_._m_pixels_by_char(*text_ptr, pos.x) + editor_.text_area_.area.x; int pos_y = static_cast((pos.y - editor_.points_.offset.y) * editor_.line_height() + editor_._m_text_top_base()); @@ -1840,18 +1843,18 @@ namespace nana{ namespace widgets //move_caret //Set caret position through text coordinate - void text_editor::move_caret(const upoint& crtpos, bool reset_caret) - { - if (reset_caret) - points_.caret = crtpos; - - if (!API::is_focus_ready(window_)) - return; - + bool text_editor::move_caret(const upoint& crtpos, bool reset_caret) + { const unsigned line_pixels = line_height(); auto pos = this->behavior_->caret_to_screen(crtpos); const int line_bottom = pos.y + static_cast(line_pixels); + if (reset_caret) + points_.caret = this->behavior_->screen_to_caret(pos); + + if (!API::is_focus_ready(window_)) + return false; + bool visible = false; if (hit_text_area(pos) && (line_bottom > text_area_.area.y)) { @@ -1868,6 +1871,16 @@ namespace nana{ namespace widgets caret_->visible(visible); if(visible) caret_->position(pos); + + //Adjust the caret into screen when the caret position is modified by this function + if (reset_caret && (!hit_text_area(pos))) + { + behavior_->adjust_caret_into_screen(); + render(true); + caret_->visible(true); + return true; + } + return false; } void text_editor::move_caret_end() diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 38d0d1c7..76aed3ac 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -344,8 +344,8 @@ namespace drawerbase { { auto editor = get_drawer_trigger().editor(); internal_scope_guard lock; - if (editor) - editor->move_caret(pos, true); + if (editor && editor->move_caret(pos, true)) + API::refresh_window(handle()); return *this; } From 001eac9dbf8f19ec42376776275e6fd60a4d2b07 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 2 Sep 2016 01:06:33 +0800 Subject: [PATCH 03/14] fix a filebox error under Linux fixed by buckyeh --- source/gui/filebox.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index c18c7a83..ae57b0f7 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -498,24 +498,26 @@ namespace nana if(name.empty() || (name.front() == '.')) continue; + auto fpath = i->path().native(); + auto fattr = fs::status(fpath); + item_fs m; m.name = name; + m.directory = fs::is_directory(fattr); - auto fattr = fs::status(path + m.name); - - if(fattr.type() != fs::file_type::not_found && fattr.type() != fs::file_type::unknown) - { - m.bytes = fs::file_size(path + m.name); - m.directory = fs::is_directory(fattr); - fs_ext::modified_file_time(path + m.name, m.modified_time); - } - else + switch(fattr.type()) { + case fs::file_type::not_found: + case fs::file_type::unknown: + case fs::file_type::directory: m.bytes = 0; - m.directory = fs::is_directory(*i); - fs_ext::modified_file_time(path + i->path().filename().native(), m.modified_time); + break; + default: + m.bytes = fs::file_size(fpath); } + fs_ext::modified_file_time(fpath, m.modified_time); + file_container_.push_back(m); if(m.directory) From f400c14f9b48de7963648df22116973ec83dc82f Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 2 Sep 2016 07:45:47 +0800 Subject: [PATCH 04/14] fix mouse_wheel issue on Linux --- source/gui/detail/bedrock_posix.cpp | 30 +++++++++++++++++---------- source/gui/detail/bedrock_windows.cpp | 5 ----- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index c959b01c..26de1dbc 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -626,30 +626,38 @@ namespace detail if(pressed_wd_space) break; + msgwnd = wd_manager.find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); + if(nullptr == msgwnd) + break; + if(xevent.xbutton.button == Button4 || xevent.xbutton.button == Button5) { //The hovered window receives the message, unlike in Windows, no redirection is required. - nana::point mspos{xevent.xbutton.x, xevent.xbutton.y}; - while(msgwnd) + auto evt_wd = msgwnd; + while(evt_wd) { - if(msgwnd->annex.events_ptr->mouse_wheel.length() != 0) + if(evt_wd->annex.events_ptr->mouse_wheel.length() != 0) { - mspos -= msgwnd->pos_root; arg_wheel arg; arg.which = arg_wheel::wheel::vertical; - assign_arg(arg, msgwnd, xevent); - brock.emit(event_code::mouse_wheel, msgwnd, arg, true, &context); + assign_arg(arg, evt_wd, xevent); + brock.emit(event_code::mouse_wheel, evt_wd, arg, true, &context); break; } - msgwnd = msgwnd->parent; + evt_wd = evt_wd->parent; + } + + if(msgwnd && (nullptr == evt_wd)) + { + arg_wheel arg; + arg.which = arg_wheel::wheel::vertical; + assign_arg(arg, msgwnd, xevent); + draw_invoker(&drawer::mouse_wheel, msgwnd, arg, &context); + wd_manager.do_lazy_refresh(msgwnd, false); } } else { - msgwnd = wd_manager.find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); - if(nullptr == msgwnd) - break; - msgwnd->set_action(mouse_action::normal); if(msgwnd->flags.enabled) { diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index d6019428..300866f0 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1124,8 +1124,6 @@ namespace detail if (evt_wd->annex.events_ptr->mouse_wheel.length() != 0) { def_window_proc = false; - nana::point mspos{ scr_pos.x, scr_pos.y }; - wd_manager.calc_window_point(evt_wd, mspos); arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); @@ -1138,9 +1136,6 @@ namespace detail if (scrolled_wd && (nullptr == evt_wd)) { - nana::point mspos{ scr_pos.x, scr_pos.y }; - wd_manager.calc_window_point(scrolled_wd, mspos); - arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); assign_arg(arg, scrolled_wd, pmdec); From 6eece91fce7b46984ee62764c643631e8eea28b8 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 2 Sep 2016 07:49:55 +0800 Subject: [PATCH 05/14] fix a crash that deleting a widget in its event handler --- source/gui/detail/window_manager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 2fedf0d7..6611306e 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1535,6 +1535,11 @@ namespace detail wd->annex.caret_ptr = nullptr; } + using effect_renderer = detail::edge_nimbus_renderer; + + //remove the window from edge nimbus effect when it is destroying + effect_renderer::instance().erase(wd); + arg_destroy arg; arg.window_handle = reinterpret_cast(wd); brock.emit(event_code::destroy, wd, arg, true, brock.get_thread_context()); From 318f0ec57046c4728c345e08b0a4953d401b0bb5 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 13 Sep 2016 01:24:47 +0800 Subject: [PATCH 06/14] fix issue that event handler remains after closing --- include/nana/gui/widgets/widget.hpp | 25 +++++++++++++++++++++++-- source/gui/detail/basic_window.cpp | 2 -- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 5e1ad652..992a780f 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -150,8 +150,8 @@ namespace nana ~widget_base(); window handle() const override; - private: - void _m_notify_destroy() override final; + protected: + void _m_notify_destroy() override; protected: window handle_{ nullptr }; }; @@ -228,6 +228,13 @@ namespace nana { return *events_; } + + void _m_notify_destroy() override final + { + widget_base::_m_notify_destroy(); + events_ = std::make_shared(); + API::dev::set_events(handle_, events_); + } private: DrawerTrigger trigger_; std::shared_ptr events_; @@ -281,6 +288,13 @@ namespace nana { return *events_; } + + void _m_notify_destroy() override final + { + widget_base::_m_notify_destroy(); + events_ = std::make_shared(); + API::dev::set_events(handle_, events_); + } private: std::shared_ptr events_; std::unique_ptr scheme_; @@ -415,6 +429,13 @@ namespace nana { return *events_; } + + void _m_notify_destroy() override final + { + widget_base::_m_notify_destroy(); + events_ = std::make_shared(); + API::dev::set_events(handle_, events_); + } private: DrawerTrigger trigger_; std::shared_ptr events_; diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 525f5e57..715f7158 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -442,8 +442,6 @@ namespace nana bool basic_window::set_events(const std::shared_ptr& p) { - if (annex.events_ptr) - return false; annex.events_ptr = p; return true; } From cff816f10bf004f91534f39655d8be42fe0ab518 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 14 Sep 2016 05:51:23 +0800 Subject: [PATCH 07/14] fix listbox multi-selection exception(#154) --- source/gui/widgets/listbox.cpp | 58 +++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 91cf6d4e..5bc8fc5b 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1490,10 +1490,15 @@ namespace nana void select_display_range(index_pair fr_abs, index_pair to_dpl, bool sel) { + const auto already_selected = this->pick_items(true); + index_pair fr_dpl (fr_abs.cat, this->display_order(fr_abs.cat, fr_abs.item)); if (fr_dpl > to_dpl) std::swap(fr_dpl, to_dpl); + const auto begin = fr_dpl; + const auto last = to_dpl; + for (; fr_dpl != to_dpl; forward(fr_dpl, 1, fr_dpl)) { if (fr_dpl.is_item()) @@ -1502,6 +1507,14 @@ namespace nana if (to_dpl.is_item()) item_proxy(ess_, index_pair(to_dpl.cat, absolute( to_dpl ) )).select(sel); + + //Unselects the already selected which is out of range [begin, last] + for (auto index : already_selected) + { + index_pair disp_order{ index.cat, this->display_order(index.cat, index.item) }; + if (begin > disp_order || disp_order > last) + item_proxy(ess_, index_pair(index.cat, absolute(disp_order))).select(false); + } } bool select_for_all(bool sel) @@ -2060,7 +2073,7 @@ namespace nana return i; } public: - index_pair last_selected_abs, last_selected_dpl; + index_pair last_selected_abs; private: essence * ess_{nullptr}; nana::listbox * widget_{nullptr}; @@ -3126,7 +3139,7 @@ namespace nana void es_lister::move_select(bool upwards, bool unselect_previous, bool trace_selected) { - auto next_selected_dpl = relative_pair ( last_selected_abs); // last_selected_dpl; // ?? + auto next_selected_dpl = relative_pair ( last_selected_abs); if (next_selected_dpl.empty()) // has no cat ? (cat == npos) => beging from first cat { bool good = false; @@ -3247,7 +3260,7 @@ namespace nana if (it.selected() != sel) it.select(sel); } - last_selected_abs = last_selected_dpl = index_pair{cat, npos}; + last_selected_abs = index_pair{cat, npos}; } class drawer_header_impl @@ -4165,7 +4178,18 @@ namespace nana if (!lister.single_selection()) { if (arg.shift) - lister.select_display_range(lister.last_selected_abs , item_pos, sel); + { + //Set the first item as the begin of selected item if there + //is not a last selected item.(#154 reported by RenaudAlpes) + if (lister.last_selected_abs.empty() || lister.last_selected_abs.is_category()) + lister.last_selected_abs.set_both(0); + + auto before = lister.last_selected_abs; + + lister.select_display_range(lister.last_selected_abs, item_pos, sel); + + lister.last_selected_abs = before; + } else if (arg.ctrl) sel = !item_proxy(essence_, abs_item_pos).selected(); else @@ -4181,19 +4205,22 @@ namespace nana if(item_ptr) { - item_ptr->flags.selected = sel; - - arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; - lister.wd_ptr()->events().selected.emit(arg, lister.wd_ptr()->handle()); - - if (item_ptr->flags.selected) + if (item_ptr->flags.selected != sel) { - lister.cancel_others_if_single_enabled(true, abs_item_pos); - essence_->lister.last_selected_abs = abs_item_pos; + item_ptr->flags.selected = sel; + arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; + lister.wd_ptr()->events().selected.emit(arg, lister.wd_ptr()->handle()); + + if (item_ptr->flags.selected) + { + lister.cancel_others_if_single_enabled(true, abs_item_pos); + essence_->lister.last_selected_abs = abs_item_pos; + + } + else if (essence_->lister.last_selected_abs == abs_item_pos) + essence_->lister.last_selected_abs.set_both(npos); } - else if (essence_->lister.last_selected_abs == abs_item_pos) - essence_->lister.last_selected_abs.set_both(npos); } else if(!lister.single_selection()) lister.categ_selected(item_pos.cat, true); @@ -4772,10 +4799,11 @@ namespace nana for (item_proxy &it : *this ) it.select(sel); - ess_->lister.last_selected_abs = ess_->lister.last_selected_dpl = index_pair {this->pos_, npos}; + ess_->lister.last_selected_abs = index_pair {this->pos_, npos}; return *this; } + bool cat_proxy::selected() const { for (item_proxy &it : *this ) From 07229e566db03cc640a303b432243e7b13ca6435 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 15 Sep 2016 16:35:43 +0800 Subject: [PATCH 08/14] simplify implementation of listbox inline widget --- source/gui/widgets/listbox.cpp | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 5bc8fc5b..90fdcbdf 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -2139,7 +2139,6 @@ namespace nana inline_indicator * indicator; index_pair item_pos; //The item index of the inline widget std::size_t column_pos; - ::std::string text; //text in UTF-8 encoded }; std::map>> inline_table, inline_buffered_table; @@ -2986,7 +2985,7 @@ namespace nana : ess_{ ess }, column_pos_{column_pos} { } - + void attach(index_type pos, essence::inline_pane* pane) { for (auto & pn : panes_) @@ -3023,15 +3022,6 @@ namespace nana if (cells[column_pos_].text != value) { - for (auto & pn : panes_) - { - if (pn.first == pos) - { - pn.second->text = value; - break; - } - } - cells[column_pos_].text = value; if (model_cells.size()) @@ -3862,19 +3852,9 @@ namespace nana //To reduce the memory usage, the cells may not be allocated if (cells.size() > column_pos) - { - auto & text = cells[column_pos].text; - if (text != inline_wdg->text) - { - inline_wdg->text = text; - inline_wdg->inline_ptr->set(text); - } - } + inline_wdg->inline_ptr->set(cells[column_pos].text); else - { - inline_wdg->text.clear(); inline_wdg->inline_ptr->set({}); - } API::show_window(inline_wdg->pane_bottom, visible_state); } @@ -3932,7 +3912,9 @@ namespace nana { auto & factory = cat.factories[column_pos]; if (factory) + { return essence_->open_inline(factory.get(), cat.indicators[column_pos].get()); + } } return nullptr; } From d9fb3b3ff9265a3df3b4b1e3cf2536998c0db280 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 17 Sep 2016 03:56:23 +0800 Subject: [PATCH 09/14] add notify_status for listbox inline widget --- .../nana/gui/widgets/detail/inline_widget.hpp | 13 +- include/nana/gui/widgets/listbox.hpp | 8 +- source/gui/widgets/listbox.cpp | 143 ++++++++++++------ 3 files changed, 114 insertions(+), 50 deletions(-) diff --git a/include/nana/gui/widgets/detail/inline_widget.hpp b/include/nana/gui/widgets/detail/inline_widget.hpp index 6b40c7e3..875c2945 100644 --- a/include/nana/gui/widgets/detail/inline_widget.hpp +++ b/include/nana/gui/widgets/detail/inline_widget.hpp @@ -1,7 +1,7 @@ /** * A Inline Widget Interface Definition * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -35,6 +35,9 @@ namespace nana /// Returns the host widget of the indicator virtual ::nana::widget& host() const = 0; + /// Returns the position of column + virtual std::size_t column() const = 0; + /// Modifies the value of a item specified by pos virtual void modify(index_type pos, const value_type&) const = 0; @@ -45,7 +48,7 @@ namespace nana virtual void hovered(index_type) = 0; }; - template + template class inline_widget_notifier_interface { public: @@ -55,6 +58,9 @@ namespace nana /// A type to the value of the item using value_type = Value; + /// A type to the status + using status_type = Status; + /// A typedef name of a inline widget indicator using inline_indicator = inline_widget_indicator; @@ -70,6 +76,9 @@ namespace nana /// A message to activate the inline widget to attach a specified item virtual void activate(inline_indicator&, index_type) = 0; + /// A message to change the status + virtual void notify_status(status_type, bool) = 0; + /// A message to resize the inline widget virtual void resize(const size&) = 0; diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index e4b06724..4e414760 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -676,8 +676,14 @@ namespace nana using index_pairs = ::std::vector; - using inline_notifier_interface = detail::inline_widget_notifier_interface; + enum class inline_widget_status{ + checked, + checking, + selected, + selecting + }; + using inline_notifier_interface = detail::inline_widget_notifier_interface; // struct essence //@brief: this struct gives many data for listbox, diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 90fdcbdf..98dd3732 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -678,6 +678,16 @@ namespace nana } }; + struct inline_pane + { + ::nana::panel pane_bottom; //pane for pane_widget + ::nana::panel pane_widget; //pane for placing user-define widget + std::unique_ptr inline_ptr; + inline_indicator * indicator; + index_pair item_pos; //The item index of the inline widget + std::size_t column_pos; + }; + class es_lister { public: @@ -726,6 +736,37 @@ namespace nana std::string to_string(const export_options& exp_opt) const; + inline_pane * get_inline_pane(const index_pair& item_pos) + { + for (auto p : active_panes_) + { + if (p && (p->item_pos == item_pos)) + return p; + } + return nullptr; + } + + void emit_checked(index_pair pos) + { + item_proxy i(ess_, pos); + arg_listbox arg{ i }; + wd_ptr()->events().checked.emit(arg, wd_ptr()->handle()); + + auto pane = get_inline_pane(pos); + if (pane) + pane->inline_ptr->notify_status(inline_widget_status::checking, i.checked()); + } + + void emit_selected(index_pair pos) + { + item_proxy i(ess_, pos); + arg_listbox arg{ i }; + wd_ptr()->events().selected.emit(arg, wd_ptr()->handle()); + + auto pane = get_inline_pane(pos); + if (pane) + pane->inline_ptr->notify_status(inline_widget_status::selecting, i.selected()); + } // Definition is provided after struct essence unsigned column_content_pixels(size_type pos) const; @@ -1121,6 +1162,14 @@ namespace nana return get(pos.cat)->items.at(index); } + void append_active_panes(inline_pane* p) + { + if (nullptr == p) + active_panes_.clear(); + else + active_panes_.push_back(p); + } + // Removes all items of a specified category // It throws when the category is out of range or has an immutable model. void clear(size_type cat) @@ -1463,9 +1512,7 @@ namespace nana if(m.flags.checked != ck) { m.flags.checked = ck; - - arg_listbox arg{ item_proxy{ess_, pos}}; - wd_ptr()->events().checked.emit(arg, wd_ptr()->handle()); + emit_checked(pos); } ++pos.item; } @@ -1531,8 +1578,7 @@ namespace nana changed = true; m.flags.selected = sel; - arg_listbox arg{ item_proxy(ess_, i) }; - wd_ptr()->events().selected.emit(arg, wd_ptr()->handle()); + this->emit_selected(i); if (m.flags.selected) last_selected_abs = i; @@ -1622,18 +1668,17 @@ namespace nana return (for_selection ? m.flags.selected : m.flags.checked); }; - auto do_cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) + auto do_cancel = [this, for_selection](category_t::container::value_type& m, const index_pair& item_pos) { - arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)) }; if (for_selection) { m.flags.selected = false; - widget_->events().selected.emit(arg, widget_->handle()); + this->emit_selected(item_pos); } else { m.flags.checked = false; - widget_->events().checked.emit(arg, widget_->handle()); + this->emit_checked(item_pos); } }; @@ -1645,7 +1690,7 @@ namespace nana for (auto & m : i->items) { if ((item_pos != except.item) && pred(m)) - do_cancel(m, except.cat, item_pos); + do_cancel(m, index_pair{ except.cat, item_pos }); ++item_pos; } @@ -1661,7 +1706,7 @@ namespace nana for (auto & m : cat.items) { if (pred(m)) - do_cancel(m, cat_pos, item_pos); + do_cancel(m, index_pair{ cat_pos, item_pos }); ++item_pos; } } @@ -1671,7 +1716,7 @@ namespace nana for (auto & m : cat.items) { if ((item_pos != except.item) && pred(m)) - do_cancel(m, cat_pos, item_pos); + do_cancel(m, index_pair{ cat_pos, item_pos }); ++item_pos; } } @@ -1705,18 +1750,17 @@ namespace nana return (for_selection ? m.flags.selected : m.flags.checked); }; - auto cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) + auto cancel = [this, for_selection](category_t::container::value_type& m, const index_pair& item_pos) { - arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)) }; if (for_selection) { m.flags.selected = false; - widget_->events().selected.emit(arg, widget_->handle()); + this->emit_selected(item_pos); } else { m.flags.checked = false; - widget_->events().checked.emit(arg, widget_->handle()); + this->emit_checked(item_pos); } }; @@ -1732,7 +1776,7 @@ namespace nana for (auto end = cat.items.end(); i != end; ++i) { if (pred(*i)) - cancel(*i, cat_pos, i - cat.items.begin()); + cancel(*i, index_pair{ cat_pos, static_cast(i - cat.items.begin()) }); } } ++cat_pos; @@ -1755,7 +1799,7 @@ namespace nana for (++i; i != end; ++i) { if (pred(*i)) - cancel(*i, cat_pos, i - cat.items.begin()); + cancel(*i, index_pair{ cat_pos, static_cast(i - cat.items.begin()) }); } } } @@ -1767,7 +1811,7 @@ namespace nana for (auto & m : cat.items) { if (pred(m)) - cancel(m, cat_pos, item_pos); + cancel(m, index_pair{ cat_pos, item_pos }); ++item_pos; } @@ -1814,10 +1858,7 @@ namespace nana if(m.flags.checked != ck) { m.flags.checked = ck; - - arg_listbox arg{ item_proxy(ess_, index_pair(cat, index)) }; - wd_ptr()->events().checked.emit(arg, widget_->handle()); - + this->emit_checked(index_pair{cat, index}); changed = true; } ++index; @@ -2088,6 +2129,8 @@ namespace nana bool single_selection_category_limited_{ false }; bool single_check_{ false }; bool single_check_category_limited_{ false }; + + std::vector active_panes_; };//end class es_lister @@ -2097,6 +2140,8 @@ namespace nana enum class item_state{normal, highlighted, pressed, grabbed, floated}; enum class parts{unknown = -1, header, lister, checker}; + using inline_pane = inline_pane; + ::nana::listbox* listbox_ptr{nullptr}; ::nana::listbox::scheme_type* scheme_ptr{nullptr}; ::nana::paint::graphics *graph{nullptr}; @@ -2131,16 +2176,6 @@ namespace nana }scroll; - struct inline_pane - { - ::nana::panel pane_bottom; //pane for pane_widget - ::nana::panel pane_widget; //pane for placing user-define widget - std::unique_ptr inline_ptr; - inline_indicator * indicator; - index_pair item_pos; //The item index of the inline widget - std::size_t column_pos; - }; - std::map>> inline_table, inline_buffered_table; essence() @@ -3010,6 +3045,11 @@ namespace nana return *ess_->lister.wd_ptr(); } + std::size_t column() const override + { + return column_pos_; + } + void modify(index_type pos, const value_type& value) const override { ess_->lister.throw_if_immutable_model(pos); @@ -3533,15 +3573,19 @@ namespace nana public: using item_state = essence::item_state; using parts = essence::parts; + using status_type = inline_notifier_interface::status_type; drawer_lister_impl(essence * es) :essence_(es) {} - void draw(const nana::rectangle& rect) const + void draw(const nana::rectangle& rect) { internal_scope_guard lock; + //clear active panes + essence_->lister.append_active_panes(nullptr); + //The count of items to be drawn auto item_count = essence_->number_of_lister_items(true); if (0 == item_count) @@ -3724,7 +3768,7 @@ namespace nana nana::color bgcolor, nana::color fgcolor, item_state state - ) const + ) { auto & item = cat.items[item_pos.item]; @@ -3838,15 +3882,19 @@ namespace nana else visible_state = false; - ::nana::size sz{ wdg_w, essence_->scheme_ptr->item_height }; - inline_wdg->pane_widget.size(sz); - inline_wdg->inline_ptr->resize(sz); draw_column = inline_wdg->inline_ptr->whether_to_draw(); inline_wdg->item_pos = item_pos; inline_wdg->column_pos = column_pos; inline_wdg->inline_ptr->activate(*inline_wdg->indicator, item_pos); + + ::nana::size sz{ wdg_w, essence_->scheme_ptr->item_height }; + inline_wdg->pane_widget.size(sz); + inline_wdg->inline_ptr->resize(sz); + + inline_wdg->inline_ptr->notify_status(status_type::selected, item.flags.selected); + inline_wdg->inline_ptr->notify_status(status_type::checked, item.flags.checked); inline_wdg->indicator->attach(item_pos, inline_wdg); @@ -3857,6 +3905,8 @@ namespace nana inline_wdg->inline_ptr->set({}); API::show_window(inline_wdg->pane_bottom, visible_state); + + essence_->lister.append_active_panes(inline_wdg); } } @@ -4191,14 +4241,12 @@ namespace nana { item_ptr->flags.selected = sel; - arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; - lister.wd_ptr()->events().selected.emit(arg, lister.wd_ptr()->handle()); + lister.emit_selected(abs_item_pos); if (item_ptr->flags.selected) { lister.cancel_others_if_single_enabled(true, abs_item_pos); essence_->lister.last_selected_abs = abs_item_pos; - } else if (essence_->lister.last_selected_abs == abs_item_pos) essence_->lister.last_selected_abs.set_both(npos); @@ -4213,8 +4261,7 @@ namespace nana { item_ptr->flags.checked = ! item_ptr->flags.checked; - arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; - lister.wd_ptr()->events().checked.emit(arg, lister.wd_ptr()->handle()); + lister.emit_checked(abs_item_pos); if (item_ptr->flags.checked) lister.cancel_others_if_single_enabled(false, abs_item_pos); @@ -4479,12 +4526,13 @@ namespace nana item_proxy & item_proxy::check(bool ck) { + internal_scope_guard lock; auto & m = cat_->items.at(pos_.item); if(m.flags.checked != ck) { m.flags.checked = ck; - arg_listbox arg{*this}; - ess_->lister.wd_ptr()->events().checked.emit(arg, ess_->lister.wd_ptr()->handle()); + ess_->lister.emit_checked(pos_); + ess_->update(); } return *this; @@ -4498,13 +4546,14 @@ namespace nana /// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected item_proxy & item_proxy::select(bool s) { + internal_scope_guard lock; + //pos_ never represents a category if this item_proxy is available. auto & m = cat_->items.at(pos_.item); // a ref to the real item if(m.flags.selected == s) return *this; // ignore if no change m.flags.selected = s; // actually change selection - arg_listbox arg{*this}; - ess_->lister.wd_ptr()->events().selected.emit(arg, ess_->lister.wd_ptr()->handle()); + ess_->lister.emit_selected(this->pos_); if (m.flags.selected) { From 7ff4762cdbc39a976a753034bb2ea9cc424e3b3c Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 21 Sep 2016 07:08:30 +0800 Subject: [PATCH 10/14] add variadic parameters for make_factory --- include/nana/pat/abstract_factory.hpp | 64 ++++++++++++++++++++++++--- include/nana/pat/cloneable.hpp | 6 +-- source/gui/widgets/listbox.cpp | 10 ++--- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/include/nana/pat/abstract_factory.hpp b/include/nana/pat/abstract_factory.hpp index 9a4b3bfe..8aa44e7f 100644 --- a/include/nana/pat/abstract_factory.hpp +++ b/include/nana/pat/abstract_factory.hpp @@ -15,6 +15,8 @@ #define NANA_PAT_ABSFACTORY_HPP #include "cloneable.hpp" #include +#include +#include namespace nana { @@ -43,21 +45,73 @@ namespace nana namespace detail { - template + template + struct pack{ + using type = pack; + }; + + template + struct make_pack_helper + { // explodes gracefully below 0 + static_assert(!Negative, + "make_integer_sequence requires N to be non-negative."); + }; + + template + struct make_pack_helper, + pack > + : pack + { // ends recursion at 0 + }; + + template + struct make_pack_helper, + pack > + : make_pack_helper, + pack > + { // counts down to 0 + }; + + template + using make_pack = typename make_pack_helper, pack >::type; + + template class abs_factory : public abstract_factory { std::unique_ptr create() override { - return std::unique_ptr{ new T }; + constexpr auto Size = std::tuple_size::value; + return std::unique_ptr{ _m_new(typename make_pack{}) }; } + + template + Interface* _m_new(const pack &) + { + return new T(std::get(args_)...); + } + public: + abs_factory(Args&&... args) + : args_(std::forward(args)...) + { + } + + abs_factory(const Args&... args) + : args_(args...) + { + } + private: + std::tuple args_; }; }//end namespace detail - template - pat::cloneable> make_factory() + template + pat::cloneable> make_factory(Args &&... args) { - return detail::abs_factory(); + return detail::abs_factory::type...>(std::forward(args)...); } }//end namespace pat }//end namespace nana diff --git a/include/nana/pat/cloneable.hpp b/include/nana/pat/cloneable.hpp index c6f5d1c9..350dbe1a 100644 --- a/include/nana/pat/cloneable.hpp +++ b/include/nana/pat/cloneable.hpp @@ -1,7 +1,7 @@ /* * A Generic Cloneable Pattern Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -106,8 +106,8 @@ namespace nana{ namespace pat{ template::type* = nullptr> cloneable(T&& t) - : cwrapper_(new detail::cloneable_wrapper::type>::type>(std::forward(t)), detail::cloneable_interface_deleter()), - fast_ptr_(reinterpret_cast::type>::type*>(cwrapper_->get())) + : cwrapper_(new detail::cloneable_wrapper::type>(std::forward(t)), detail::cloneable_interface_deleter()), + fast_ptr_(reinterpret_cast::type*>(cwrapper_->get())) {} cloneable(const cloneable& r) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 98dd3732..1dcb7f9d 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -2140,8 +2140,6 @@ namespace nana enum class item_state{normal, highlighted, pressed, grabbed, floated}; enum class parts{unknown = -1, header, lister, checker}; - using inline_pane = inline_pane; - ::nana::listbox* listbox_ptr{nullptr}; ::nana::listbox::scheme_type* scheme_ptr{nullptr}; ::nana::paint::graphics *graph{nullptr}; @@ -3021,7 +3019,7 @@ namespace nana { } - void attach(index_type pos, essence::inline_pane* pane) + void attach(index_type pos, inline_pane* pane) { for (auto & pn : panes_) { @@ -3093,7 +3091,7 @@ namespace nana private: essence * const ess_; const std::size_t column_pos_; - std::vector> panes_; + std::vector> panes_; }; void es_lister::scroll(const index_pair& pos, bool to_bottom) @@ -3956,7 +3954,7 @@ namespace nana _m_draw_border(content_r.x, y, show_w); } - essence::inline_pane * _m_get_inline_pane(const category_t& cat, std::size_t column_pos) const + inline_pane * _m_get_inline_pane(const category_t& cat, std::size_t column_pos) const { if (column_pos < cat.factories.size()) { @@ -3969,7 +3967,7 @@ namespace nana return nullptr; } - essence::inline_pane* _m_find_inline_pane(const index_pair& pos, std::size_t column_pos) const + inline_pane* _m_find_inline_pane(const index_pair& pos, std::size_t column_pos) const { auto & cat = *essence_->lister.get(pos.cat); From 74e113b2cce2cc90e3503b9d44b83a70d9793613 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 21 Sep 2016 08:19:00 +0800 Subject: [PATCH 11/14] returns not only one inline widget --- source/gui/widgets/listbox.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 1dcb7f9d..a37e20c9 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -736,14 +736,17 @@ namespace nana std::string to_string(const export_options& exp_opt) const; - inline_pane * get_inline_pane(const index_pair& item_pos) + std::vector get_inline_pane(const index_pair& item_pos) { + std::vector panes; for (auto p : active_panes_) { if (p && (p->item_pos == item_pos)) - return p; + { + panes.emplace_back(p); + } } - return nullptr; + return panes; } void emit_checked(index_pair pos) @@ -752,9 +755,9 @@ namespace nana arg_listbox arg{ i }; wd_ptr()->events().checked.emit(arg, wd_ptr()->handle()); - auto pane = get_inline_pane(pos); - if (pane) - pane->inline_ptr->notify_status(inline_widget_status::checking, i.checked()); + auto panes = get_inline_pane(pos); + for (auto p : panes) + p->inline_ptr->notify_status(inline_widget_status::checking, i.checked()); } void emit_selected(index_pair pos) @@ -763,9 +766,9 @@ namespace nana arg_listbox arg{ i }; wd_ptr()->events().selected.emit(arg, wd_ptr()->handle()); - auto pane = get_inline_pane(pos); - if (pane) - pane->inline_ptr->notify_status(inline_widget_status::selecting, i.selected()); + auto panes = get_inline_pane(pos); + for (auto p : panes) + p->inline_ptr->notify_status(inline_widget_status::selecting, i.selected()); } // Definition is provided after struct essence From c916d0ace30a3bc686570a0813e4131538524251 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 22 Sep 2016 08:03:30 +0800 Subject: [PATCH 12/14] add textbox::set_undo_queue_length a new method for setting the undo queue length --- include/nana/gui/widgets/skeletons/text_editor.hpp | 9 +++++++++ include/nana/gui/widgets/textbox.hpp | 6 ++++++ source/gui/widgets/skeletons/text_editor.cpp | 5 +++++ source/gui/widgets/textbox.cpp | 8 ++++++++ 4 files changed, 28 insertions(+) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 22e099e4..a6904e2e 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -52,7 +52,15 @@ namespace nana{ namespace widgets { max_steps_ = maxs; if (maxs && (commands_.size() >= maxs)) + { + auto move = commands_.size() - pos_; commands_.erase(commands_.begin(), commands_.begin() + (commands_.size() - maxs + 1)); + pos_ = commands_.size() - std::min(move, commands_.size()); + } + else if (0 == maxs) + { + clear(); + } } std::size_t max_steps() const @@ -258,6 +266,7 @@ namespace nana{ namespace widgets void del(); void backspace(bool record_undo = true); void undo(bool reverse); + void set_undo_queue_length(std::size_t len); void move_ns(bool to_north); //Moves up and down void move_left(); void move_right(); diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 12008c37..b10425f7 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -228,6 +228,12 @@ namespace nana /// E.g. Whether caret moves to left of selected content or moves to left of last position when left arrow key is pressed. /// @param move_to_end determines whether to move caret to left of selected_content or to left of last position. void select_behavior(bool move_to_end); + + /// Sets the undo/redo queue length + /** + * @param len The length of the queue. If this parameter is zero, the undo/redo is disabled. + */ + void set_undo_queue_length(std::size_t len); protected: //Overrides widget's virtual functions native_string_type _m_caption() const throw() override; diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index a030471a..3cda2d81 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -2348,6 +2348,11 @@ namespace nana{ namespace widgets _m_scrollbar(); } + void text_editor::set_undo_queue_length(std::size_t len) + { + undo_.max_steps(len); + } + void text_editor::move_ns(bool to_north) { const bool redraw_required = _m_cancel_select(0); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 76aed3ac..dcc2b702 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -606,6 +606,14 @@ namespace drawerbase { editor->select_behavior(move_to_end); } + void textbox::set_undo_queue_length(std::size_t len) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + editor->set_undo_queue_length(len); + } + //Override _m_caption for caption() auto textbox::_m_caption() const throw() -> native_string_type { From 8dccccc420d01b630d781114d19ac05b00cefc4a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 22 Sep 2016 08:10:24 +0800 Subject: [PATCH 13/14] fix a syntax error --- include/nana/pat/abstract_factory.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nana/pat/abstract_factory.hpp b/include/nana/pat/abstract_factory.hpp index 8aa44e7f..bf1769e2 100644 --- a/include/nana/pat/abstract_factory.hpp +++ b/include/nana/pat/abstract_factory.hpp @@ -85,7 +85,7 @@ namespace nana std::unique_ptr create() override { constexpr auto Size = std::tuple_size::value; - return std::unique_ptr{ _m_new(typename make_pack{}) }; + return std::unique_ptr{ _m_new(make_pack{}) }; } template From 0e6ea358580c8db2cea89d3568bbd4496129aa88 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 24 Sep 2016 08:15:44 +0800 Subject: [PATCH 14/14] fix wrong order of widget deletion --- include/nana/gui/widgets/widget.hpp | 22 ++++++++++++++++++++-- source/gui/widgets/widget.cpp | 6 ------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 992a780f..3b6ec2ae 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -147,8 +147,6 @@ namespace nana : public widget { public: - ~widget_base(); - window handle() const override; protected: void _m_notify_destroy() override; @@ -172,6 +170,11 @@ namespace nana scheme_{ API::dev::make_scheme() } {} + ~widget_object() + { + API::close_window(handle()); + } + event_type& events() const { return *events_; @@ -255,6 +258,11 @@ namespace nana : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } {} + ~widget_object() + { + API::close_window(handle()); + } + event_type& events() const { return *events_; @@ -329,6 +337,11 @@ namespace nana _m_bind_and_attach(); } + ~widget_object() + { + API::close_window(handle()); + } + event_type& events() const { return *events_; @@ -461,6 +474,11 @@ namespace nana : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } {} + ~widget_object() + { + API::close_window(handle()); + } + event_type& events() const { return *events_; diff --git a/source/gui/widgets/widget.cpp b/source/gui/widgets/widget.cpp index ac50687b..183768ab 100644 --- a/source/gui/widgets/widget.cpp +++ b/source/gui/widgets/widget.cpp @@ -382,12 +382,6 @@ namespace nana } //class widget_base - widget_base::~widget_base() - { - if (handle_) - API::close_window(handle_); - } - window widget_base::handle() const { return handle_;