diff --git a/.travis.yml b/.travis.yml index 08c3d6f0..7b71b9ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,11 @@ cache: matrix: include: - - env: CXX=g++-5 CC=gcc-5 + - env: CXX=g++-8 addons: apt: packages: - - g++-5 + - g++-8 - libjpeg8-dev - libpng-dev - libasound2-dev @@ -21,7 +21,7 @@ matrix: - libx11-dev - libxft-dev - libxcursor-dev - sources: + sources: - ubuntu-toolchain-r-test allow_failures: @@ -44,7 +44,7 @@ matrix: before_install: - cd .. - - git clone --depth=1 --branch=cmake-dev https://github.com/qPCR4vir/nana-demo.git nana-demo + - git clone --depth=1 --branch=hotfix https://github.com/qPCR4vir/nana-demo.git nana-demo - export PATH="$HOME/bin:$PATH" #- mkdir ~/bin #it seemd that a bin already exists from 20170901 diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index f14c043f..64b37d97 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -254,7 +254,7 @@ namespace nana { namespace experimental { namespace filesystem path relative_path() const; path parent_path() const; path filename() const; - //path stem() const; + path stem() const; path extension() const; // query @@ -290,6 +290,8 @@ namespace nana { namespace experimental { namespace filesystem // std::u16string generic_u16string() const; // std::u32string generic_u32string() const; + path lexically_normal() const; + //appends path& operator/=(const path& other); @@ -544,6 +546,9 @@ namespace std { path canonical(const path& p); path canonical(const path& p, std::error_code& err); + + path weakly_canonical(const path& p); + path weakly_canonical(const path& p, std::error_code& err); #endif #if defined(NANA_FILESYSTEM_FORCE) || defined(NANA_MINGW) diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index b79ec168..bc0a116f 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -127,6 +127,9 @@ namespace detail basic_window * seek_non_lite_widget_ancestor() const; void set_action(mouse_action); + + /// Only refresh when the root of window is in lazy-updating mode + bool try_lazy_update(bool try_refresh); public: /// Override event_holder bool set_events(const std::shared_ptr&) override; @@ -176,8 +179,7 @@ namespace detail bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key. bool draggable : 1; - bool ignore_child_mapping : 1; - unsigned Reserved :16; + unsigned Reserved : 17; unsigned char tab; ///< indicate a window that can receive the keyboard TAB mouse_action action; mouse_action action_before; @@ -205,11 +207,14 @@ namespace detail { struct attr_root_tag { + bool ime_enabled{ false }; + bool lazy_update{ false }; ///< Indicates whether the window is in lazy-updating mode. + + container update_requesters; ///< Container for lazy-updating requesting windows. container tabstop; std::vector effects_edge_nimbus; basic_window* focus{nullptr}; basic_window* menubar{nullptr}; - bool ime_enabled{false}; cursor state_cursor{nana::cursor::arrow}; basic_window* state_cursor_window{ nullptr }; @@ -223,8 +228,6 @@ namespace detail update_state upd_state; dragdrop_status dnd_state{ dragdrop_status::not_ready }; - container mapping_requester; ///< Children which are ignored to mapping - union { attr_root_tag * root; diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index 82e3c57c..d9c26326 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -99,7 +99,11 @@ namespace detail bool show(core_window_t* wd, bool visible); - core_window_t* find_window(native_window_type root, const point& pos); + //find a widget window at specified position + //@param root A root window + //@param pos Position + //@param ignore_captured A flag indicates whether to ignore redirecting the result to its captured window. If this paramter is true, it returns the window at the position, if the parameter is false, it returns the captured window if the captured window don't ignore children. + core_window_t* find_window(native_window_type root, const point& pos, bool ignore_captured = false); //move the wnd and its all children window, x and y is a relatively coordinate for wnd's parent window bool move(core_window_t*, int x, int y, bool passive); @@ -116,7 +120,6 @@ namespace detail void refresh_tree(core_window_t*); void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); - void map_requester(core_window_t*); bool set_parent(core_window_t* wd, core_window_t* new_parent); core_window_t* set_focus(core_window_t*, bool root_has_been_focused, arg_focus::reason); diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index b13e8465..16f3e448 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -225,8 +225,12 @@ namespace nana { namespace experimental { namespace filesystem //Because of No wide character version of POSIX #if defined(NANA_POSIX) const char* separators = "/"; + const char separator = '/'; + const char* punt = "."; #else const wchar_t* separators = L"/\\"; + const char separator = '\\'; + const wchar_t* punt = L"."; #endif //class file_status @@ -453,6 +457,22 @@ namespace nana { namespace experimental { namespace filesystem return{ pathstr_ }; } + path path::stem() const + { + auto pos = pathstr_.find_last_of(separators); + auto ext = pathstr_.find_last_of(punt); + + if (pos == pathstr_.npos) + pos = 0; + else + pos++; + + if (ext == pathstr_.npos || ext < pos) + return path(pathstr_.substr(pos)); + else + return path(pathstr_.substr(pos, ext-pos)); + } + void path::clear() noexcept { pathstr_.clear(); @@ -550,6 +570,71 @@ namespace nana { namespace experimental { namespace filesystem std::replace(str.begin(), str.end(), '\\', '/'); // uppss ... revise this !!!!! return to_utf8(str); } + + path path::lexically_normal() const + { + if (pathstr_.empty()) + return *this; + + std::vector elements; + path temp{ pathstr_ }; + while (!temp.empty()) + { + elements.emplace_back(temp.filename()); + temp.remove_filename(); + } + + auto start = elements.begin(); + auto last = elements.end(); + auto stop = last--; + for (auto itr(start); itr != stop; ++itr) + { + // ignore "." except at start and last + if (itr->native().size() == 1 + && (itr->native())[0] == '.' + && itr != start + && itr != last) continue; + + // ignore a name and following ".." + if (!temp.empty() + && itr->native().size() == 2 + && (itr->native())[0] == '.' + && (itr->native())[1] == '.') // dot dot + { + string_type lf(temp.filename().native()); + if (lf.size() > 0 + && (lf.size() != 1 + || (lf[0] != '.' + && (lf[0] != '/' && lf[0] != '\\'))) + && (lf.size() != 2 + || (lf[0] != '.' + && lf[1] != '.' +# ifdef NANA_WINDOWS + && lf[1] != ':' +# endif + ) + ) + ) + { + temp.remove_filename(); + auto next = itr; + if (temp.empty() && ++next != stop + && next == last && last->string() == ".") + { + temp /= "."; + } + continue; + } + } + + temp /= *itr; + }; + + if (temp.empty()) + temp = "."; + return temp; + } + path & path::operator/=(const path& p) { if (p.empty()) @@ -1254,6 +1339,81 @@ namespace std { return canonical(p, &err); } + + bool try_throw(int err_val, const path& p, std::error_code* ec, const char* message) + { + if (0 == err_val) + { + if (ec) ec->clear(); + } + else + { //error + if (nullptr == ec) + throw (filesystem_error( + "nana::filesystem::canonical", p, + error_code(err_val, generic_category()))); + else + ec->assign(err_val, system_category()); + } + return err_val != 0; + } + + path weakly_canonical(const path& p, std::error_code* err) + { + path head{ p }; + + std::error_code tmp_err; + std::vector elements; + while (!head.empty()) + { + auto head_status = status(head, tmp_err); + + if (head_status.type() == file_type::unknown) + { + if (try_throw(static_cast(errc::invalid_argument), head, err, "nana::filesystem::weakly_canonical")) + return path{}; + } + if (head_status.type() != file_type::not_found) + break; + + elements.emplace_back(head.filename()); + head.remove_filename(); + } + + bool tail_has_dots = false; + path tail; + + for (auto & e : elements) + { + tail /= e; + // for a later optimization, track if any dot or dot-dot elements are present + if (e.native().size() <= 2 + && e.native()[0] == '.' + && (e.native().size() == 1 || e.native()[1] == '.')) + tail_has_dots = true; + } + + if (head.empty()) + return p.lexically_normal(); + head = canonical(head, tmp_err); + if (try_throw(tmp_err.value(), head, err, "nana::filesystem::weakly_canonical")) + return path(); + return tail.empty() + ? head + : (tail_has_dots // optimization: only normalize if tail had dot or dot-dot element + ? (head / tail).lexically_normal() + : head / tail); + } + + path weakly_canonical(const path& p) + { + return weakly_canonical(p, nullptr); + } + + path weakly_canonical(const path& p, std::error_code& err) + { + return weakly_canonical(p, &err); + } #endif #if defined(NANA_FILESYSTEM_FORCE) || defined(NANA_MINGW) diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index a52b2215..e52d402e 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -336,6 +336,43 @@ namespace nana flags.action = act; } + + bool basic_window::try_lazy_update(bool try_refresh) + { + if (drawer.graphics.empty()) + return true; + + if (!this->root_widget->other.attribute.root->lazy_update) + return false; + + if (nullptr == effect.bground) + { + if (try_refresh) + { + flags.refreshing = true; + drawer.refresh(); + flags.refreshing = false; + } + } + + for (auto i = this->root_widget->other.attribute.root->update_requesters.cbegin(); i != this->root_widget->other.attribute.root->update_requesters.cend();) + { + auto req = *i; + //Avoid redundancy, don't insert the window if it or its ancestor window already exist in the container. + if ((req == this) || req->is_ancestor_of(this)) + return true; + + //If there is a window which is a child or child's child of the window, remove it. + if (this->is_ancestor_of(req)) + i = this->root_widget->other.attribute.root->update_requesters.erase(i); + else + ++i; + } + + this->root_widget->other.attribute.root->update_requesters.push_back(this); + return true; + } + void basic_window::_m_init_pos_and_size(basic_window* parent, const rectangle& r) { pos_owner = pos_root = r.position(); @@ -390,7 +427,6 @@ namespace nana flags.ignore_menubar_focus = false; flags.ignore_mouse_focus = false; flags.space_click_enabled = false; - flags.ignore_child_mapping = false; visible = false; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 65d644ec..0cd1e796 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -641,13 +641,8 @@ namespace nana if (update_state::none == wd->other.upd_state) wd->other.upd_state = update_state::lazy; - auto ignore_mapping_value = wd->flags.ignore_child_mapping; - wd->flags.ignore_child_mapping = true; - _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); - wd->flags.ignore_child_mapping = ignore_mapping_value; - bool good_wd = false; if(wd_manager().available(wd)) { @@ -658,10 +653,7 @@ namespace nana wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); } else - { - wd_manager().map_requester(wd); wd->other.upd_state = update_state::none; - } good_wd = true; } diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 65e66701..9c96a7cd 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "inner_fwd_implement.hpp" #include @@ -555,6 +556,27 @@ namespace detail context.is_alt_pressed = false; } + class window_proc_guard + { + public: + window_proc_guard(detail::basic_window* wd) : + root_wd_(wd) + { + root_wd_->other.attribute.root->lazy_update = true; + } + + ~window_proc_guard() + { + if (!bedrock::instance().wd_manager().available(root_wd_)) + return; + + root_wd_->other.attribute.root->lazy_update = false; + root_wd_->other.attribute.root->update_requesters.clear(); + } + private: + detail::basic_window* const root_wd_; + }; + void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -569,10 +591,13 @@ namespace detail if(root_runtime) { - auto msgwnd = root_runtime->window; + auto const root_wd = root_runtime->window; + auto msgwnd = root_wd; + window_proc_guard wp_guard{ root_wd }; + auto& context = *brock.get_thread_context(msgwnd->thread_id); - auto pre_event_window = context.event_window; + auto const pre_event_window = context.event_window; auto pressed_wd = root_runtime->condition.pressed; auto pressed_wd_space = root_runtime->condition.pressed_by_space; auto hovered_wd = root_runtime->condition.hovered; @@ -1190,6 +1215,15 @@ namespace detail } } + if (wd_manager.available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) + { + for (auto wd : root_wd->other.attribute.root->update_requesters) + { + window_layout::paint(wd, window_layout::paint_operation::have_refreshed, false); + wd_manager.map(wd, true); + } + } + root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index bd02a00d..551cda69 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "inner_fwd_implement.hpp" @@ -37,6 +38,7 @@ #include "bedrock_types.hpp" + typedef void (CALLBACK *win_event_proc_t)(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); namespace nana @@ -738,6 +740,27 @@ namespace detail return static_cast(vkey); } + class window_proc_guard + { + public: + window_proc_guard(detail::basic_window* wd) : + root_wd_(wd) + { + root_wd_->other.attribute.root->lazy_update = true; + } + + ~window_proc_guard() + { + if (!bedrock::instance().wd_manager().available(root_wd_)) + return; + + root_wd_->other.attribute.root->lazy_update = false; + root_wd_->other.attribute.root->update_requesters.clear(); + } + private: + detail::basic_window* const root_wd_; + }; + LRESULT CALLBACK Bedrock_WIN32_WindowProc(HWND root_window, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT window_proc_value = 0; @@ -757,6 +780,7 @@ namespace detail bool def_window_proc = false; auto& context = *brock.get_thread_context(); + auto const pre_event_window = context.event_window; auto pressed_wd = root_runtime->condition.pressed; auto pressed_wd_space = root_runtime->condition.pressed_by_space; auto hovered_wd = root_runtime->condition.hovered; @@ -766,7 +790,10 @@ namespace detail pmdec.raw_param.wparam = wParam; internal_scope_guard lock; - auto msgwnd = root_runtime->window; + auto const root_wd = root_runtime->window; + auto msgwnd = root_wd; + + window_proc_guard wp_guard{ root_wd }; switch (message) { @@ -775,10 +802,7 @@ namespace detail { auto i = root_runtime->wpassoc->accel_commands.find(LOWORD(wParam)); if (i != root_runtime->wpassoc->accel_commands.end()) - { - auto fn = i->second; - fn(); - } + i->second(); } break; case WM_IME_STARTCOMPOSITION: @@ -1092,7 +1116,9 @@ namespace detail //The focus window receives the message in Windows system, it should be redirected to the hovered window ::POINT scr_pos{ pmdec.mouse.x, pmdec.mouse.y}; //Screen position auto pointer_wd = ::WindowFromPoint(scr_pos); - if (pointer_wd == root_window) + + //Ignore the message if the window is disabled. + if ((pointer_wd == root_window) && ::IsWindowEnabled(root_window)) { ::ScreenToClient(pointer_wd, &scr_pos); auto scrolled_wd = wd_manager.find_window(reinterpret_cast(pointer_wd), { scr_pos.x, scr_pos.y }); @@ -1124,7 +1150,7 @@ namespace detail wd_manager.do_lazy_refresh(scrolled_wd, false); } } - else + else if (pointer_wd != root_window) { DWORD pid = 0; ::GetWindowThreadProcessId(pointer_wd, &pid); @@ -1450,7 +1476,7 @@ namespace detail wd_manager.do_lazy_refresh(msgwnd, false); } } - return 0; + break; case WM_KEYUP: if(wParam != VK_MENU) //MUST NOT BE AN ALT { @@ -1551,13 +1577,28 @@ namespace detail def_window_proc = true; } + if (wd_manager.available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) + { + for (auto wd : root_wd->other.attribute.root->update_requesters) + { + window_layout::paint(wd, window_layout::paint_operation::have_refreshed, false); + wd_manager.map(wd, true); + } + } + root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { + context.event_window = pre_event_window; root_runtime->condition.pressed = pressed_wd; root_runtime->condition.hovered = hovered_wd; root_runtime->condition.pressed_by_space = pressed_wd_space; } + else + { + auto context = brock.get_thread_context(); + if(context) context->event_window = pre_event_window; + } if (!def_window_proc) return 0; diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 6349685d..0d7d0eb8 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -701,19 +701,40 @@ namespace detail return true; } - window_manager::core_window_t* window_manager::find_window(native_window_type root, const point& pos) + window_manager::core_window_t* window_manager::find_window(native_window_type root, const point& pos, bool ignore_captured) { if (nullptr == root) return nullptr; - if((false == attr_.capture.ignore_children) || (nullptr == attr_.capture.window) || (attr_.capture.window->root != root)) + //Thread-Safe Required! + std::lock_guard lock(mutex_); + + if (ignore_captured || (nullptr == attr_.capture.window)) { - //Thread-Safe Required! - std::lock_guard lock(mutex_); auto rrt = root_runtime(root); if (rrt && _m_effective(rrt->window, pos)) return _m_find(rrt->window, pos); + + return nullptr; } + + if (attr_.capture.ignore_children) + return attr_.capture.window; + + auto rrt = root_runtime(root); + if (rrt && _m_effective(rrt->window, pos)) + { + auto target = _m_find(rrt->window, pos); + + auto p = target; + while (p) + { + if (p == attr_.capture.window) + return target; + p = p->parent; + } + } + return attr_.capture.window; } @@ -980,28 +1001,7 @@ namespace detail //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) && !wd->is_draw_through()) - { - auto parent = wd->parent; - while (parent) - { - if(parent->flags.ignore_child_mapping || parent->flags.refreshing) - { - auto top = parent; - while(parent->parent) - { - parent = parent->parent; - if(parent->flags.ignore_child_mapping || parent->flags.refreshing) - top = parent; - } - - top->other.mapping_requester.push_back(wd); - return; - } - parent = parent->parent; - } - bedrock::instance().flush_surface(wd, forced, update_area); - } } //update @@ -1028,8 +1028,11 @@ namespace detail { if (!wd->flags.refreshing) { - window_layer::paint(wd, (redraw ? paint_operation::try_refresh : paint_operation::none), false); - this->map(wd, forced, update_area); + if (!wd->try_lazy_update(redraw)) + { + window_layer::paint(wd, (redraw ? paint_operation::try_refresh : paint_operation::none), false); + this->map(wd, forced, update_area); + } return true; } else if (forced) @@ -1076,39 +1079,28 @@ namespace detail { if ((wd->other.upd_state == core_window_t::update_state::refreshed) || (wd->other.upd_state == core_window_t::update_state::request_refresh) || force_copy_to_screen) { - window_layer::paint(wd, (wd->other.upd_state == core_window_t::update_state::request_refresh ? paint_operation::try_refresh : paint_operation::have_refreshed), refresh_tree); - this->map(wd, force_copy_to_screen); + if (!wd->try_lazy_update(wd->other.upd_state == core_window_t::update_state::request_refresh)) + { + window_layer::paint(wd, (wd->other.upd_state == core_window_t::update_state::request_refresh ? paint_operation::try_refresh : paint_operation::have_refreshed), refresh_tree); + this->map(wd, force_copy_to_screen); + } } else if (effects::edge_nimbus::none != wd->effect.edge_nimbus) { //The window is still mapped because of edge nimbus effect. //Avoid duplicate copy if action state is not changed and the window is not focused. if (wd->flags.action != wd->flags.action_before) - this->map(wd, true); + { + if (!wd->try_lazy_update(false)) + this->map(wd, true); + } } } else window_layer::paint(wd, paint_operation::try_refresh, refresh_tree); //only refreshing if it has an invisible parent } + wd->other.upd_state = core_window_t::update_state::none; - wd->other.mapping_requester.clear(); - } - - void window_manager::map_requester(core_window_t* wd) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - - if (false == impl_->wd_register.available(wd)) - return; - - if (wd->visible_parents()) - { - for(auto requestor : wd->other.mapping_requester) - this->map(requestor, true); - } - - wd->other.mapping_requester.clear(); } bool window_manager::set_parent(core_window_t* wd, core_window_t* newpa) diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 5b2db9e2..aff5b2a7 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1359,7 +1359,7 @@ namespace nana path.resize(len); impl_->path = to_utf8(path); - } + } #endif } @@ -1589,7 +1589,7 @@ namespace nana }; folderbox::folderbox(window owner, const path_type& init_path, std::string title) - : impl_(new implement{ owner, fs::canonical(init_path).make_preferred(), title, false}) + : impl_(new implement{ owner, fs::weakly_canonical(init_path).make_preferred(), title, false}) {} diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index 7263aa84..3c9d691b 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -1,7 +1,7 @@ /* * A Message Box Class * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -453,7 +453,20 @@ namespace nana default: break; } - auto bt = ::MessageBoxW(reinterpret_cast(API::root(wd_)), to_wstring(sstream_.str()).c_str(), to_wstring(title_).c_str(), type); + //Disables the owner window to prevent the owner window processing mouse wheel event + //when the message box is showing and scroll the wheel on the owner window. + auto native = reinterpret_cast(API::root(wd_)); + BOOL enabled = FALSE; + if (native) + { + enabled = ::IsWindowEnabled(native); + if (enabled) + ::EnableWindow(native, FALSE); + } + auto bt = ::MessageBoxW(native, to_wstring(sstream_.str()).c_str(), to_wstring(title_).c_str(), type); + + if (native && enabled) + ::EnableWindow(native, TRUE); switch(bt) { diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 9b278111..92625b8a 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1,7 +1,7 @@ /* * An Implementation of Place for Layout * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -630,7 +630,8 @@ namespace nana void collocate(); static division * search_div_name(division* start, const std::string&) noexcept; - std::unique_ptr scan_div(place_parts::tokenizer&); + + std::unique_ptr scan_div(place_parts::tokenizer&, bool implicitly_started, const std::string& ignore_duplicate = {}); void check_unique(const division*) const; //connect the field/dock with div object @@ -1987,7 +1988,7 @@ namespace nana std::string::size_type tag_pos{ left ? div.find('<', bound.second + 2) : div.rfind('>', bound.first - 2) }; if (div.npos == tag_pos) - throw std::runtime_error("place report an issue if it throws"); + throw std::invalid_argument("please report an issue if it throws"); auto other_bound = get_field_boundary(div, tag_pos); @@ -2706,7 +2707,9 @@ namespace nana throw std::invalid_argument("nana.place: the type of the " + std::string{pos_strs[pos]} +"th parameter for collapse should be integer."); } - auto place::implement::scan_div(place_parts::tokenizer& tknizer) -> std::unique_ptr + //implicitly_started indicates whether the field in div-text starts without < mark. + //ignore_duplicate A field is allowed to have same name if its has an ancestor which name is same with ignore_duplicate. + auto place::implement::scan_div(place_parts::tokenizer& tknizer, bool implicitly_started, const std::string& ignore_duplicate) -> std::unique_ptr { using token = place_parts::tokenizer::token ; @@ -2728,7 +2731,8 @@ namespace nana bool undisplayed = false; bool invisible = false; - for (token tk = tknizer.read(); (tk != token::eof && tk != token::div_end); tk = tknizer.read()) + token tk = token::eof; + for (tk = tknizer.read(); (tk != token::eof && tk != token::div_end); tk = tknizer.read()) { switch (tk) { @@ -2752,14 +2756,25 @@ namespace nana { auto splitter = new div_splitter(tknizer.number(), this); children.back()->div_next = splitter; + + //Hides the splitter if its left leaf is undisplayed. + if (!children.back()->display) + splitter->display = false; + children.emplace_back(std::unique_ptr{ splitter }); } break; case token::div_start: { - auto div = scan_div(tknizer); + auto div = scan_div(tknizer, false, ignore_duplicate); if (!children.empty()) + { + //Hides the splitter if its right leaf is undisplayed. + if ((children.back()->kind_of_division == division::kind::splitter) && !div->display) + children.back()->display = false; + children.back()->div_next = div.get(); + } children.emplace_back(std::move(div)); } @@ -2887,6 +2902,9 @@ namespace nana } } + if (implicitly_started && (tk == token::div_end)) + throw std::invalid_argument("nana.place: the div-text ends prematurely at " + std::to_string(tknizer.pos())); + field_gather * attached_field = nullptr; //find the field with specified name. @@ -2897,7 +2915,30 @@ namespace nana attached_field = i->second; //the field is attached to a division, it means there is another division with same name. if (attached_field->attached) - throw std::runtime_error("place, the name '" + name + "' is redefined."); + { + //The fields are allowed to have a same name. E.g. + //place.div("A "); + //place.modify("A", ""); Here the same name B must be allowed, otherwise it throws runtime error. + + bool allow_same_name = false; + if (!ignore_duplicate.empty()) + { + auto f = attached_field->attached->div_owner; + while (f) + { + if (f->name == ignore_duplicate) + { + allow_same_name = true; + break; + } + + f = f->div_owner; + } + } + + if (!allow_same_name) + throw std::runtime_error("place, the name '" + name + "' is redefined."); + } } token unmatch = token::width; @@ -2977,7 +3018,16 @@ namespace nana //attach the field to the division div->field = attached_field; if (attached_field) + { + //Replaces the previous div with the new div which is allowed to have a same name. + + //Detaches the field from the previous div. + if (attached_field->attached) + attached_field->attached->field = nullptr; + + //Attaches new div attached_field->attached = div.get(); + } if (children.size()) { @@ -3194,7 +3244,7 @@ namespace nana { place_parts::tokenizer tknizer(div_text.c_str()); impl_->disconnect(); - auto div = impl_->scan_div(tknizer); + auto div = impl_->scan_div(tknizer, true); try { impl_->connect(div.get()); //throws if there is a redefined name of field. @@ -3263,7 +3313,7 @@ namespace nana try { place_parts::tokenizer tknizer(div_text); - auto modified = impl_->scan_div(tknizer); + auto modified = impl_->scan_div(tknizer, true, name); auto modified_ptr = modified.get(); modified_ptr->name = name; diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 148667ff..85d4d4c5 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -1403,7 +1403,7 @@ namespace API ::nana::point clipos{pos}; interface_type::calc_window_point(wd, clipos); return reinterpret_cast( - restrict::wd_manager().find_window(wd, clipos)); + restrict::wd_manager().find_window(wd, clipos, true)); } return nullptr; } diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 619446a2..6e949def 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -1,7 +1,7 @@ /* * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -676,7 +676,10 @@ namespace nana { auto * editor = drawer_->editor(); editor->mouse_pressed(arg); - drawer_->open_lister_if_push_button_positioned(); + + //Pops up the droplist only if left button is clicked + if(arg.is_left_button()) + drawer_->open_lister_if_push_button_positioned(); drawer_->draw(); if(editor->attr().editable) diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index cf58e0f6..3cbe921d 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -1,6 +1,6 @@ /* * A Tabbar Implementation - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -1291,7 +1291,8 @@ namespace nana void trigger::mouse_down(graph_reference, const arg_mouse& arg) { - if(layouter_->press()) + //Activates the tab only if left button is clicked. + if(arg.is_left_button() && layouter_->press()) { if(false == layouter_->active_by_trace(arg)) layouter_->toolbox_answer(arg); @@ -1593,10 +1594,10 @@ namespace nana API::dev::lazy_refresh(); } - void driver::mouse_down(graph_reference graph, const arg_mouse&) + void driver::mouse_down(graph_reference graph, const arg_mouse& arg) { auto & indexes = model_->get_indexes(); - if ((indexes.hovered_pos == model_->npos) || (indexes.active_pos == indexes.hovered_pos)) + if ((indexes.hovered_pos == model_->npos) || (indexes.active_pos == indexes.hovered_pos) || !arg.is_left_button()) return; if (indexes.active_pos != indexes.hovered_pos) diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 5fb97c7b..eb859353 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -963,6 +963,9 @@ namespace nana if(text_r.right() > visible_w_pixels()) { node_state.tooltip = new tooltip_window(data.widget_ptr->handle(), text_r); + + //PR#406 Error Flynn's contribution + //fix: tooltip window doesn't have tree scheme & typeface API::dev::set_scheme(node_state.tooltip->handle(), API::dev::get_scheme(data.widget_ptr->handle())); node_state.tooltip->typeface(data.widget_ptr->typeface());