diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0dc030..7f89337e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ endif() if(WIN32) add_definitions(-DWIN32) + set(CMAKE_DEBUG_POSTFIX "_d") #Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. if(MSVC) option(MSVC_USE_MP "Set to ON to build nana with the /MP option (Visual Studio 2005 and above)." ON) @@ -100,7 +101,7 @@ endif(WIN32) if(APPLE) add_definitions(-DAPPLE) include_directories(/opt/X11/include/) - set(NANA_LINKS "${NANA_LINKS} -L/opt/X11/lib/ -liconv") + list(APPEND NANA_LINKS -L/opt/X11/lib/ -liconv) set(ENABLE_AUDIO OFF) elseif(UNIX) add_definitions(-Dlinux) @@ -108,11 +109,11 @@ elseif(UNIX) endif(APPLE) if(UNIX) - set(NANA_LINKS "${NANA_LINKS} -lX11") + list(APPEND NANA_LINKS -lX11) find_package(Freetype) if (FREETYPE_FOUND) include_directories( ${FREETYPE_INCLUDE_DIRS}) - set(NANA_LINKS "${NANA_LINKS} -lXft") + list(APPEND NANA_LINKS -lXft) endif(FREETYPE_FOUND) endif(UNIX) @@ -140,13 +141,13 @@ endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # enable static linkage # GNU || CLang not MinGW if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW # set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") - set(NANA_LINKS "${NANA_LINKS} -static-libgcc -static-libstdc++ -pthread") + list(APPEND NANA_LINKS -static-libgcc -static-libstdc++ -pthread) # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) # IS_GNUCXX < 5.3 else(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++fs") # IS_GNUCXX 5.3 or more - set(NANA_LINKS "${NANA_LINKS} -lstdc++fs") + list(APPEND NANA_LINKS -lstdc++fs) endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW @@ -154,7 +155,7 @@ endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang # set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libstdc++") - set(NANA_LINKS "${NANA_LINKS} -stdlib=libstdc++") + list(APPEND NANA_LINKS -stdlib=libstdc++) endif () @@ -163,11 +164,11 @@ endif () # Find PNG if(NANA_CMAKE_ENABLE_PNG) add_definitions(-DNANA_ENABLE_PNG) - set(NANA_LINKS "${NANA_LINKS} -lpng") if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if (PNG_FOUND) include_directories( ${PNG_INCLUDE_DIRS}) + list(APPEND NANA_LINKS ${PNG_LIBRARIES}) add_definitions(-DUSE_LIBPNG_FROM_OS) endif(PNG_FOUND) endif(NANA_CMAKE_LIBPNG_FROM_OS) @@ -176,11 +177,11 @@ endif(NANA_CMAKE_ENABLE_PNG) # Find JPEG if(NANA_CMAKE_ENABLE_JPEG) add_definitions(-DNANA_ENABLE_JPEG) - set(NANA_LINKS "${NANA_LINKS} -ljpeg") if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if (JPEG_FOUND) - include_directories( ${JPEG_INCLUDE_DIRS}) + include_directories( ${JPEG_INCLUDE_DIR}) + list(APPEND NANA_LINKS ${JPEG_LIBRARY}) add_definitions(-DUSE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) endif(NANA_CMAKE_LIBJPEG_FROM_OS) @@ -193,7 +194,7 @@ if(NANA_CMAKE_ENABLE_AUDIO) find_package(ASOUND) if (ASOUND_FOUND) include_directories( ${ASOUND_INCLUDE_DIRS}) - set(NANA_LINKS "${NANA_LINKS} -lasound") + list(APPEND NANA_LINKS -lasound) else(ASOUND_FOUND) message(FATAL_ERROR "libasound is not found") endif(ASOUND_FOUND) @@ -217,7 +218,7 @@ elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) if (Boost_FOUND) add_definitions(-DNANA_BOOST_FILESYSTEM_AVAILABLE) include_directories(SYSTEM "${Boost_INCLUDE_DIR}") - set(NANA_LINKS "${NANA_LINKS} ${Boost_LIBRARIES}") ###### FIRST !!!!!!!!!!!!!!!!! add is not first + list(APPEND NANA_LINKS ${Boost_LIBRARIES}) ###### FIRST !!!!!!!!!!!!!!!!! add is not first endif (Boost_FOUND) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) # ?? diff --git a/build/makefile/makefile b/build/makefile/makefile index 9f0fb052..0cdc8d1d 100644 --- a/build/makefile/makefile +++ b/build/makefile/makefile @@ -8,7 +8,6 @@ SRCROOT = ../../source EXTRLIB = ../../extrlib NANA_INC= $(INCROOT)/nana OUTROOT = out -#CXXFLAGS= -g -std=c++11 -Wall CXXFLAGS= -g -fexceptions -std=c++11 -Wall -Wextra -Wunused-variable -Wfatal-errors INCS = -I$(INCROOT) -I/usr/include/freetype2 -I$(EXTRLIB) diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index 6ee38270..b1e450b8 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -23,7 +23,7 @@ {42D0520F-EFA5-4831-84FE-2B9085301C5D} Win32Proj nana - 10.0.14393.0 + 10.0.15063.0 @@ -82,6 +82,18 @@ ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + + ../bin/ + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\include;$(IncludePath) + + + ../bin/ + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\include;$(IncludePath) + diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index 551b45bb..a9381f23 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -62,7 +62,7 @@ # else # undef STD_FILESYSTEM_NOT_SUPPORTED # endif -#elif defined(__GNUC__) +#elif defined(__GNUC__) && not defined(__clang__) # if (__GNUC__ == 4 && __GNUC_MINOR__ < 6) # define noexcept //no support of noexcept until GCC 4.6 # endif diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 2230a422..c686f889 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -52,7 +52,7 @@ namespace detail void position(const point& pos) override; nana::point position() const override; size dimension() const override; - void dimension(const size& s); + void dimension(const size& s) override; void visible(bool visibility) override; bool visible() const override; private: diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 410e5118..6215d276 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -186,7 +186,7 @@ namespace nana public: virtual ~container_interface() = default; - virtual void clear() noexcept = 0; + virtual void clear() = 0; virtual void erase(std::size_t pos) = 0; virtual std::size_t size() const = 0; @@ -238,7 +238,7 @@ namespace nana translator_({ vtrans, ctrans }) {} private: - void clear() noexcept override + void clear() override { container_.clear(); } @@ -246,7 +246,7 @@ namespace nana void erase(std::size_t pos) override { auto i = container_.begin(); - std::advance(i, static_cast(pos)); + std::advance(i, pos); container_.erase(i); } @@ -263,7 +263,7 @@ namespace nana void emplace(std::size_t pos) override { auto i = container_.begin(); - std::advance(i, static_cast(pos)); + std::advance(i, pos); container_.emplace(i); } @@ -327,7 +327,7 @@ namespace nana } private: - void clear() noexcept override + void clear() override { container_.clear(); } @@ -335,7 +335,7 @@ namespace nana void erase(std::size_t pos) override { auto i = container_.begin(); - std::advance(i, static_cast(pos)); + std::advance(i, pos); container_.erase(i); } @@ -352,7 +352,7 @@ namespace nana void emplace(std::size_t pos) override { auto i = container_.begin(); - std::advance(i, static_cast(pos)); + std::advance(i, pos); container_.emplace(i); } @@ -419,7 +419,7 @@ namespace nana throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); } - void erase(std::size_t pos) override + void erase(std::size_t /*pos*/) override { throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); } @@ -434,7 +434,7 @@ namespace nana return true; } - void emplace(std::size_t pos) override + void emplace(std::size_t /*pos*/) override { throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); } @@ -444,7 +444,7 @@ namespace nana throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); } - void assign(std::size_t pos, const std::vector& cells) override + void assign(std::size_t /*pos*/, const std::vector& /*cells*/) override { throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); } @@ -454,7 +454,7 @@ namespace nana return ctrans_(container_.at(pos)); } - bool push_back(const const_virtual_pointer& dptr) override + bool push_back(const const_virtual_pointer& /*dptr*/) override { throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); } @@ -893,7 +893,7 @@ namespace nana } template - T* value_ptr() const + T const * value_ptr() const { return any_cast(_m_value()); } @@ -1395,6 +1395,11 @@ the nana::detail::basic_window member pointer scheme size_type append_header(std::string text_utf8, unsigned width = 120); size_type append_header(std::wstring text, unsigned width = 120); + cat_proxy append(std::string category); ///< Appends a new category to the end + cat_proxy append(std::wstring category); ///< Appends a new category to the end + void append(std::initializer_list categories); ///< Appends categories to the end + void append(std::initializer_list categories); ///< Appends categories to the end + /// Access a column at specified position /** * @param pos Position of column @@ -1416,10 +1421,8 @@ the nana::detail::basic_window member pointer scheme /// Returns the number of columns size_type column_size() const; - cat_proxy append(std::string category); ///< Appends a new category to the end - cat_proxy append(std::wstring category); ///< Appends a new category to the end - void append(std::initializer_list categories); ///< Appends categories to the end - void append(std::initializer_list categories); ///< Appends categories to the end + /// Returns a rectangle in where the content is drawn. + rectangle content_area() const; cat_proxy insert(cat_proxy, ::std::string); cat_proxy insert(cat_proxy, ::std::wstring); diff --git a/include/nana/gui/widgets/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index b636a125..a76b21bb 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -50,7 +50,6 @@ namespace nana virtual void caption(const point&, const native_string_type&); scheme *scheme_ptr() const { return scheme_ptr_; }; private: - window handle_; graph_reference graph_; scheme *scheme_ptr_; }; diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 896027c0..cf8ee302 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -85,6 +85,8 @@ namespace nana{ namespace widgets text_editor(window, graph_reference, const text_editor_scheme*); ~text_editor(); + size caret_size() const; + void set_highlight(const ::std::string& name, const ::nana::color&, const ::nana::color&); void erase_highlight(const ::std::string& name); void set_keyword(const ::std::wstring& kw, const std::string& name, bool case_sensitive, bool whole_word_matched); @@ -181,6 +183,8 @@ namespace nana{ namespace widgets void focus_behavior(text_focus_behavior); void select_behavior(bool move_to_end); + + std::size_t line_count(bool text_lines) const; public: void draw_corner(); void render(bool focused); @@ -217,8 +221,10 @@ namespace nana{ namespace widgets std::vector _m_render_text(const ::nana::color& text_color); void _m_pre_calc_lines(std::size_t line_off, std::size_t lines); - ::nana::point _m_caret_to_screen(::nana::upoint pos) const; - ::nana::upoint _m_screen_to_caret(::nana::point pos) const; + //Caret to screen coordinate or context coordiate(in pixels) + ::nana::point _m_caret_to_coordinate(::nana::upoint pos, bool to_screen_coordinate = true) const; + //Screen coordinate or context coordinate(in pixels) to caret, + ::nana::upoint _m_coordinate_to_caret(::nana::point pos, bool from_screen_coordinate = true) const; bool _m_pos_from_secondary(std::size_t textline, const nana::upoint& secondary, unsigned & pos); bool _m_pos_secondary(const nana::upoint& charpos, nana::upoint& secondary_pos) const; @@ -240,11 +246,11 @@ namespace nana{ namespace widgets static bool _m_resolve_text(const ::std::wstring&, std::vector> & lines); bool _m_cancel_select(int align); - unsigned _m_tabs_pixels(size_type tabs) const; nana::size _m_text_extent_size(const char_type*, size_type n) const; - /// Moves the view of window. - bool _m_move_offset_x_while_over_border(int many); + /// Adjust position of view to make caret stay in screen + bool _m_adjust_view(); + bool _m_move_select(bool record_undo); int _m_text_top_base() const; @@ -316,7 +322,6 @@ namespace nana{ namespace widgets { nana::upoint caret; //position of caret by text, it specifies the position of a new character nana::upoint shift_begin_caret; - unsigned xpos{0}; //This data is used for move up/down }points_; }; }//end namespace skeletons diff --git a/include/nana/gui/widgets/skeletons/textbase.hpp b/include/nana/gui/widgets/skeletons/textbase.hpp index 55828351..751d9888 100644 --- a/include/nana/gui/widgets/skeletons/textbase.hpp +++ b/include/nana/gui/widgets/skeletons/textbase.hpp @@ -1,7 +1,7 @@ /* * A textbase class 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 @@ -44,7 +44,7 @@ namespace skeletons { attr_max_.reset(); //Insert an empty string for the first line of empty text. - text_cont_.emplace_back(); + text_cont_.emplace_back(new string_type); } void set_event_agent(textbase_event_agent_interface * evt) @@ -55,7 +55,7 @@ namespace skeletons bool empty() const { return (text_cont_.empty() || - ((text_cont_.size() == 1) && (text_cont_[0].empty()))); + ((text_cont_.size() == 1) && (text_cont_.front()->empty()))); } bool load(const char* file_utf8) @@ -135,10 +135,10 @@ namespace skeletons while(ifs.good()) { std::getline(ifs, str_mbs); - text_cont_.emplace_back(static_cast(nana::charset{ str_mbs })); - if(text_cont_.back().size() > attr_max_.size) + text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str_mbs }))); + if(text_cont_.back()->size() > attr_max_.size) { - attr_max_.size = text_cont_.back().size(); + attr_max_.size = text_cont_.back()->size(); attr_max_.line = text_cont_.size() - 1; } } @@ -218,9 +218,9 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); + text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str, encoding }))); - attr_max_.size = text_cont_.back().size(); + attr_max_.size = text_cont_.back()->size(); attr_max_.line = 0; } @@ -236,10 +236,10 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); - if(text_cont_.back().size() > attr_max_.size) + text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str, encoding }))); + if(text_cont_.back()->size() > attr_max_.size) { - attr_max_.size = text_cont_.back().size(); + attr_max_.size = text_cont_.back()->size(); attr_max_.line = text_cont_.size() - 1; } } @@ -253,6 +253,9 @@ namespace skeletons std::ofstream ofs(to_osmbstr(fs), std::ios::binary); if(ofs && text_cont_.size()) { + auto i = text_cont_.cbegin(); + auto const count = text_cont_.size() - 1; + std::string last_mbs; if (is_unicode) @@ -272,32 +275,27 @@ namespace skeletons if (bytes) ofs.write(le_boms[static_cast(encoding)], bytes); - if (text_cont_.size() > 1) + for (std::size_t pos = 0; pos < count; ++pos) { - std::string mbs; - for (auto i = text_cont_.cbegin(), end = text_cont_.cend() - 1; i != end; ++i) - { - std::string(nana::charset(*i).to_bytes(encoding)).swap(mbs); - mbs += "\r\n"; - ofs.write(mbs.c_str(), static_cast(mbs.size())); - } + auto mbs = nana::charset(**(i++)).to_bytes(encoding); + ofs.write(mbs.c_str(), static_cast(mbs.size())); + ofs.write("\r\n", 2); } - last_mbs = nana::charset(text_cont_.back()).to_bytes(encoding); + last_mbs = nana::charset(*text_cont_.back()).to_bytes(encoding); } else { - if (text_cont_.size() > 1) + for (std::size_t pos = 0; pos < count; ++pos) { - for (auto i = text_cont_.cbegin(), end = text_cont_.cend() - 1; i != end; ++i) - { - std::string mbs = nana::charset(*i); - ofs.write(mbs.c_str(), mbs.size()); - ofs.write("\r\n", 2); - } + std::string mbs = nana::charset(**(i++)); + ofs.write(mbs.c_str(), mbs.size()); + ofs.write("\r\n", 2); } - last_mbs = nana::charset(text_cont_.back()); + + last_mbs = nana::charset(*text_cont_.back()); } + ofs.write(last_mbs.c_str(), static_cast(last_mbs.size())); _m_saved(std::move(fs)); } @@ -310,8 +308,8 @@ namespace skeletons const string_type& getline(size_type pos) const { - if(pos < text_cont_.size()) - return text_cont_[pos]; + if (pos < text_cont_.size()) + return *text_cont_[pos]; return nullstr_; } @@ -323,13 +321,13 @@ namespace skeletons public: void replace(size_type pos, string_type && text) { - if(text_cont_.size() <= pos) + if (text_cont_.size() <= pos) { - text_cont_.emplace_back(std::move(text)); + text_cont_.emplace_back(new string_type(std::move(text))); pos = text_cont_.size() - 1; } else - text_cont_[pos].swap(text); + _m_at(pos).swap(text); _m_make_max(pos); _m_edited(); @@ -339,7 +337,7 @@ namespace skeletons { if(pos.y < text_cont_.size()) { - string_type& lnstr = text_cont_[pos.y]; + string_type& lnstr = _m_at(pos.y); if(pos.x < lnstr.size()) lnstr.insert(pos.x, str); @@ -348,7 +346,7 @@ namespace skeletons } else { - text_cont_.emplace_back(std::move(str)); + text_cont_.emplace_back(new string_type(std::move(str))); pos.y = static_cast(text_cont_.size() - 1); } @@ -358,10 +356,10 @@ namespace skeletons void insertln(size_type pos, string_type&& str) { - if(pos < text_cont_.size()) - text_cont_.emplace(text_cont_.begin() + pos, std::move(str)); + if (pos < text_cont_.size()) + text_cont_.emplace(_m_iat(pos), new string_type(std::move(str))); else - text_cont_.emplace_back(std::move(str)); + text_cont_.emplace_back(new string_type(std::move(str))); _m_make_max(pos); _m_edited(); @@ -371,7 +369,7 @@ namespace skeletons { if (line < text_cont_.size()) { - string_type& lnstr = text_cont_[line]; + string_type& lnstr = _m_at(line); if ((pos == 0) && (count >= lnstr.size())) lnstr.clear(); else @@ -393,7 +391,7 @@ namespace skeletons if (pos + n > text_cont_.size()) n = text_cont_.size() - pos; - text_cont_.erase(text_cont_.begin() + pos, text_cont_.begin() + (pos + n)); + text_cont_.erase(_m_iat(pos), _m_iat(pos + n)); if (pos <= attr_max_.line && attr_max_.line < pos + n) _m_scan_for_max(); @@ -408,7 +406,7 @@ namespace skeletons { text_cont_.clear(); attr_max_.reset(); - text_cont_.emplace_back(); //text_cont_ must not be empty + text_cont_.emplace_back(new string_type); //text_cont_ must not be empty _m_saved(std::string()); } @@ -417,9 +415,14 @@ namespace skeletons { if(pos + 1 < text_cont_.size()) { - text_cont_[pos] += text_cont_[pos + 1]; - text_cont_.erase(text_cont_.begin() + (pos + 1)); + auto i = _m_iat(pos + 1); + _m_at(pos) += **i; + text_cont_.erase(i); _m_make_max(pos); + + //If the maxline is behind the pos line, + //decrease the maxline. Because a line between maxline and pos line + //has been deleted. if(pos < attr_max_.line) --attr_max_.line; @@ -458,9 +461,19 @@ namespace skeletons return edited() || filename_.empty(); } private: + string_type& _m_at(size_type pos) + { + return **_m_iat(pos); + } + + typename std::deque>::iterator _m_iat(size_type pos) + { + return text_cont_.begin() + pos; + } + void _m_make_max(std::size_t pos) { - const string_type& str = text_cont_[pos]; + const string_type& str = _m_at(pos); if(str.size() > attr_max_.size) { attr_max_.size = str.size(); @@ -472,11 +485,11 @@ namespace skeletons { attr_max_.size = 0; std::size_t n = 0; - for(auto & s : text_cont_) + for(auto & p : text_cont_) { - if(s.size() > attr_max_.size) + if(p->size() > attr_max_.size) { - attr_max_.size = s.size(); + attr_max_.size = p->size(); attr_max_.line = n; } ++n; @@ -514,7 +527,7 @@ namespace skeletons evt_agent_->text_changed(); } private: - std::deque text_cont_; + std::deque> text_cont_; textbase_event_agent_interface* evt_agent_{ nullptr }; mutable bool changed_{ false }; diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index f22e6e5c..0e2e2e9c 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -237,9 +237,9 @@ namespace nana return this->get_drawer_trigger().activated(); } - value_type & at(std::size_t pos) const /// Returns pos'th element + value_type const & at(std::size_t pos) const /// Returns pos'th element { - return static_cast(this->get_drawer_trigger().at(pos)); + return any_cast(this->get_drawer_trigger().at(pos)); } void close_fly(bool fly) /// Draw or not a close button in each tab. diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index e37ed6fc..591b4e7e 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -257,6 +257,17 @@ namespace nana * @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); + + /// Returns the number of lines that text are displayed in the screen. + /** + * The number of display lines may be not equal to the number of text lines when the textbox + * is line wrapped mode. + * @return the number of lines that text are displayed in the screen. + */ + std::size_t display_line_count() const noexcept; + + /// Returns the number of text lines. + std::size_t text_line_count() const noexcept; protected: //Overrides widget's virtual functions native_string_type _m_caption() const throw() override; diff --git a/include/nana/unicode_bidi.hpp b/include/nana/unicode_bidi.hpp index 71a5f882..72d65860 100644 --- a/include/nana/unicode_bidi.hpp +++ b/include/nana/unicode_bidi.hpp @@ -49,7 +49,7 @@ namespace nana unsigned level; }; - void linestr(const char_type*, std::size_t len, std::vector & reordered); + std::vector reorder(const char_type*, std::size_t len); private: static unsigned _m_paragraph_level(const char_type * begin, const char_type * end); @@ -65,13 +65,12 @@ namespace nana void _m_reordering_resolved_levels(std::vector & reordered); static bidi_category _m_bidi_category(bidi_char); static bidi_char _m_char_dir(char_type); - private: - void _m_output_levels() const; - void _m_output_bidi_char() const; private: std::vector levels_; }; + std::vector unicode_reorder(const wchar_t* text, std::size_t length); + } #include diff --git a/source/audio/detail/buffer_preparation.cpp b/source/audio/detail/buffer_preparation.cpp index acd0aaa3..82942846 100644 --- a/source/audio/detail/buffer_preparation.cpp +++ b/source/audio/detail/buffer_preparation.cpp @@ -33,7 +33,7 @@ namespace nana{ namespace audio prepared_.emplace_back(m); } - thr_ = std::move(std::thread([this](){this->_m_prepare_routine();})); + thr_ = std::thread{[this](){this->_m_prepare_routine();}}; } buffer_preparation::~buffer_preparation() diff --git a/source/charset.cpp b/source/charset.cpp index 5c802b15..454b5ea3 100644 --- a/source/charset.cpp +++ b/source/charset.cpp @@ -484,7 +484,7 @@ namespace nana virtual std::string&& str_move() { if(is_unicode_) - data_ = std::move(str()); + data_ = str(); return std::move(data_); } @@ -506,6 +506,8 @@ namespace nana std::u32string u32str = std::wstring_convert, char32_t>().from_bytes(data_); return std::string(reinterpret_cast(u32str.c_str()), u32str.size() * sizeof(char32_t)); } + default: + break; //no conversion } break; case unicode::utf16: @@ -520,6 +522,8 @@ namespace nana std::u32string u32str = std::wstring_convert, char32_t>().from_bytes(data_); return std::string(reinterpret_cast(u32str.c_str()), u32str.size() * sizeof(char32_t)); } + default: + break; //no conversion } break; case unicode::utf32: @@ -533,6 +537,8 @@ namespace nana return std::wstring_convert, char32_t>().to_bytes( std::u32string(reinterpret_cast(data_.c_str()), data_.size() / sizeof(char32_t)) ); + default: + break; //no conversion } break; } @@ -588,7 +594,7 @@ namespace nana virtual std::wstring && wstr_move() { - wdata_for_move_ = std::move(wstr()); + wdata_for_move_ = wstr(); return std::move(wdata_for_move_); } private: diff --git a/source/detail/mswin/platform_spec.hpp b/source/detail/mswin/platform_spec.hpp index 6de8c4e2..ed4b0d9a 100644 --- a/source/detail/mswin/platform_spec.hpp +++ b/source/detail/mswin/platform_spec.hpp @@ -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 @@ -144,7 +144,6 @@ namespace detail drawable_impl_type(); ~drawable_impl_type(); - void fgcolor(const ::nana::color&); //deprecated unsigned get_color() const; unsigned get_text_color() const; void set_color(const ::nana::color&); diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 89ea7b8c..e495cf7d 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -423,35 +423,6 @@ namespace detail } } - void drawable_impl_type::fgcolor(const ::nana::color& clr) - { - auto rgb = clr.px_color().value; - - if (rgb != current_color_) - { - auto & spec = nana::detail::platform_spec::instance(); - platform_scope_guard psg; - - current_color_ = rgb; - switch(spec.screen_depth()) - { - case 16: - rgb = ((((rgb >> 16) & 0xFF) * 31 / 255) << 11) | - ((((rgb >> 8) & 0xFF) * 63 / 255) << 5) | - (rgb & 0xFF) * 31 / 255; - break; - } - ::XSetForeground(spec.open_display(), context, rgb); - ::XSetBackground(spec.open_display(), context, rgb); -#if defined(NANA_USE_XFT) - xft_fgcolor.color.red = ((0xFF0000 & rgb) >> 16) * 0x101; - xft_fgcolor.color.green = ((0xFF00 & rgb) >> 8) * 0x101; - xft_fgcolor.color.blue = (0xFF & rgb) * 0x101; - xft_fgcolor.color.alpha = 0xFFFF; -#endif - } - } - platform_scope_guard::platform_scope_guard() { platform_spec::instance().lock_xlib(); diff --git a/source/detail/platform_spec_windows.cpp b/source/detail/platform_spec_windows.cpp index 3e4e044b..eb58c603 100644 --- a/source/detail/platform_spec_windows.cpp +++ b/source/detail/platform_spec_windows.cpp @@ -221,11 +221,6 @@ namespace detail ::DeleteObject(round_region.handle); } - void drawable_impl_type::fgcolor(const ::nana::color& clr) - { - set_text_color(clr); - } - unsigned drawable_impl_type::get_color() const { return color_; diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index f1f32448..2ada1eac 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -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 @@ -101,7 +101,6 @@ namespace detail drawable_impl_type(); ~drawable_impl_type(); - void fgcolor(const ::nana::color&); //deprecated unsigned get_color() const; unsigned get_text_color() const; void set_color(const ::nana::color&); diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index a6047b4e..c737d334 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -454,7 +454,7 @@ namespace nana thr->performance_parameter = 0.0; thr->fps = p->fps; thr->interval = 1000.0 / double(p->fps); - thr->thread = std::make_shared([this, thr]() + thr->thread = std::make_shared([thr]() { nana::system::timepiece tmpiece; while (true) diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index ca36a92f..294c56f9 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -131,7 +131,7 @@ namespace nana if (((0 == thread_id) || (wd->thread_id == thread_id)) && (wd->root != root)) { root = wd->root; - if (roots.cend() == std::find(roots.cbegin(), roots.cend(), root)) + if (roots.end() == std::find(roots.begin(), roots.end(), root)) roots.emplace_back(root); } } diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 7a8980e8..6d1ce3ca 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1397,14 +1397,14 @@ namespace detail { auto & tabs = wd->root_widget->other.attribute.root->tabstop; - auto end = tabs.cend(); + auto end = tabs.end(); if (forward) { if (detail::tab_type::none == wd->flags.tab) return (tabs.front()); else if (detail::tab_type::tabstop & wd->flags.tab) { - auto i = std::find(tabs.cbegin(), end, wd); + auto i = std::find(tabs.begin(), end, wd); if (i != end) { ++i; @@ -1417,9 +1417,9 @@ namespace detail } else if (tabs.size() > 1) //at least 2 elments in tabs are required when moving backward. { - auto i = std::find(tabs.cbegin(), end, wd); + auto i = std::find(tabs.begin(), end, wd); if (i != end) - return (tabs.cbegin() == i ? tabs.back() : *(i - 1)); + return (tabs.begin() == i ? tabs.back() : *(i - 1)); } return nullptr; } @@ -1549,7 +1549,7 @@ namespace detail for (auto child : wd->children) { auto child_keys = shortkeys(child, true); - std::copy(child_keys.cbegin(), child_keys.cend(), std::back_inserter(result)); + std::copy(child_keys.begin(), child_keys.end(), std::back_inserter(result)); } } } @@ -1716,7 +1716,7 @@ namespace detail if (pa_children.size() > 1) { - for (auto i = pa_children.cbegin(), end = pa_children.cend(); i != end; ++i) + for (auto i = pa_children.begin(), end = pa_children.end(); i != end; ++i) { if (((*i)->index) > (wd->index)) { diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index ae90915d..c3fa7fb0 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -270,7 +270,7 @@ namespace nana return (reverse ? a > b : a < b); }); - ls_file_.set_sort_compare(3, [this](const std::string&, nana::any* anyptr_a, const std::string&, nana::any* anyptr_b, bool reverse) -> bool + ls_file_.set_sort_compare(3, [](const std::string&, nana::any* anyptr_a, const std::string&, nana::any* anyptr_b, bool reverse) -> bool { item_fs * fsa = any_cast(anyptr_a); item_fs * fsb = any_cast(anyptr_b); diff --git a/source/gui/notifier.cpp b/source/gui/notifier.cpp index 545b3a47..e3ec1b66 100644 --- a/source/gui/notifier.cpp +++ b/source/gui/notifier.cpp @@ -265,6 +265,9 @@ namespace nana auto ico = impl_->icons[impl_->play_index++]; impl_->set_icon(ico); +#else + //eliminates warnings in clang + static_cast(this); #endif }); diff --git a/source/gui/place.cpp b/source/gui/place.cpp index c0a4f972..f1b4a5ff 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -3055,7 +3055,7 @@ namespace nana void place::bind(window wd) { if (impl_->window_handle) - throw std::runtime_error("place.bind: it has already binded to a window."); + 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) diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 0b4f4e23..3dde6ca9 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -68,13 +68,13 @@ namespace nana API::dev::lazy_refresh(); } - void mouse_down(graph_reference graph, const arg_mouse&) + void mouse_down(graph_reference graph, const arg_mouse&) override { refresh(graph); API::dev::lazy_refresh(); } - void mouse_up(graph_reference graph, const arg_mouse&) + void mouse_up(graph_reference graph, const arg_mouse&) override { refresh(graph); API::dev::lazy_refresh(); diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index a4a8cef1..1d436f68 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -519,9 +519,7 @@ namespace nana void _m_draw_block(graph_reference graph, const std::wstring& s, dstream::linecontainer::iterator block_start, render_status& rs) { - nana::unicode_bidi bidi; - std::vector reordered; - bidi.linestr(s.data(), s.length(), reordered); + auto const reordered = unicode_reorder(s.data(), s.length()); pixel_tag px = rs.pixels[rs.index]; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 1a3325ec..078cd6c8 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -195,7 +195,7 @@ namespace nana _m_refresh(); } - void width(unsigned minimum, unsigned maximum) + void width(unsigned minimum, unsigned maximum) override { //maximum must be larger than minimum, but maximum == 0 is allowed if minimum is 0 if ((minimum >= maximum) && (minimum != 0)) @@ -332,7 +332,8 @@ namespace nana unsigned ranged_px = 0; unsigned ranged_count = 0; - for (auto & col : cont_) + auto const & const_cont = cont_; + for (auto & col : const_cont) { if (col.visible_state) { @@ -465,23 +466,25 @@ namespace nana return npos; } - /// Returns the left point position and width(in variable *pixels) of column originaly at position pos. - int position(size_type pos, unsigned * pixels) const + unsigned margin() const { - int left = 0; + return margin_; + } + + std::pair range(size_type pos) const + { + int left = static_cast(margin_); + for (auto & m : cont_) { if (m.index == pos) - { - if (pixels) - *pixels = m.width_px; - break; - } + return{left, m.width_px}; if (m.visible_state) - left += m.width_px; + left += static_cast(m.width_px); } - return left; + + return{ left, 0 }; } /// return the original index of the visible col currently before(in front of) or after the col originaly at index "index" @@ -525,11 +528,15 @@ namespace nana if ((from == to) || (from >= cont_.size()) || (to >= cont_.size())) return; +#ifdef _MSC_VER + for (auto i = cont_.cbegin(); i != cont_.cend(); ++i) +#else for (auto i = cont_.begin(); i != cont_.end(); ++i) +#endif { if (from == i->index) { - auto col_from = std::move(*i); + auto col_from = *i; cont_.erase(i); //A workaround for old libstdc++, that some operations of vector @@ -553,6 +560,7 @@ namespace nana private: bool visible_{true}; bool sortable_{true}; + unsigned margin_{ 5 }; container cont_; }; @@ -757,19 +765,6 @@ namespace nana std::string to_string(const export_options& exp_opt) const; - 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)) - { - panes.emplace_back(p); - } - } - return panes; - } - void emit_cs(const index_pair& pos, bool for_selection) { item_proxy i(ess_, pos); @@ -782,13 +777,16 @@ namespace nana else events.checked.emit(arg, wd_ptr()->handle()); - auto panes = get_inline_pane(pos); - for (auto p : panes) + //notify the inline pane. An item may have multiple panes, each pane is for a column. + for (auto p : active_panes_) { - if(for_selection) - p->inline_ptr->notify_status(inline_widget_status::selecting, i.selected()); - else - p->inline_ptr->notify_status(inline_widget_status::checking, i.checked()); + if (p && (p->item_pos == pos)) + { + if (for_selection) + p->inline_ptr->notify_status(inline_widget_status::selecting, i.selected()); + else + p->inline_ptr->notify_status(inline_widget_status::checking, i.checked()); + } } } @@ -1509,11 +1507,19 @@ namespace nana } template - std::vector> select_display_range_if(index_pair fr_abs, index_pair to_dpl, bool unselect_others, Pred pred) + std::vector> select_display_range_if(const index_pair& fr_abs, index_pair to_dpl, bool deselect_others, Pred pred) { - const auto already_selected = this->pick_items(true); + const index_pairs already_selected = (deselect_others ? this->pick_items(true) : index_pairs{}); - auto fr_dpl = this->index_cast(fr_abs, false); //Converts an absolute position to display position + if (to_dpl.empty()) + { + if (fr_abs.empty()) + return{}; + + to_dpl = this->last(); + } + + auto fr_dpl = (fr_abs.is_category() ? fr_abs : this->index_cast(fr_abs, false)); //Converts an absolute position to display position if (fr_dpl > to_dpl) std::swap(fr_dpl, to_dpl); @@ -1524,9 +1530,28 @@ namespace nana //pair second: indicates whether the index is selected before selection. std::vector> pairs; - for (; fr_dpl != to_dpl; fr_dpl = advance(fr_dpl, 1)) + for (; !(fr_dpl > to_dpl); fr_dpl = advance(fr_dpl, 1)) //fr_dpl <= to_dpl { - if (!fr_dpl.is_category()) + if (fr_dpl.is_category()) + { + if (!expand(fr_dpl.cat)) + { + auto size = size_item(fr_dpl.cat); + for (std::size_t i = 0; i < size; ++i) + { + index_pair abs_pos{ fr_dpl.cat, i }; + item_proxy m{ ess_, abs_pos }; + pairs.emplace_back(abs_pos, m.selected()); + + if (pred(abs_pos)) + m.select(true); + } + + if (fr_dpl.cat == to_dpl.cat) + break; + } + } + else { auto abs_pos = index_cast(fr_dpl, true); //convert display position to absolute position item_proxy m{ ess_, abs_pos }; @@ -1537,20 +1562,9 @@ namespace nana } } - if (!to_dpl.is_category()) + if (deselect_others) { - auto abs_pos = index_cast(to_dpl, true); //convert display position to absolute position - - item_proxy m(ess_, abs_pos); - pairs.emplace_back(abs_pos, m.selected()); - - if (pred(abs_pos)) - m.select(true); - } - - if (unselect_others) - { - //Unselects the already selected which is out of range [begin, last] + //Deselects the already selected which is out of range [begin, last] for (auto index : already_selected) { auto disp_order = this->index_cast(index, false); //converts an absolute position to a display position @@ -1581,9 +1595,9 @@ namespace nana this->emit_cs(pos, true); if (m.flags.selected) - last_selected_abs = pos; - else if (last_selected_abs == pos) - last_selected_abs.set_both(npos); //make empty + latest_selected_abs = pos; + else if (latest_selected_abs == pos) + latest_selected_abs.set_both(npos); //make empty } } ++pos.item; @@ -1822,8 +1836,13 @@ namespace nana else i.item = npos; } - else if (i.item) - --i.item; + else + { + if (i.item) + --i.item; + else + return index_pair{ npos, npos }; + } return i; } @@ -1831,9 +1850,14 @@ namespace nana /// can be used as the absolute position of the first absolute item, or as the display pos of the first displayed item index_pair first() const noexcept { - index_pair fst{0,npos}; - good_item(fst,fst); - return fst; + auto i = categories_.cbegin(); + if (i->items.size()) + return index_pair{ 0, 0 }; + + if (categories_.size() > 1) + return index_pair{ 1, npos }; + + return index_pair{ npos, npos }; } bool good(size_type cat) const noexcept @@ -1846,36 +1870,6 @@ namespace nana return ((pos.cat < categories_.size()) && (pos.item < size_item(pos.cat))); } - /// if good return the same item (in arg item), or just the next cat and true, but If fail return false - bool good_item(const index_pair& pos, index_pair& item) const noexcept - { - if (!good(pos.cat)) - return false; // cat out of range - - if (pos.is_category()) - { - item = pos; // return the cat self - if (0 == pos.cat) // but for cat 0 return first item - item.item = 0; // let check this is good - else - return true; - } - - auto i = get(pos.cat); // pos is not a cat and i point to it cat - if (pos.item < i->items.size()) - { - item = pos; // good item, return it - return true; - } - - if (++i == categories_.cend()) // item out of range and no more cat - return false; - - item.cat = pos.cat + 1; // select the next cat - item.item = npos; - return true; - } - /// categories iterator container::iterator get(size_type pos) { @@ -1895,7 +1889,7 @@ namespace nana return i; } public: - index_pair last_selected_abs; + index_pair latest_selected_abs; //Stands for the latest selected item that selected by last operation. Invalid if it is empty. private: essence * ess_{nullptr}; nana::listbox * widget_{nullptr}; @@ -1927,6 +1921,7 @@ namespace nana bool auto_draw{true}; bool checkable{false}; bool if_image{false}; + bool deselect_deferred{ false }; //deselects items when mouse button is released. unsigned text_height; ::nana::listbox::export_options def_exp_options; @@ -2051,16 +2046,19 @@ namespace nana return{ r.x - origin.x, r.x - origin.x + static_cast(header.pixels()) }; } - void start_mouse_selection(const point& screen_pos) + void start_mouse_selection(const arg_mouse& arg) { - auto logic_pos = coordinate_cast(screen_pos, true); + auto logic_pos = coordinate_cast(arg.pos, true); mouse_selection.started = true; mouse_selection.begin_position = logic_pos; mouse_selection.end_position = logic_pos; - mouse_selection.already_selected = lister.pick_items(true); - + if (arg.ctrl || arg.shift) + { + mouse_selection.already_selected = lister.pick_items(true); + mouse_selection.reverse_selection = arg.ctrl; + } API::set_capture(*listbox_ptr, true); } @@ -2081,93 +2079,88 @@ namespace nana mouse_selection.end_position = logic_pos; + bool cancel_selections = true; + auto content_x = coordinate_cast({ columns_range().first, 0 }, true).x; - if ((std::max)(mouse_selection.end_position.x, mouse_selection.begin_position.x) <= content_x || - (std::min)(mouse_selection.end_position.x, mouse_selection.begin_position.x) >= content_x + static_cast(header.pixels()) - ) + if ((std::max)(mouse_selection.end_position.x, mouse_selection.begin_position.x) >= content_x && + (std::min)(mouse_selection.end_position.x, mouse_selection.begin_position.x) < content_x + static_cast(header.pixels())) { - lister.select_for_all(false); - return; - } + auto const begin_off = (std::max)((std::min)(mouse_selection.begin_position.y, mouse_selection.end_position.y), 0) / item_height(); - auto begin_off = (std::max)((std::min)(mouse_selection.begin_position.y, mouse_selection.end_position.y), 0) / item_height(); - auto last_off = (std::max)(mouse_selection.begin_position.y, mouse_selection.end_position.y) / item_height(); - - auto begin = lister.advance(lister.first(), begin_off); - auto last = lister.advance(lister.first(), last_off); - - if (!lister.good(last)) - last = lister.last(); - - if (lister.good(begin) && ((mouse_selection.end_position.y < 0) || (lister.distance(lister.first(), begin) == begin_off))) - { - auto selections = lister.select_display_range_if(begin, last, false, [this](const index_pair& abs_pos) { - if (this->mouse_selection.reverse_selection) - { - if(mouse_selection.already_selected.cend() != std::find(mouse_selection.already_selected.cbegin(), mouse_selection.already_selected.cend(), abs_pos)) - { - item_proxy{ this, abs_pos }.select(false); - return false; - } - } - return true; - }); - - for (auto & pair : selections) + auto begin = lister.advance(lister.first(), begin_off); + if (!begin.empty()) { - if (pair.second) - continue; + std::vector> selections; - if (mouse_selection.selections.cend() == - std::find(mouse_selection.selections.cbegin(), mouse_selection.selections.cend(), pair.first)) + if ((mouse_selection.end_position.y < 0) || (lister.distance(lister.first(), begin) == begin_off)) { - mouse_selection.selections.push_back(pair.first); - } - } + //The range [begin_off, last_off] is a range of box selection + auto last_off = (std::max)(mouse_selection.begin_position.y, mouse_selection.end_position.y) / item_height(); + auto last = lister.advance(lister.first(), last_off); + + selections = lister.select_display_range_if(begin, last, false, [this](const index_pair& abs_pos) { + if (this->mouse_selection.reverse_selection) + { + if (mouse_selection.already_selected.cend() != std::find(mouse_selection.already_selected.cbegin(), mouse_selection.already_selected.cend(), abs_pos)) + { + item_proxy{ this, abs_pos }.select(false); + return false; + } + } + return true; + }); + + for (auto & pair : selections) + { + if (pair.second) + continue; + + if (mouse_selection.selections.cend() == + std::find(mouse_selection.selections.cbegin(), mouse_selection.selections.cend(), pair.first)) + { + mouse_selection.selections.push_back(pair.first); + } + } #ifdef _MSC_VER - for(auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();) + for (auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();) #else - for(auto i = mouse_selection.selections.begin(); i != mouse_selection.selections.end();) + for(auto i = mouse_selection.selections.begin(); i != mouse_selection.selections.end();) #endif - { - if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{*i})) - { - item_proxy{ this, *i }.select(false); - i = mouse_selection.selections.erase(i); - } - else - ++i; - } - - if (mouse_selection.reverse_selection) - { - for (auto & abs_pos : mouse_selection.already_selected) - { - if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{abs_pos})) { - item_proxy{ this, abs_pos }.select(true); + if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{ *i })) + { + item_proxy{ this, *i }.select(false); + i = mouse_selection.selections.erase(i); + } + else + ++i; } + + cancel_selections = false; } } } - else + + if (cancel_selections) { - for (auto & pos : mouse_selection.selections) + if (!mouse_selection.already_selected.empty()) { - item_proxy{ this, pos }.select(false); - } + for (auto & pos : mouse_selection.selections) + item_proxy(this, pos).select(false); - mouse_selection.selections.clear(); - - if (mouse_selection.reverse_selection) - { - for (auto & abs_pos : mouse_selection.already_selected) + //Don't restore the already selections if it is reverse selection(pressing shift). Behaves like Windows Explorer. + if (!mouse_selection.reverse_selection) { - item_proxy{ this, abs_pos }.select(true); + for (auto & abs_pos : mouse_selection.already_selected) + item_proxy(this, abs_pos).select(true); } } + else + lister.select_for_all(false); + + mouse_selection.selections.clear(); } } @@ -2203,12 +2196,10 @@ namespace nana ::nana::size calc_content_size(bool try_update = true) { size ctt_size( - this->header.pixels() + 5, - static_cast(this->lister.the_number_of_expanded()) + this->header.pixels() + this->header.margin(), + static_cast(this->lister.the_number_of_expanded()) * this->item_height() ); - ctt_size.height *= this->item_height(); - this->content_view->content_size(ctt_size, try_update); return ctt_size; @@ -2226,7 +2217,7 @@ namespace nana if (seq.empty()) return 0; - return (header.position(seq[0], nullptr) - this->content_view->origin().x + r.x); + return header.range(seq.front()).first + r.x - this->content_view->origin().x; } //Returns the absolute coordinate of the specified item in the window @@ -2258,12 +2249,19 @@ namespace nana } else if (area.x <= pos.x + origin.x && pos.x + origin.x < area.x + static_cast(header.pixels())) { + // detect if cursor is in the area of header margin + if (pos.x < area.x - origin.x + static_cast(header.margin())) + return{ parts::list_blank, npos }; + new_where.first = parts::list; auto const item_h = item_height(); //don't combine the following formula into the (pos.y - area.y - header_visible_px()) / item_h new_where.second = ((pos.y - area.y - header_visible_px() + origin.y) / item_h) - (origin.y / item_h); + if (this->lister.the_number_of_expanded() < new_where.second + 1) + return{ parts::list_blank, npos }; + if (checkable) { nana::rectangle r; @@ -2874,7 +2872,7 @@ namespace nana void es_lister::move_select(bool upwards, bool unselect_previous, bool /*trace_selected*/) noexcept { - auto next_selected_dpl = index_cast_noexcpt(last_selected_abs, false); //convert absolute position to display position + auto next_selected_dpl = index_cast_noexcpt(latest_selected_abs, false); //convert absolute position to display position if (next_selected_dpl.empty()) // has no cat ? (cat == npos) => beging from first cat { @@ -2995,8 +2993,8 @@ namespace nana for (item_proxy &it : cpx) it.select(value); - last_selected_abs.cat = pos; - last_selected_abs.item = npos; + latest_selected_abs.cat = pos; + latest_selected_abs.item = npos; return true; } @@ -3042,7 +3040,8 @@ namespace nana { if(essence_->ptr_state == item_state::highlighted) { - x -= r.x - essence_->content_view->origin().x; + x -= r.x - essence_->content_view->origin().x + static_cast(essence_->header.margin()); + for(auto & col : essence_->header.cont()) // in current order { @@ -3135,21 +3134,26 @@ namespace nana 0, r.height - 1 }; + //The first item includes the margin + unsigned margin = essence_->header.margin(); + for (auto & col : essence_->header.cont()) { if (col.visible_state) { - column_r.width = col.width_px; + column_r.width = col.width_px + margin; const auto right_pos = column_r.right(); //Make sure the column is in the display area. if (right_pos > r.x) { - _m_draw_header_item(graph, column_r, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); + _m_draw_header_item(graph, margin, column_r, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); graph.line({ right_pos - 1, r.y }, { right_pos - 1, r.bottom() - 2 }, border_color); } + margin = 0; + column_r.x = right_pos; if (right_pos > r.right()) break; @@ -3189,28 +3193,31 @@ namespace nana auto i = essence_->header.column_from_point(x); - if(i == npos) - i = essence_->header.boundary(essence_->header.position(grab, nullptr) >= x); + if (i == npos) + i = essence_->header.boundary(essence_->header.range(grab).first >= x); if(grab != i) { - unsigned item_pixels = 0; - auto item_x = essence_->header.position(i, &item_pixels); + auto item_rg = essence_->header.range(i); //Get the item pos //if mouse pos is at left of an item middle, the pos of itself otherwise the pos of the next. - place_front = (x <= (item_x + static_cast(item_pixels / 2))); - x = (place_front ? item_x : essence_->header.position(essence_->header.next(i), nullptr)); + place_front = (x <= (item_rg.first + static_cast(item_rg.second / 2))); + x = (place_front ? item_rg.first : essence_->header.range(essence_->header.next(i)).first); if (npos != i) - essence_->graph->rectangle({x - x_offset + rect.x, rect.y, 2, rect.height}, true, colors::red); + { + if (place_front && (0 == essence_->header.cast(i, false))) + x -= static_cast(essence_->header.margin()); + essence_->graph->rectangle({ x - x_offset + rect.x, rect.y, 2, rect.height }, true, colors::red); + } return i; } return npos; } - void _m_draw_header_item(graph_reference graph, const rectangle& column_r, const ::nana::color& fgcolor, const es_header::column& column, item_state state) + void _m_draw_header_item(graph_reference graph, unsigned margin, const rectangle& column_r, const ::nana::color& fgcolor, const es_header::column& column, item_state state) { ::nana::color bgcolor; @@ -3241,7 +3248,7 @@ namespace nana { graph.palette(true, fgcolor); - point text_pos{ column_r.x, (static_cast(essence_->scheme_ptr->header_height) - static_cast(essence_->text_height)) / 2 }; + point text_pos{ column_r.x + static_cast(margin), (static_cast(essence_->scheme_ptr->header_height) - static_cast(essence_->text_height)) / 2 }; if (align::left == column.alignment) text_pos.x += text_margin; @@ -3265,13 +3272,18 @@ namespace nana { const auto & col = essence_->header.at(essence_->pointer_where.second); - paint::graphics fl_graph({ col.width_px, essence_->scheme_ptr->header_height }); + auto margin = 0; + + if (&essence_->header.at(0, true) == &col) + margin = essence_->header.margin(); + + paint::graphics fl_graph({ col.width_px + margin, essence_->scheme_ptr->header_height }); fl_graph.typeface(essence_->graph->typeface()); - _m_draw_header_item(fl_graph, rectangle{ fl_graph.size()}, colors::white, col, item_state::floated); + _m_draw_header_item(fl_graph, margin, rectangle{ fl_graph.size()}, colors::white, col, item_state::floated); - auto xpos = essence_->header.position(col.index, nullptr) + pos.x - grabs_.start_pos; + auto xpos = essence_->header.range(col.index).first + pos.x - grabs_.start_pos; essence_->graph->blend(rectangle{ point{ xpos - essence_->content_view->origin().x + rect.x, rect.y } , fl_graph.size() }, fl_graph, {}, 0.5); } @@ -3331,23 +3343,38 @@ namespace nana auto const header_w = essence_->header.pixels(); auto const item_height_px = essence_->item_height(); - auto origin = essence_->content_view->origin(); - if (header_w < origin.x + rect.width) + auto const origin = essence_->content_view->origin(); + + auto const header_margin = essence_->header.margin(); + if (header_w + header_margin < origin.x + rect.width) { - rectangle r{ point{ rect.x + static_cast(header_w)-origin.x, rect.y }, + rectangle r{ point{ rect.x + static_cast(header_w + header_margin) - origin.x, rect.y }, size{ rect.width + origin.x - header_w, rect.height } }; if (!API::dev::copy_transparent_background(essence_->listbox_ptr->handle(), r, *essence_->graph, r.position())) essence_->graph->rectangle(r, true); } + if (header_margin > 0) + { + rectangle r = rect; + r.width = header_margin; + + if (!API::dev::copy_transparent_background(essence_->listbox_ptr->handle(), r, *essence_->graph, r.position())) + essence_->graph->rectangle(r, true); + } + es_lister & lister = essence_->lister; auto & ptr_where = essence_->pointer_where; - int item_top = rect.y - (origin.y % item_height_px); auto first_disp = essence_->first_display(); + point item_coord{ + essence_->item_xpos(rect), + rect.y - static_cast(origin.y % item_height_px) + }; + // The first display is empty when the listbox is empty. if (!first_disp.empty()) { @@ -3379,8 +3406,6 @@ namespace nana ind->detach(); } - const int x = essence_->item_xpos(rect); - //Here we draw the root categ (0) or a first item if the first drawing is not a categ.(item!=npos)) if (idx.cat == 0 || !idx.is_category()) { @@ -3393,16 +3418,16 @@ namespace nana std::size_t size = i_categ->items.size(); for (std::size_t offs = first_disp.item; offs < size; ++offs, ++idx.item) { - if (item_top >= rect.bottom()) + if (item_coord.y >= rect.bottom()) break; auto item_pos = lister.index_cast(index_pair{ idx.cat, offs }, true); //convert display position to absolute position - _m_draw_item(*i_categ, item_pos, x, item_top, txtoff, header_w, rect, subitems, bgcolor, fgcolor, + _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, rect, subitems, bgcolor, fgcolor, (hoverred_pos == idx ? item_state::highlighted : item_state::normal) ); - item_top += item_height_px; + item_coord.y += static_cast(item_height_px); } ++i_categ; @@ -3411,15 +3436,15 @@ namespace nana for (; i_categ != lister.cat_container().end(); ++i_categ, ++idx.cat) { - if (item_top > rect.bottom()) + if (item_coord.y > rect.bottom()) break; idx.item = 0; - _m_draw_categ(*i_categ, rect.x - origin.x, item_top, txtoff, header_w, rect, bgcolor, + _m_draw_categ(*i_categ, rect.x - origin.x, item_coord.y, txtoff, header_w, bgcolor, (hoverred_pos.is_category() && (idx.cat == hoverred_pos.cat) ? item_state::highlighted : item_state::normal) ); - item_top += item_height_px; + item_coord.y += static_cast(item_height_px); if (false == i_categ->expand) continue; @@ -3427,17 +3452,17 @@ namespace nana auto size = i_categ->items.size(); for (decltype(size) pos = 0; pos < size; ++pos) { - if (item_top > rect.bottom()) + if (item_coord.y > rect.bottom()) break; auto item_pos = lister.index_cast(index_pair{ idx.cat, pos }, true); //convert display position to absolute position - _m_draw_item(*i_categ, item_pos, x, item_top, txtoff, header_w, rect, subitems, bgcolor, fgcolor, + _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, rect, subitems, bgcolor, fgcolor, (idx == hoverred_pos ? item_state::highlighted : item_state::normal) ); - item_top += item_height_px; - if (item_top >= rect.bottom()) + item_coord.y += static_cast(item_height_px); + if (item_coord.y >= rect.bottom()) break; ++idx.item; @@ -3447,9 +3472,9 @@ namespace nana essence_->inline_buffered_table.clear(); } - if (item_top < rect.bottom()) + if (item_coord.y < rect.bottom()) { - rectangle bground_r{ rect.x, item_top, rect.width, static_cast(rect.bottom() - item_top) }; + rectangle bground_r{ rect.x, item_coord.y, rect.width, static_cast(rect.bottom() - item_coord.y) }; if (!API::dev::copy_transparent_background(essence_->listbox_ptr->handle(), bground_r, *essence_->graph, bground_r.position())) essence_->graph->rectangle(bground_r, true, bgcolor); } @@ -3475,11 +3500,11 @@ namespace nana } } private: - void _m_draw_categ(const category_t& categ, int x, int y, int txtoff, unsigned width, const nana::rectangle& r, nana::color bgcolor, item_state state) + void _m_draw_categ(const category_t& categ, int x, int y, int txtoff, unsigned width, nana::color bgcolor, item_state state) { const auto item_height = essence_->item_height(); - rectangle bground_r{ x, y, width, item_height }; + rectangle bground_r{ x + static_cast(essence_->header.margin()), y, width, item_height }; auto graph = essence_->graph; item_data item; @@ -3514,7 +3539,7 @@ namespace nana //Draw selecting inner rectangle if (item.flags.selected && (categ.expand == false)) - _m_draw_item_border(r.x, y, (std::min)(r.width, width - essence_->content_view->origin().x)); + _m_draw_item_border(y); } color _m_draw_item_bground(const rectangle& bground_r, color bgcolor, color cell_color, item_state state, const item_data& item) @@ -3563,8 +3588,7 @@ namespace nana /// Draws an item void _m_draw_item(const category_t& cat, const index_pair& item_pos, - const int x, ///< left coordinate ? - const int y, ///< top coordinate + const point& coord, const int txtoff, ///< below y to print the text unsigned width, const nana::rectangle& content_r, ///< the rectangle where the full list content have to be drawn @@ -3592,10 +3616,10 @@ namespace nana auto graph = essence_->graph; //draw the background for the whole item - rectangle bground_r{ content_r.x, y, show_w, essence_->item_height() }; + 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); - int column_x = x; + 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 { @@ -3638,8 +3662,8 @@ namespace nana { nana::rectangle imgt(item.img_show_size); img_r = imgt; - img_r.x = content_pos + column_x + (16 - static_cast(item.img_show_size.width)) / 2; // center in 16 - geom scheme? - img_r.y = y + (static_cast(essence_->item_height()) - static_cast(item.img_show_size.height)) / 2; // center + img_r.x = content_pos + coord.x + (16 - static_cast(item.img_show_size.width)) / 2; // center in 16 - geom scheme? + img_r.y = coord.y + (static_cast(essence_->item_height()) - static_cast(item.img_show_size.height)) / 2; // center } content_pos += 18; // image width, geom scheme? } @@ -3655,18 +3679,18 @@ namespace nana //Make sure the user-define inline widgets is in the right visible rectangle. rectangle pane_r; - const auto wdg_x = column_x + content_pos; + const auto wdg_x = coord.x + content_pos; const auto wdg_w = col.width_px - static_cast(content_pos); bool visible_state = true; - if (::nana::overlap(content_r, { wdg_x, y, wdg_w, essence_->item_height() }, pane_r)) + if (::nana::overlap(content_r, { wdg_x, coord.y, wdg_w, essence_->item_height() }, pane_r)) { ::nana::point pane_pos; if (wdg_x < content_r.x) pane_pos.x = wdg_x - content_r.x; - if (y < content_r.y) - pane_pos.y = y - content_r.y; + if (coord.y < content_r.y) + pane_pos.y = coord.y - content_r.y; inline_wdg->pane_widget.move(pane_pos); inline_wdg->pane_bottom.move(pane_r); @@ -3713,7 +3737,7 @@ namespace nana { col_fgcolor = m_cell.custom_format->fgcolor; - bground_r = rectangle{ column_x, y, col.width_px, essence_->item_height() }; + bground_r = rectangle{ column_x, coord.y, col.width_px, essence_->item_height() }; col_bgcolor = this->_m_draw_item_bground(bground_r, bgcolor, m_cell.custom_format->bgcolor, state, item); } else @@ -3730,27 +3754,28 @@ namespace nana text_margin_right = essence_->scheme_ptr->text_margin; graph->palette(true, col_fgcolor); - text_aligner.draw(m_cell.text, { column_x + content_pos, y + txtoff }, col.width_px - content_pos - text_margin_right); + text_aligner.draw(m_cell.text, { column_x + content_pos, coord.y + txtoff }, col.width_px - content_pos - text_margin_right); } } if (0 == display_order) { if (essence_->checkable) - crook_renderer_.draw(*graph, col_bgcolor, col_fgcolor, essence_->checkarea(column_x, y), estate); + crook_renderer_.draw(*graph, col_bgcolor, col_fgcolor, essence_->checkarea(column_x, coord.y), estate); if (item.img) item.img.stretch(rectangle{ item.img.size() }, *graph, img_r); } - graph->line({ column_x - 1, y }, { column_x - 1, y + static_cast(essence_->item_height()) - 1 }, static_cast(0xEBF4F9)); + if (display_order > 0) + graph->line({ column_x - 1, coord.y }, { column_x - 1, coord.y + static_cast(essence_->item_height()) - 1 }, static_cast(0xEBF4F9)); } column_x += col.width_px; } //Draw selecting inner rectangle - if(item.flags.selected) - _m_draw_item_border(content_r.x, y, show_w); + if (item.flags.selected) + _m_draw_item_border(coord.y); } inline_pane * _m_get_inline_pane(const category_t& cat, std::size_t column_pos) const @@ -3789,9 +3814,10 @@ namespace nana return nullptr; } - void _m_draw_item_border(int x, int y, unsigned width) const + void _m_draw_item_border(int item_top) const { //Draw selecting inner rectangle + /* rectangle r{ x, y, width, essence_->item_height() }; essence_->graph->rectangle(r, false, static_cast(0x99defd)); @@ -3799,6 +3825,21 @@ namespace nana essence_->graph->palette(false, colors::white); paint::draw(*essence_->graph).corner(r, 1); + essence_->graph->rectangle(r.pare_off(1), false); + */ + + rectangle r{ + essence_->content_area().x - essence_->content_view->origin().x + static_cast(essence_->header.margin()), + item_top, + essence_->header.pixels(), + essence_->item_height() + }; + + essence_->graph->rectangle(r, false, static_cast(0x99defd)); + + essence_->graph->palette(false, colors::white); + paint::draw(*essence_->graph).corner(r, 1); + essence_->graph->rectangle(r.pare_off(1), false); } private: @@ -3882,6 +3923,9 @@ namespace nana using item_state = essence::item_state; using parts = essence::parts; + //Cancel deferred deselection operation when mouse moves. + essence_->deselect_deferred = false; + bool need_refresh = false; point pos_in_header = arg.pos; @@ -4023,18 +4067,18 @@ namespace nana if (arg.shift) { //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); + //is not a latest selected item.(#154 reported by RenaudAlpes) + if (lister.latest_selected_abs.empty() || lister.latest_selected_abs.is_category()) + lister.latest_selected_abs.set_both(0); - auto before = lister.last_selected_abs; + auto before = lister.latest_selected_abs; - lister.select_display_range_if(lister.last_selected_abs, item_pos, true, [](const index_pair&) + lister.select_display_range_if(lister.latest_selected_abs, item_pos, true, [](const index_pair&) { return true; }); - lister.last_selected_abs = before; + lister.latest_selected_abs = before; } else if (arg.ctrl) { @@ -4075,10 +4119,10 @@ namespace nana if (item_ptr->flags.selected) { lister.cancel_others_if_single_enabled(true, abs_item_pos); - essence_->lister.last_selected_abs = abs_item_pos; + essence_->lister.latest_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.latest_selected_abs == abs_item_pos) + essence_->lister.latest_selected_abs.set_both(npos); } } else @@ -4101,23 +4145,6 @@ namespace nana } update = true; } - else - { - //Blank area is clicked - - bool unselect_all = true; - if (!lister.single_status(true)) //not single selected - { - if (arg.ctrl || arg.shift) - { - essence_->mouse_selection.reverse_selection = arg.ctrl; - unselect_all = false; - } - } - - if(unselect_all) - update = lister.select_for_all(false); //unselect all items due to the blank area being clicked - } if(update) { @@ -4135,9 +4162,11 @@ namespace nana { //Start box selection if mulit-selection is enabled if (arg.is_left_button() && (!lister.single_status(true))) - essence_->start_mouse_selection(arg.pos); + essence_->start_mouse_selection(arg); - lister.select_for_all(false); + //Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed + //Pressing ctrl or shift is to selects other items without deselecting current selections. + essence_->deselect_deferred = !(arg.ctrl || arg.shift); } if(update) @@ -4177,6 +4206,12 @@ namespace nana need_refresh = true; } + if (essence_->deselect_deferred) + { + essence_->deselect_deferred = false; + need_refresh |= (essence_->lister.select_for_all(false)); + } + if (need_refresh) { refresh(graph); @@ -4242,17 +4277,18 @@ namespace nana void trigger::key_press(graph_reference graph, const arg_keyboard& arg) { - bool up = false; + // Exit if list is empty + if (essence_->lister.first().empty()) + return; - if (essence_->lister.cat_container().size() == 1 && essence_->lister.size_item(0)==0) - return ; + bool upward = false; switch(arg.key) { case keyboard::os_arrow_up: - up = true; + upward = true; case keyboard::os_arrow_down: - essence_->lister.move_select(up, !arg.shift, true); + essence_->lister.move_select(upward, !arg.shift, true); break; case L' ': { @@ -4263,18 +4299,18 @@ namespace nana } break; case keyboard::os_pageup : - up = true; + upward = true; case keyboard::os_pagedown: { //Turns page, then returns if no change occurs - if (!essence_->content_view->turn_page(!up, false)) + if (!essence_->content_view->turn_page(!upward, false)) return; essence_->lister.select_for_all(false); auto idx = essence_->first_display(); - if (!up) + if (!upward) idx = essence_->lister.advance(idx, static_cast(essence_->count_of_exposed(false)) - 1); if (!idx.is_category()) @@ -4285,20 +4321,59 @@ namespace nana break; } case keyboard::os_home: + case keyboard::os_end: { essence_->lister.select_for_all(false); - index_pair frst{essence_->lister.first()}; - if (! frst.is_category()) - item_proxy::from_display(essence_, frst).select(true); - else if (!essence_->lister.single_status(true)) //not selected - essence_->lister.cat_status(frst.cat, true, true); + auto pos = (keyboard::os_home == arg.key ? essence_->lister.first() : essence_->lister.last()); + if (!pos.empty()) + { + //When the pos indicates an empty category, then search forwards/backwards(depending on arg.key whether it is Home or End) for a non empty category. + //When a non-empty category is found, assign the pos to the first/last item of the category if the category is expanded. + if (pos.is_category()) + { + if (keyboard::os_home == arg.key) + { + while (0 == essence_->lister.size_item(pos.cat)) + { + if (++pos.cat >= essence_->lister.cat_container().size()) + { + pos = index_pair{ npos, npos }; + break; + } + } + } + else + { + while (0 == essence_->lister.size_item(pos.cat)) + { + if (pos.cat-- == 0) + { + pos = index_pair{ npos, npos }; + break; + } + } + } - break; + if (!pos.empty()) + { + if (essence_->lister.expand(pos.cat)) + pos.item = 0; + } + } + + if (!pos.empty()) + { + if (pos.is_category()) + { + if (!essence_->lister.single_status(true)) //multiple selection is not enabled + essence_->lister.cat_status(pos.cat, true, true); + } + else + item_proxy::from_display(essence_, pos).select(true); + } + } } - case keyboard::os_end: - essence_->lister.select_for_all(false); - item_proxy::from_display(essence_, essence_->lister.last()).select(true); break; default: return; @@ -4320,9 +4395,12 @@ namespace nana return; } case keyboard::select_all : - essence_->lister.select_for_all(true); - refresh(graph); - API::dev::lazy_refresh(); + if (!essence_->lister.single_status(true)) + { + essence_->lister.select_for_all(true); + refresh(graph); + API::dev::lazy_refresh(); + } break; default: return; @@ -4420,10 +4498,10 @@ namespace nana if (m.flags.selected) { ess_->lister.cancel_others_if_single_enabled(true, pos_); //Cancel all selections except pos_ if single_selection is enabled. - ess_->lister.last_selected_abs = pos_; + ess_->lister.latest_selected_abs = pos_; } - else if (ess_->lister.last_selected_abs == pos_) - ess_->lister.last_selected_abs.set_both(npos); + else if (ess_->lister.latest_selected_abs == pos_) + ess_->lister.latest_selected_abs.set_both(npos); if (scroll_view) { @@ -4697,7 +4775,7 @@ namespace nana for (item_proxy &it : *this ) it.select(sel); - ess_->lister.last_selected_abs = index_pair {this->pos_, npos}; + ess_->lister.latest_selected_abs = index_pair {this->pos_, npos}; return *this; } @@ -5140,6 +5218,15 @@ namespace nana ess.update(); } + rectangle listbox::content_area() const + { + auto & ess = _m_ess(); + auto carea = ess.content_area(); + carea.x += ess.header.margin(); + carea.width -= (std::min)(carea.width, ess.header.margin()); + return carea; + } + auto listbox::insert(cat_proxy cat, std::string str) -> cat_proxy { internal_scope_guard lock; @@ -5312,7 +5399,6 @@ namespace nana { auto & ess = _m_ess(); ess.lister.erase(); - //ess.first_display(ess.lister.first()); ess.calc_content_size(); ess.update(); } diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 9b17d1e7..8ad5e35b 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -85,7 +85,7 @@ namespace nana //class item_renderer item_renderer::item_renderer(window wd, graph_reference graph) - :handle_(wd), graph_(graph), scheme_ptr_(static_cast(API::dev::get_scheme(wd))) + :graph_(graph), scheme_ptr_(static_cast(API::dev::get_scheme(wd))) {} void item_renderer::background(const nana::point& pos, const nana::size& size, state item_state) diff --git a/source/gui/widgets/skeletons/content_view.cpp b/source/gui/widgets/skeletons/content_view.cpp index 438ce4ea..8995944a 100644 --- a/source/gui/widgets/skeletons/content_view.cpp +++ b/source/gui/widgets/skeletons/content_view.cpp @@ -30,7 +30,8 @@ namespace nana { point skew_vert; nana::size extra_px; - bool enable_update{ true }; + bool passive{ true }; //The passive mode determines whether to update if scrollbar changes. It updates the client window if passive is true. + bool drag_started{ false }; point origin; @@ -171,7 +172,7 @@ namespace nana { }); } - void size_changed(bool try_update) + void size_changed(bool passive) { auto imd_area = view.view_area(); @@ -186,11 +187,11 @@ namespace nana { if (this->events.scrolled) this->events.scrolled(); - if (this->enable_update) + if (this->passive) API::refresh_window(this->window_handle); }; - this->enable_update = try_update; + this->passive = passive; if (imd_area.width != disp_area.width) { @@ -199,7 +200,7 @@ namespace nana { vert.create(window_handle); vert.events().value_changed.connect_unignorable(event_fn); API::take_active(vert, false, window_handle); - this->enable_update = false; + this->passive = false; } vert.move({ @@ -226,7 +227,7 @@ namespace nana { horz.create(window_handle); horz.events().value_changed.connect_unignorable(event_fn); API::take_active(horz, false, window_handle); - this->enable_update = false; + this->passive = false; } horz.move({ @@ -246,7 +247,7 @@ namespace nana { origin.x = 0; } - this->enable_update = true; + this->passive = true; } }; @@ -319,31 +320,31 @@ namespace nana { void content_view::content_size(const size& sz, bool try_update) { + auto const view_sz = this->view_area(sz); + if (sz.height < impl_->content_size.height) { - if (impl_->origin.y + impl_->disp_area.height > sz.height) + if (impl_->origin.y + view_sz.height > sz.height) { - if (impl_->disp_area.height > sz.height) + if (view_sz.height > sz.height) impl_->origin.y = 0; else - impl_->origin.y = sz.height - impl_->disp_area.height; + impl_->origin.y = sz.height - view_sz.height; } } if (sz.width < impl_->content_size.width) { - if (impl_->origin.x + impl_->disp_area.width > sz.width) + if (impl_->origin.x + view_sz.width > sz.width) { - if (impl_->disp_area.width > sz.width) + if (view_sz.width > sz.width) impl_->origin.x = 0; else - impl_->origin.x = sz.width - impl_->disp_area.width; + impl_->origin.x = sz.width - view_sz.width; } } - impl_->content_size = sz; - impl_->size_changed(try_update); } @@ -388,18 +389,23 @@ namespace nana { rectangle content_view::view_area() const { - unsigned extra_horz = (impl_->disp_area.width < impl_->content_size.width ? space() : 0); - unsigned extra_vert = (impl_->disp_area.height < impl_->content_size.height + extra_horz ? space() : 0); + return view_area(impl_->content_size); + } + + rectangle content_view::view_area(const size& alt_content_size) const + { + unsigned extra_horz = (impl_->disp_area.width < alt_content_size.width ? space() : 0); + unsigned extra_vert = (impl_->disp_area.height < alt_content_size.height + extra_horz ? space() : 0); if ((0 == extra_horz) && extra_vert) - extra_horz = (impl_->disp_area.width < impl_->content_size.width + extra_vert ? space() : 0); + extra_horz = (impl_->disp_area.width < alt_content_size.width + extra_vert ? space() : 0); return rectangle{ impl_->disp_area.position(), size{ - impl_->disp_area.width > extra_vert ? impl_->disp_area.width - extra_vert : 0, - impl_->disp_area.height > extra_horz ? impl_->disp_area.height - extra_horz : 0 - } + impl_->disp_area.width > extra_vert ? impl_->disp_area.width - extra_vert : 0, + impl_->disp_area.height > extra_horz ? impl_->disp_area.height - extra_horz : 0 + } }; } @@ -457,12 +463,12 @@ namespace nana { return (pre_origin != impl_->origin); } - void content_view::sync(bool try_update) + void content_view::sync(bool passive) { - impl_->enable_update = try_update; + impl_->passive = passive; impl_->horz.value(impl_->origin.x); impl_->vert.value(impl_->origin.y); - impl_->enable_update = true; + impl_->passive = true; } void content_view::pursue(const point& cursor) diff --git a/source/gui/widgets/skeletons/content_view.hpp b/source/gui/widgets/skeletons/content_view.hpp index 6d4b2d40..9abd59a4 100644 --- a/source/gui/widgets/skeletons/content_view.hpp +++ b/source/gui/widgets/skeletons/content_view.hpp @@ -65,6 +65,7 @@ namespace skeletons void draw_corner(graph_reference); rectangle view_area() const; + rectangle view_area(const size& alt_content_size) const; unsigned extra_space(bool horz) const; @@ -73,7 +74,7 @@ namespace skeletons /// Returns true if the origin is moved bool move_origin(const point& skew); - void sync(bool try_update); + void sync(bool passive); void pursue(const point& cursor); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index d008d70a..a461137e 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -18,6 +18,7 @@ #include #include "content_view.hpp" +#include #include #include #include @@ -45,7 +46,7 @@ namespace nana{ namespace widgets { public: using command = EnumCommand; - using container = std::deque < std::unique_ptr> >; + using container = std::deque>>; void clear() { @@ -142,6 +143,11 @@ namespace nana{ namespace widgets //sel_a_ and sel_b_ are not sorted, sel_b_ keeps the caret position. sel_a_ = editor_.select_.a; sel_b_ = editor_.select_.b; + + if (sel_a_ != sel_b_) + { + selected_text_ = editor_._m_make_select_string(); + } } void set_caret_pos() @@ -178,8 +184,7 @@ namespace nana{ namespace widgets void set_removed(std::wstring str) { - //use selected_text_ as removed text - selected_text_ = str; + selected_text_ = std::move(str); } void execute(bool redo) override @@ -286,15 +291,15 @@ namespace nana{ namespace widgets } else editor_.textbase().erase(pos_.y, pos_.x, text_.size()); //undo + } - if (!selected_text_.empty()) - { - editor_.points_.caret = sel_a_; - editor_._m_put(selected_text_); - editor_.points_.caret = sel_b_; - editor_.select_.a = sel_a_; //Reset the selected text - editor_.select_.b = sel_b_; - } + if (!selected_text_.empty()) + { + editor_.points_.caret = (std::min)(sel_a_, sel_b_); + editor_._m_put(selected_text_); + editor_.points_.caret = sel_b_; + editor_.select_.a = sel_a_; //Reset the selected text + editor_.select_.b = sel_b_; } } editor_.move_caret(editor_.points_.caret); @@ -406,11 +411,11 @@ namespace nana{ namespace widgets std::shared_ptr find(std::size_t pos) const { - for (auto i = colored_areas_.cbegin(); i != colored_areas_.cend(); ++i) + for (auto & sp : colored_areas_) { - if (i->get()->begin <= pos && pos < i->get()->begin + i->get()->count) - return *i; - else if (i->get()->begin > pos) + if (sp->begin <= pos && pos < sp->begin + sp->count) + return sp; + else if (sp->begin > pos) break; } return{}; @@ -440,7 +445,7 @@ namespace nana{ namespace widgets ); } - bool clear() + bool clear() override { if (colored_areas_.empty()) return false; @@ -555,7 +560,6 @@ namespace nana{ namespace widgets virtual std::size_t take_lines() const = 0; /// Returns the number of lines that the line of text specified by pos takes. virtual std::size_t take_lines(std::size_t pos) const = 0; - virtual bool adjust_caret_into_screen() = 0; }; inline bool is_right_text(const unicode_bidi::entity& e) @@ -623,7 +627,9 @@ namespace nana{ namespace widgets //textbase is implement by using deque, and the linemtr holds the text pointers //If the textbase is changed, it will check the text pointers. std::size_t line = 0; - for (auto & sct : this->sections_) + + auto const & const_sections = sections_; + for (auto & sct : const_sections) { auto const& text = editor_.textbase().getline(line); if (sct.begin < text.c_str() || (text.c_str() + text.size() < sct.begin)) @@ -646,7 +652,9 @@ namespace nana{ namespace widgets //textbase is implement by using deque, and the linemtr holds the text pointers //If the textbase is changed, it will check the text pointers. std::size_t line = 0; - for (auto & sct : this->sections_) + + auto const & const_sections = sections_; + for (auto & sct : const_sections) { if (line < pos || (pos + line_size) <= line) { @@ -664,8 +672,8 @@ namespace nana{ namespace widgets auto const & text = editor_.textbase().getline(pos); auto& txt_section = this->sections_[pos]; txt_section.begin = text.c_str(); - txt_section.end = text.c_str() + text.size(); - txt_section.pixels = editor_._m_text_extent_size(text.c_str(), text.size()).width; + txt_section.end = txt_section.begin + text.size(); + txt_section.pixels = editor_._m_text_extent_size(txt_section.begin, text.size()).width; } void pre_calc_lines(unsigned) override @@ -685,62 +693,6 @@ namespace nana{ namespace widgets { return 1; } - - //adjust_caret_into_screen - //@brief: Adjust the text offset in order to moving caret into visible area if it is out of the visible area - //@note: the function assumes the points_.caret is correct - bool adjust_caret_into_screen() override - { - const auto scrlines = editor_.screen_lines(); - if (0 == scrlines) - return false; - - auto const pre_origin = editor_.impl_->cview->origin(); - auto origin = pre_origin; - - auto& points = editor_.points_; - auto& textbase = editor_.textbase(); - - auto& lnstr = textbase.getline(points.caret.y); - const auto x = (std::min)(points.caret.x, static_cast(lnstr.size())); - auto const text_w = editor_._m_pixels_by_char(lnstr, x); - - auto area_w = editor_.impl_->cview->view_area().width; - - if (static_cast(text_w) < origin.x) - { - auto delta_pixels = editor_._m_text_extent_size(L" ", 4).width; - origin.x = (text_w > delta_pixels ? text_w - delta_pixels : 0); - } - else if (area_w && (text_w >= origin.x + area_w)) - origin.x = text_w - area_w + 2; - - int row = origin.y / static_cast(editor_.line_height()); - if (points.caret.y >= row + scrlines) //implicit condition scrlines > 0 - { - row = static_cast(points.caret.y - scrlines) + 1; - } - else if (static_cast(points.caret.y) < row) - { - if (scrlines >= static_cast(row)) - row = 0; - else - row = static_cast(row - scrlines); - } - else if (row && (textbase.lines() <= scrlines)) - row = 0; - - if(row != origin.y / static_cast(editor_.line_height())) - origin.y = row * editor_.line_height(); - - if (pre_origin != origin) - { - editor_.impl_->cview->move_origin(origin - pre_origin); - editor_.impl_->cview->sync(true); - return true; - } - return false; - } private: text_editor& editor_; std::vector sections_; @@ -773,9 +725,9 @@ namespace nana{ namespace widgets if ((0 == editor_.textbase().lines()) || (0 == line_px)) return coord; - auto screen_rows = (top - editor_.text_area_.area.y + editor_.impl_->cview->origin().y) / line_px; + auto text_row = (std::max)(0, (top - editor_.text_area_.area.y + editor_.impl_->cview->origin().y) / line_px); - coord = _m_textline(static_cast(screen_rows)); + coord = _m_textline(static_cast(text_row)); if (linemtr_.size() <= coord.first) { coord.first = linemtr_.size() - 1; @@ -795,22 +747,11 @@ namespace nana{ namespace widgets std::swap(first, second); if (second < linemtr_.size()) - linemtr_.erase(linemtr_.begin() + first + 1, linemtr_.begin() + second); + linemtr_.erase(linemtr_.begin() + first + 1, linemtr_.begin() + second + 1); - pre_calc_line(first, editor_.width_pixels()); + auto const width_px = editor_.width_pixels(); - //textbase is implement by using deque, and the linemtr holds the text pointers - //If the textbase is changed, it will check the text pointers. - std::size_t line = 0; - for (auto & mtr: linemtr_) - { - auto& linestr = editor_.textbase().getline(line); - auto p = mtr.line_sections.front().begin; - if (p < linestr.c_str() || (linestr.c_str() + linestr.size() < p)) - pre_calc_line(line, editor_.width_pixels()); - - ++line; - } + pre_calc_line(first, width_px); } void add_lines(std::size_t pos, std::size_t lines) override @@ -823,7 +764,9 @@ namespace nana{ namespace widgets //textbase is implement by using deque, and the linemtr holds the text pointers //If the textbase is changed, it will check the text pointers. std::size_t line = 0; - for (auto & mtr : linemtr_) + + auto const & const_linemtr = linemtr_; + for (auto & mtr : const_linemtr) { if (line < pos || (pos + lines) <= line) { @@ -942,42 +885,6 @@ namespace nana{ namespace widgets { return (pos < linemtr_.size() ? linemtr_[pos].take_lines : 0); } - - bool adjust_caret_into_screen() override - { - const auto scrlines = editor_.screen_lines(); - if (0 == scrlines) - return false; - - const auto & points = editor_.points_; - auto off_coord = _m_textline(static_cast(editor_._m_text_topline())); - - nana::upoint caret_secondary; - editor_._m_pos_secondary(points.caret, caret_secondary); - - //Use the caret line for the offset line when caret is in front of current offset line. - if (off_coord.first > points.caret.y || ((off_coord.first == points.caret.y) && (off_coord.second > caret_secondary.y))) - { - //Use the line which was specified by points.caret for the first line. - _m_set_offset_by_secondary(row_coordinate(points.caret.y, caret_secondary.y)); - return true; - } - - //Find the last screen line. If the text does not reach the bottom of screen, - //do not adjust the offset line. - row_coordinate bottom; - if (false == _m_advance_secondary(off_coord, static_cast(scrlines - 1), bottom)) - return false; - - //Do not adjust the offset line if the caret line does not reach the bottom line. - if (points.caret.y < bottom.first || ((points.caret.y == bottom.first) && (caret_secondary.y <= bottom.second))) - return false; - - _m_advance_secondary(row_coordinate(points.caret.y, caret_secondary.y), -static_cast(scrlines - 1), bottom); - - _m_set_offset_by_secondary(bottom); - return true; - } private: void _m_text_section(const std::wstring& str, std::vector& tsec) { @@ -1014,90 +921,6 @@ namespace nana{ namespace widgets tsec.emplace_back(word, end, unsigned{}); } - void _m_set_offset_by_secondary(row_coordinate row) - { - for (auto i = linemtr_.begin(), end = linemtr_.begin() + row.first; i != end; ++i) - row.second += i->take_lines; - - auto origin = editor_.impl_->cview->origin(); - origin.y = static_cast(row.second * editor_.line_height()); - editor_.impl_->cview->move_origin(origin - editor_.impl_->cview->origin()); - } - - bool _m_advance_secondary(row_coordinate row, int distance, row_coordinate& new_row) - { - if ((row.first >= linemtr_.size()) || (row.second >= linemtr_[row.first].take_lines)) - return false; - - if (0 == distance) - { - new_row = row; - return true; - } - - if (distance < 0) - { - std::size_t n = static_cast(-distance); - - if (row.second > n) - { - new_row.first = row.first; - new_row.second = row.second - n; - return true; - } - - if (0 == row.first) - return false; - - --row.first; - n -= (row.second + 1); - - while (true) - { - auto lines = linemtr_[row.first].take_lines; - if (lines >= n) - { - new_row.first = row.first; - new_row.second = lines - n; - return true; - } - if (0 == row.first) - return false; - - --row.first; - n -= lines; - } - } - else - { - std::size_t n = static_cast(distance); - - auto delta_lines = linemtr_[row.first].take_lines - (row.second + 1); - - if (delta_lines >= n) - { - new_row.first = row.first; - new_row.second = row.second + n; - return true; - } - - n -= delta_lines; - - while (++row.first < linemtr_.size()) - { - auto & mtr = linemtr_[row.first]; - if (mtr.take_lines >= n) - { - new_row.first = row.first; - new_row.second = n - 1; - return true; - } - n -= mtr.take_lines; - } - } - return false; - } - row_coordinate _m_textline(std::size_t scrline) const { row_coordinate coord; @@ -1236,6 +1059,12 @@ namespace nana{ namespace widgets this->reset_caret(); }; + impl_->cview->events().hover_outside = [this](const point& pos) { + mouse_caret(pos, false); + if (selection::mode::mouse_selected == select_.mode_selection || selection::mode::method_selected == select_.mode_selection) + set_end_caret(false); + }; + API::create_caret(wd, { 1, line_height() }); API::bgcolor(wd, colors::white); API::fgcolor(wd, colors::black); @@ -1249,6 +1078,11 @@ namespace nana{ namespace widgets delete impl_; } + size text_editor::caret_size() const + { + return { 1, line_height() }; + } + void text_editor::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor) { if (fgcolor.invisible() && bgcolor.invisible()) @@ -1411,10 +1245,9 @@ namespace nana{ namespace widgets return false; _m_reset(); - impl_->capacities.behavior->pre_calc_lines(width_pixels()); impl_->try_refresh = sync_graph::refresh; - _m_reset_content_size(); + _m_reset_content_size(true); return true; } @@ -1680,7 +1513,7 @@ namespace nana{ namespace widgets API::set_capture(window_, true); text_area_.captured = true; - if (this->hit_select_area(_m_screen_to_caret(arg.pos), true)) + if (this->hit_select_area(_m_coordinate_to_caret(arg.pos), true)) { //The selected of text can be moved only if it is editable if (attributes_.editable) @@ -1732,7 +1565,7 @@ namespace nana{ namespace widgets //no move occurs select(false); - move_caret(_m_screen_to_caret(arg.pos)); + move_caret(_m_coordinate_to_caret(arg.pos)); } select_.mode_selection = selection::mode::no_selected; @@ -1798,10 +1631,10 @@ namespace nana{ namespace widgets if (graph_) { - impl_->capacities.behavior->adjust_caret_into_screen(); + this->_m_adjust_view(); + reset_caret(); impl_->try_refresh = sync_graph::refresh; - points_.xpos = 0; //_m_put calcs the lines _m_reset_content_size(false); @@ -1832,7 +1665,7 @@ namespace nana{ namespace widgets const unsigned line_pixels = line_height(); //The coordinate of caret - auto coord = _m_caret_to_screen(crtpos); + auto coord = _m_caret_to_coordinate(crtpos); const int line_bottom = coord.y + static_cast(line_pixels); @@ -1863,7 +1696,7 @@ namespace nana{ namespace widgets //Adjust the caret into screen when the caret position is modified by this function if (reset_caret && (!hit_text_area(coord))) { - impl_->capacities.behavior->adjust_caret_into_screen(); + this->_m_adjust_view(); impl_->try_refresh = sync_graph::refresh; caret->visible(true); return true; @@ -1947,9 +1780,8 @@ namespace nana{ namespace widgets { bool new_sel_end = (select_.b != points_.caret); select_.b = points_.caret; - points_.xpos = points_.caret.x; - if(new_sel_end || (stay_in_view && impl_->capacities.behavior->adjust_caret_into_screen())) + if (new_sel_end || (stay_in_view && this->_m_adjust_view())) impl_->try_refresh = sync_graph::refresh; } @@ -1985,7 +1817,7 @@ namespace nana{ namespace widgets { points_.caret = select_.b; - if (impl_->capacities.behavior->adjust_caret_into_screen()) + if (this->_m_adjust_view()) impl_->try_refresh = sync_graph::refresh; reset_caret(); @@ -1994,7 +1826,7 @@ namespace nana{ namespace widgets if (_m_move_select(true)) { - impl_->capacities.behavior->adjust_caret_into_screen(); + this->_m_adjust_view(); impl_->try_refresh = sync_graph::refresh; return true; } @@ -2040,6 +1872,14 @@ namespace nana{ namespace widgets select_.move_to_end = move_to_end; } + std::size_t text_editor::line_count(bool text_lines) const + { + if (text_lines) + return textbase().lines(); + + return impl_->capacities.behavior->take_lines(); + } + void text_editor::draw_corner() { impl_->cview->draw_corner(graph_); @@ -2115,13 +1955,14 @@ namespace nana{ namespace widgets impl_->undo.push(std::move(undo_ptr)); + _m_reset_content_size(true); + if(graph_) { - impl_->capacities.behavior->adjust_caret_into_screen(); + this->_m_adjust_view(); + reset_caret(); impl_->try_refresh = sync_graph::refresh; - _m_reset_content_size(true); - points_.xpos = points_.caret.x; } } @@ -2146,6 +1987,7 @@ namespace nana{ namespace widgets points_.caret.x ++; + _m_reset_content_size(); if (!refresh) { @@ -2155,8 +1997,6 @@ namespace nana{ namespace widgets else impl_->try_refresh = sync_graph::refresh; - _m_reset_content_size(); - points_.xpos = points_.caret.x; } void text_editor::copy() const @@ -2208,11 +2048,9 @@ namespace nana{ namespace widgets return; auto undo_ptr = std::unique_ptr(new undo_input_text(*this, std::wstring(1, '\n'))); - bool need_refresh = (select_.a != select_.b); undo_ptr->set_selected_text(); - if(need_refresh) - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(); undo_ptr->set_caret_pos(); @@ -2244,12 +2082,7 @@ namespace nana{ namespace widgets points_.caret.x = 0; auto origin = impl_->cview->origin(); - if (origin.x || (points_.caret.y < textbase.lines()) || textbase.getline(points_.caret.y).size()) - { - origin.x = -origin.x; - impl_->cview->move_origin(origin); - need_refresh = true; - } + origin.x = 0; if (impl_->indent.enabled) { @@ -2267,37 +2100,33 @@ namespace nana{ namespace widgets put(text); } } + else + _m_reset_content_size(); - impl_->cview->move_origin(origin - impl_->cview->origin()); - if (impl_->capacities.behavior->adjust_caret_into_screen() || need_refresh) + auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin()); + + if (this->_m_adjust_view() || origin_moved) impl_->cview->sync(true); - - _m_reset_content_size(); } void text_editor::del() { - bool has_erase = true; - if(select_.a == select_.b) { if(textbase().getline(points_.caret.y).size() > points_.caret.x) { ++points_.caret.x; } - else if(points_.caret.y + 1 < textbase().lines()) + else if (points_.caret.y + 1 < textbase().lines()) { //Move to next line points_.caret.x = 0; - ++ points_.caret.y; + ++points_.caret.y; } else - has_erase = false; //No characters behind the caret + return; //No characters behind the caret } - if(has_erase) backspace(); - - _m_reset_content_size(); - points_.xpos = points_.caret.x; + backspace(); } void text_editor::backspace(bool record_undo) @@ -2320,7 +2149,7 @@ namespace nana{ namespace widgets textbase.erase(points_.caret.y, points_.caret.x, erase_number); _m_pre_calc_lines(points_.caret.y, 1); - if(_m_move_offset_x_while_over_border(-2) == false) + if (!this->_m_adjust_view()) { _m_update_line(points_.caret.y, secondary); has_to_redraw = false; @@ -2339,8 +2168,6 @@ namespace nana{ namespace widgets } else { - undo_ptr->set_removed(this->_m_make_select_string()); - undo_ptr->set_selected_text(); points_.caret = _m_erase_select(); undo_ptr->set_caret_pos(); @@ -2349,11 +2176,11 @@ namespace nana{ namespace widgets if (record_undo) impl_->undo.push(std::move(undo_ptr)); - _m_reset_content_size(has_to_redraw); + _m_reset_content_size(false); if(has_to_redraw) { - impl_->capacities.behavior->adjust_caret_into_screen(); + this->_m_adjust_view(); impl_->try_refresh = sync_graph::refresh; } } @@ -2367,7 +2194,7 @@ namespace nana{ namespace widgets _m_reset_content_size(true); - impl_->capacities.behavior->adjust_caret_into_screen(); + this->_m_adjust_view(); impl_->try_refresh = sync_graph::refresh; } @@ -2391,10 +2218,8 @@ namespace nana{ namespace widgets if(points_.caret.x) { --points_.caret.x; - pending = false; - bool adjust_y = (attributes_.line_wrapped && impl_->capacities.behavior->adjust_caret_into_screen()); - if (_m_move_offset_x_while_over_border(-2) || adjust_y) + if (this->_m_adjust_view()) impl_->try_refresh = sync_graph::refresh; } else if (points_.caret.y) //Move to previous line @@ -2403,39 +2228,33 @@ namespace nana{ namespace widgets pending = false; } - if (pending && impl_->capacities.behavior->adjust_caret_into_screen()) + if (pending && this->_m_adjust_view()) impl_->try_refresh = sync_graph::refresh; - - points_.xpos = points_.caret.x; } void text_editor::move_right() { bool do_render = false; - if(_m_cancel_select(2) == false) + if (_m_cancel_select(2) == false) { auto lnstr = textbase().getline(points_.caret.y); - if(lnstr.size() > points_.caret.x) + if (lnstr.size() > points_.caret.x) { ++points_.caret.x; - - bool adjust_y = (attributes_.line_wrapped && impl_->capacities.behavior->adjust_caret_into_screen()); - do_render = (_m_move_offset_x_while_over_border(2) || adjust_y); + do_render = this->_m_adjust_view(); } - else if(points_.caret.y + 1 < textbase().lines()) + else if (points_.caret.y + 1 < textbase().lines()) { //Move to next line points_.caret.x = 0; - ++ points_.caret.y; - do_render = impl_->capacities.behavior->adjust_caret_into_screen(); + ++points_.caret.y; + do_render = this->_m_adjust_view(); } } else - do_render = impl_->capacities.behavior->adjust_caret_into_screen(); + do_render = this->_m_adjust_view(); if (do_render) impl_->try_refresh = sync_graph::refresh; - - points_.xpos = points_.caret.x; } void text_editor::_m_handle_move_key(const arg_keyboard& arg) @@ -2444,155 +2263,122 @@ namespace nana{ namespace widgets select_.a = select_.b = points_.caret; auto origin = impl_->cview->origin(); - origin.y = _m_text_topline(); + auto pos = points_.caret; + auto coord = _m_caret_to_coordinate(points_.caret, false); - bool changed = false; - nana::upoint caret = points_.caret; wchar_t key = arg.key; - size_t nlines = textbase().lines(); - if (arg.ctrl) { - switch (key) { - case keyboard::os_arrow_left: - case keyboard::os_arrow_right: - // TODO: move the caret word by word - break; - case keyboard::os_home: - if (caret.y != 0) { - caret.y = 0; - origin.y = 0; - changed = true; - } - break; - case keyboard::os_end: - if (caret.y != nlines - 1) { - caret.y = static_cast(nlines - 1); - changed = true; - } - break; - } - } - size_t lnsz = textbase().getline(caret.y).size(); + + auto const line_px = this->line_height(); + + //The number of text lines + auto const line_count = textbase().lines(); + + //The number of charecters in the line of caret + auto const text_length = textbase().getline(points_.caret.y).size(); + + switch (key) { case keyboard::os_arrow_left: if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) { - caret = select_.a; - changed = true; + pos = select_.a; } - else + else if (pos.x != 0) { - if (caret.x != 0) { - --caret.x; - changed = true; - } - else { - if (caret.y != 0) { - --caret.y; - caret.x = static_cast(textbase().getline(caret.y).size()); - changed = true; - } - } + --pos.x; + } + else if (pos.y != 0) { + --pos.y; + pos.x = static_cast(textbase().getline(pos.y).size()); } break; case keyboard::os_arrow_right: if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) { - caret = select_.b; - changed = true; + pos = select_.b; } - else + else if (pos.x < text_length) { - if (caret.x < lnsz) { - ++caret.x; - changed = true; - } - else { - if (caret.y != nlines - 1) { - ++caret.y; - caret.x = 0; - changed = true; - } - } + ++pos.x; + } + else if (pos.y != line_count - 1) + { + ++pos.y; + pos.x = 0; } break; case keyboard::os_arrow_up: + coord.y -= static_cast(line_px); + break; case keyboard::os_arrow_down: - { - auto screen_pt = _m_caret_to_screen(caret); - int offset = line_height(); - if (key == keyboard::os_arrow_up) { - offset = -offset; - } - screen_pt.y += offset; - - auto const new_caret = _m_screen_to_caret(screen_pt); - if (new_caret != caret) { - caret = new_caret; - if (screen_pt.y < 0) { - scroll(true, true); - } - changed = true; - } - } + coord.y += static_cast(line_px); break; case keyboard::os_home: - if (caret.x != 0) { - caret.x = 0; - changed = true; - } + //move the caret to the begining of the line + pos.x = 0; + + //move the caret to the begining of the text if Ctrl is pressed + if (arg.ctrl) + pos.y = 0; break; case keyboard::os_end: - if (caret.x < lnsz) { - caret.x = static_cast(lnsz); - changed = true; - } + //move the caret to the end of the line + pos.x = static_cast(text_length); + + //move the caret to the end of the text if Ctrl is pressed + if(arg.ctrl) + pos.y = static_cast((line_count - 1) * line_px); break; case keyboard::os_pageup: - if (caret.y >= screen_lines() && origin.y >= static_cast(screen_lines())) { - origin.y -= screen_lines(); - caret.y -= screen_lines(); - changed = true; + if(origin.y > 0) + { + auto off = coord - origin; + origin.y -= (std::min)(origin.y, static_cast(impl_->cview->view_area().height)); + coord = off + origin; } break; case keyboard::os_pagedown: - if (caret.y + screen_lines() <= impl_->capacities.behavior->take_lines()) { - origin.y += static_cast(screen_lines()); - caret.y += screen_lines(); - changed = true; + if (impl_->cview->content_size().height > impl_->cview->view_area().height) + { + auto off = coord - origin; + origin.y = (std::min)(origin.y + static_cast(impl_->cview->view_area().height), static_cast(impl_->cview->content_size().height - impl_->cview->view_area().height)); + coord = off + origin; } break; } - if (select_.a != caret || select_.b != caret) { - changed = true; + + if (pos == points_.caret) + { + impl_->cview->move_origin(origin - impl_->cview->origin()); + pos = _m_coordinate_to_caret(coord, false); } - if (changed) { + if (pos != points_.caret) { if (arg.shift) { switch (key) { case keyboard::os_arrow_left: case keyboard::os_arrow_up: case keyboard::os_home: case keyboard::os_pageup: - select_.b = caret; + select_.b = pos; break; case keyboard::os_arrow_right: case keyboard::os_arrow_down: case keyboard::os_end: case keyboard::os_pagedown: - select_.b = caret; + select_.b = pos; break; } - }else { - select_.b = caret; - select_.a = caret; } - points_.caret = caret; - - origin.y *= line_height(); - impl_->cview->move_origin(origin - impl_->cview->origin()); - impl_->cview->sync(true); - points_.xpos = points_.caret.x; + else { + select_.b = pos; + select_.a = pos; + } + points_.caret = pos; impl_->try_refresh = sync_graph::refresh; + this->_m_adjust_view(); + impl_->cview->sync(true); + this->reset_caret(); } } @@ -2633,9 +2419,9 @@ namespace nana{ namespace widgets const upoint& text_editor::mouse_caret(const point& scrpos, bool stay_in_view) //From screen position { - points_.caret = _m_screen_to_caret(scrpos); + points_.caret = _m_coordinate_to_caret(scrpos); - if (stay_in_view && impl_->capacities.behavior->adjust_caret_into_screen()) + if (stay_in_view && this->_m_adjust_view()) impl_->try_refresh = sync_graph::refresh; move_caret(points_.caret); @@ -2649,7 +2435,7 @@ namespace nana{ namespace widgets point text_editor::caret_screen_pos() const { - return _m_caret_to_screen(points_.caret); + return _m_caret_to_coordinate(points_.caret); } bool text_editor::scroll(bool upwards, bool vert) @@ -2667,7 +2453,7 @@ namespace nana{ namespace widgets { auto const height = line_height(); - auto top = _m_caret_to_screen(upoint{ 0, static_cast(row.first) }).y; + auto top = _m_caret_to_coordinate(upoint{ 0, static_cast(row.first) }).y; std::size_t lines = 1; if (whole_line) @@ -2751,7 +2537,7 @@ namespace nana{ namespace widgets this->impl_->capacities.behavior->pre_calc_line(pos, width_px); } - nana::point text_editor::_m_caret_to_screen(nana::upoint pos) const + nana::point text_editor::_m_caret_to_coordinate(nana::upoint pos, bool to_screen_coordinate) const { auto const behavior = impl_->capacities.behavior; auto const sections = behavior->line(pos.y); @@ -2759,7 +2545,7 @@ namespace nana{ namespace widgets std::size_t lines = 0; //lines before the caret line; for (std::size_t i = 0; i < pos.y; ++i) { - lines += behavior->line(i).size(); + lines += behavior->take_lines(i); } const text_section * sct_ptr = nullptr; @@ -2767,6 +2553,8 @@ namespace nana{ namespace widgets if (0 != pos.x) { std::wstring str; + + std::size_t sct_pos = 0; for (auto & sct : sections) { std::size_t chsize = sct.end - sct.begin; @@ -2776,7 +2564,9 @@ namespace nana{ namespace widgets else str.append(sct.begin, sct.end); - if (pos.x <= chsize) + //In line-wrapped mode. If the caret is at the end of a line which is not the last section, + //the caret should be moved to the beginning of next section line. + if ((sct_pos + 1 < sections.size()) ? (pos.x < chsize) : (pos.x <= chsize)) { sct_ptr = &sct; if (pos.x == chsize) @@ -2790,6 +2580,8 @@ namespace nana{ namespace widgets pos.x -= static_cast(chsize); ++lines; } + + ++sct_pos; } } @@ -2803,13 +2595,26 @@ namespace nana{ namespace widgets else scrpos.x += _m_text_x(*sct_ptr); - scrpos.y = static_cast(lines * line_height()) - impl_->cview->origin().y + this->_m_text_top_base(); + scrpos.y = static_cast(lines * line_height()); + + if (!to_screen_coordinate) + { + //_m_text_x includes origin x and text_area x. remove these factors + scrpos.x += (impl_->cview->origin().x - text_area_.area.x); + } + else + scrpos.y += this->_m_text_top_base() - impl_->cview->origin().y; + return scrpos; } - upoint text_editor::_m_screen_to_caret(point scrpos) const + upoint text_editor::_m_coordinate_to_caret(point scrpos, bool from_screen_coordinate) const { + if (!from_screen_coordinate) + scrpos -= (impl_->cview->origin() - point{ text_area_.area.x, this->_m_text_top_base() }); + auto const behavior = impl_->capacities.behavior; + auto const row = behavior->text_position_from_screen(scrpos.y); auto sections = behavior->line(row.first); @@ -2829,27 +2634,28 @@ namespace nana{ namespace widgets text_ptr = mask_str.c_str(); } - std::vector reordered; - unicode_bidi{}.linestr(text_ptr, text_size, reordered); + auto const reordered = unicode_reorder(text_ptr, text_size); nana::upoint res(static_cast(real_str.begin - sections.front().begin), static_cast(row.first)); - scrpos.x -= _m_text_x(sections[row.second]); - if (scrpos.x < 0) - scrpos.x = 0; + scrpos.x = (std::max)(0, (scrpos.x - _m_text_x(sections[row.second]))); for (auto & ent : reordered) { auto str_px = static_cast(_m_text_extent_size(ent.begin, ent.end - ent.begin).width); if (scrpos.x <= str_px) { - res.x += _m_char_by_pixels(ent, scrpos.x); - res.x += static_cast(ent.begin - text_ptr); + res.x += _m_char_by_pixels(ent, scrpos.x) + static_cast(ent.begin - text_ptr); return res; } scrpos.x -= str_px; } - res.x = static_cast(textbase().getline(res.y).size()); + + //move the caret to the end of this section. + res.x = static_cast(text_size); + for (std::size_t i = 0; i < row.second; ++i) + res.x += static_cast(sections[i].end - sections[i].begin); + return res; } @@ -2939,7 +2745,7 @@ namespace nana{ namespace widgets } _m_pos_from_secondary(points_.caret.y, secondary_pos, points_.caret.x); - return behavior->adjust_caret_into_screen(); + return this->_m_adjust_view(); } void text_editor::_m_update_line(std::size_t pos, std::size_t secondary_count_before) @@ -2947,7 +2753,7 @@ namespace nana{ namespace widgets auto behavior = impl_->capacities.behavior; if (behavior->take_lines(pos) == secondary_count_before) { - auto top = _m_caret_to_screen(upoint{ 0, static_cast(pos) }).y; + auto top = _m_caret_to_coordinate(upoint{ 0, static_cast(pos) }).y; const unsigned pixels = line_height(); const rectangle update_area = { text_area_.area.x, top, width_pixels(), static_cast(pixels * secondary_count_before) }; @@ -3010,12 +2816,12 @@ namespace nana{ namespace widgets if (this->attributes_.line_wrapped) { + //detect if vertical scrollbar is required + auto const max_lines = screen_lines(true); + if (calc_lines) { - //detect if vertical scrollbar is required - auto const max_lines = screen_lines(true); auto text_lines = textbase().lines(); - if (text_lines <= max_lines) { std::size_t lines = 0; @@ -3034,19 +2840,20 @@ namespace nana{ namespace widgets //enable vertical scrollbar when text_lines > max_lines csize.width = _m_width_px(text_lines <= max_lines); - impl_->capacities.behavior->pre_calc_lines(csize.width); } else + { csize.width = impl_->cview->content_size().width; + } } else { if (calc_lines) - impl_->capacities.behavior->pre_calc_lines(0); + impl_->capacities.behavior->pre_calc_lines(width_pixels()); auto maxline = textbase().max_line(); - csize.width = _m_text_extent_size(textbase().getline(maxline.first).c_str(), maxline.second).width; + csize.width = _m_text_extent_size(textbase().getline(maxline.first).c_str(), maxline.second).width + caret_size().width; } csize.height = static_cast(impl_->capacities.behavior->take_lines() * line_height()); @@ -3138,11 +2945,12 @@ namespace nana{ namespace widgets std::wstring text_editor::_m_make_select_string() const { std::wstring text; - + nana::upoint a, b; if (get_selected_points(a, b)) { auto & textbase = this->textbase(); + if (a.y != b.y) { text = textbase.getline(a.y).substr(a.x); @@ -3155,7 +2963,7 @@ namespace nana{ namespace widgets text += textbase.getline(b.y).substr(0, b.x); } else - text = textbase.getline(a.y).substr(a.x, b.x - a.x); + return textbase.getline(a.y).substr(a.x, b.x - a.x); } return text; @@ -3230,20 +3038,16 @@ namespace nana{ namespace widgets bool text_editor::_m_cancel_select(int align) { - nana::upoint a, b; + upoint a, b; if (get_selected_points(a, b)) { - switch(align) + //domain of algin = [0, 2] + if (align) { - case 1: - points_.caret = a; - _m_move_offset_x_while_over_border(-2); - break; - case 2: - points_.caret = b; - _m_move_offset_x_while_over_border(2); - break; + this->points_.caret = (1 == align ? a : b); + this->_m_adjust_view(); } + select_.a = select_.b = points_.caret; reset_caret(); return true; @@ -3251,15 +3055,6 @@ namespace nana{ namespace widgets return false; } - unsigned text_editor::_m_tabs_pixels(size_type tabs) const - { - if(0 == tabs) return 0; - - wchar_t ws[2] = {}; - ws[0] = mask_char_ ? mask_char_ : ' '; - return static_cast(tabs * graph_.text_extent_size(ws).width * text_area_.tab_space); - } - nana::size text_editor::_m_text_extent_size(const char_type* str, size_type n) const { if(mask_char_) @@ -3271,50 +3066,60 @@ namespace nana{ namespace widgets return graph_.text_extent_size(str, static_cast(n)); } - //_m_move_offset_x_while_over_border - //@brief: Move the view window - bool text_editor::_m_move_offset_x_while_over_border(int many) + bool text_editor::_m_adjust_view() { - //x never beyonds border in line-wrapped mode. - if (attributes_.line_wrapped || (0 == many)) + auto const view_area = impl_->cview->view_area(); + + auto const line_px = static_cast(this->line_height()); + auto coord = _m_caret_to_coordinate(points_.caret, true); + + if (view_area.is_hit(coord) && view_area.is_hit({coord.x, coord.y + line_px})) return false; - const string_type& lnstr = textbase().getline(points_.caret.y); - unsigned width = _m_text_extent_size(lnstr.c_str(), points_.caret.x).width; + unsigned extra_count_horz = 4; + unsigned extra_count_vert = 0; - const auto count = static_cast(std::abs(many)); - if(many < 0) + auto const origin = impl_->cview->origin(); + coord = _m_caret_to_coordinate(points_.caret, false); + + point moved_origin; + + //adjust x-axis if it isn't line-wrapped mode + if (!attributes_.line_wrapped) { - auto origin = impl_->cview->origin(); + auto extra = points_.caret; - if (origin.x && (origin.x >= static_cast(width))) - { //Out of screen text area - if (points_.caret.x > count) - origin.x = static_cast(width - _m_text_extent_size(lnstr.c_str() + points_.caret.x - count, count).width); - else - origin.x = 0; - - impl_->cview->move_origin(origin - impl_->cview->origin()); - return true; + if (coord.x < origin.x) + { + extra.x -= (std::min)(extra_count_horz, points_.caret.x); + moved_origin.x = _m_caret_to_coordinate(extra, false).x - origin.x; + } + else if (coord.x + static_cast(caret_size().width) >= origin.x + static_cast(view_area.width)) + { + extra.x = (std::min)(static_cast(textbase().getline(points_.caret.y).size()), points_.caret.x + extra_count_horz); + auto new_origin = _m_caret_to_coordinate(extra, false).x + static_cast(caret_size().width) - static_cast(view_area.width); + moved_origin.x = new_origin - origin.x; } } - else + + auto extra_px = static_cast(line_px * extra_count_vert); + + if (coord.y < origin.y) { - auto const right_pos = impl_->cview->view_area().right(); - width += text_area_.area.x; + //Top of caret is less than the top of view - auto origin = impl_->cview->origin(); - if (static_cast(width) - origin.x >= right_pos) - { //Out of screen text area - origin.x = static_cast(width) - right_pos + 1; - auto rest_size = lnstr.size() - points_.caret.x; - origin.x += static_cast(_m_text_extent_size(lnstr.c_str() + points_.caret.x, (rest_size >= static_cast(many) ? static_cast(many) : rest_size)).width); - - impl_->cview->move_origin(origin - impl_->cview->origin()); - return true; - } + moved_origin.y = (std::max)(0, coord.y - extra_px) - origin.y; } - return false; + else if (coord.y + line_px >= origin.y + static_cast(view_area.height)) + { + //Bottom of caret is greater than the bottom of view + + auto const bottom = static_cast(impl_->capacities.behavior->take_lines() * line_px); + auto new_origin = (std::min)(coord.y + line_px + extra_px, bottom) - static_cast(view_area.height); + moved_origin.y = new_origin - origin.y; + } + + return impl_->cview->move_origin(moved_origin); } bool text_editor::_m_move_select(bool record_undo) @@ -3531,8 +3336,7 @@ namespace nana{ namespace widgets const auto focused = API::is_focus_ready(window_); - std::vector reordered; - unicode_bidi{}.linestr(text_ptr, text_len, reordered); + auto const reordered = unicode_reorder(text_ptr, text_len); //Parse highlight keywords keyword_parser parser; @@ -3696,14 +3500,19 @@ namespace nana{ namespace widgets bool text_editor::_m_update_caret_line(std::size_t secondary_before) { - if (false == impl_->capacities.behavior->adjust_caret_into_screen()) + if (false == this->_m_adjust_view()) { - if (_m_caret_to_screen(points_.caret).x < impl_->cview->view_area().right()) + if (_m_caret_to_coordinate(points_.caret).x < impl_->cview->view_area().right()) { _m_update_line(points_.caret.y, secondary_before); return false; } } + else + { + //The content view is adjusted, now syncs it with active mode to avoid updating. + impl_->cview->sync(false); + } impl_->try_refresh = sync_graph::refresh; return true; } @@ -3757,9 +3566,8 @@ namespace nana{ namespace widgets if (pos > lnstr.size()) return 0; - std::vector reordered; - unicode_bidi{}.linestr(lnstr.c_str(), lnstr.size(), reordered); - + auto const reordered = unicode_reorder(lnstr.c_str(), lnstr.size()); + auto target = lnstr.c_str() + pos; unsigned text_w = 0; @@ -3789,4 +3597,3 @@ namespace nana{ namespace widgets }//end namespace skeletons }//end namespace widgets }//end namespace nana - diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 87cec724..2cb89870 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -441,7 +441,10 @@ namespace nana text = to_wstring(modifier_.prefix + range_->value() + modifier_.suffix); if (editor_->text() != text) + { editor_->text(text, false); + editor_->try_refresh(); + } _m_draw_spins(spin_stated_); } @@ -736,7 +739,8 @@ namespace nana if (editor) { editor->text(to_wstring(text), false); - API::refresh_window(*this); + if (editor->try_refresh()) + API::update_window(*this); } } }//end namespace nana diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 909cde92..1be973ea 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -720,6 +720,26 @@ namespace drawerbase { editor->set_undo_queue_length(len); } + std::size_t textbox::display_line_count() const noexcept + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + return editor->line_count(false); + + return 0; + } + + std::size_t textbox::text_line_count() const noexcept + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + return editor->line_count(true); + + return 0; + } + //Override _m_caption for caption() auto textbox::_m_caption() const throw() -> native_string_type { @@ -738,7 +758,9 @@ namespace drawerbase { if (editor) { editor->text(to_wstring(str), false); - API::update_window(this->handle()); + + if (editor->try_refresh()) + API::update_window(this->handle()); } } diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 54c94f94..c84e66d2 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -2200,7 +2200,7 @@ namespace nana } path.insert(0, pnode->value.first); - return std::move(path); + return path; } return{}; } diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index b3bedd40..21aa4948 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -509,9 +509,7 @@ namespace paint nana::size sz; if(impl_->handle && impl_->handle->context && str.size()) { - std::vector reordered; - unicode_bidi bidi; - bidi.linestr(str.c_str(), str.size(), reordered); + auto const reordered = unicode_reorder(str.c_str(), str.size()); for(auto & i: reordered) { nana::size t = text_extent_size(i.begin, i.end - i.begin); @@ -958,9 +956,8 @@ namespace paint unsigned graphics::bidi_string(const nana::point& pos, const wchar_t * str, std::size_t len) { auto moved_pos = pos; - unicode_bidi bidi; - std::vector reordered; - bidi.linestr(str, len, reordered); + + auto const reordered = unicode_reorder(str, len); for (auto & i : reordered) { string(moved_pos, i.begin, i.end - i.begin); @@ -1208,7 +1205,8 @@ namespace paint unsigned last_color = (int(r) << 16) | (int(g) << 8) | int(b); Display * disp = nana::detail::platform_spec::instance().open_display(); - impl_->handle->fgcolor(static_cast(last_color)); + impl_->handle->set_color(static_cast(last_color)); + impl_->handle->update_color(); const int endpos = deltapx + (vertical ? rct.y : rct.x); if (endpos > 0) { @@ -1223,7 +1221,8 @@ namespace paint if (new_color != last_color) { last_color = new_color; - impl_->handle->fgcolor(static_cast(last_color)); + impl_->handle->set_color(static_cast(last_color)); + impl_->handle->update_color(); } } } @@ -1238,7 +1237,8 @@ namespace paint if (new_color != last_color) { last_color = new_color; - impl_->handle->fgcolor(static_cast(last_color)); + impl_->handle->set_color(static_cast(last_color)); + impl_->handle->update_color(); } } } diff --git a/source/paint/text_renderer.cpp b/source/paint/text_renderer.cpp index af206d40..07b71e0f 100644 --- a/source/paint/text_renderer.cpp +++ b/source/paint/text_renderer.cpp @@ -13,26 +13,23 @@ namespace nana template void for_each_line(const wchar_t * str, std::size_t len, int top, F & f) { - auto head = str; - auto end = str + len; + auto const end = str + len; for(auto i = str; i != end; ++i) { if(*i == '\n') { - top += static_cast(f(top, head, i - head)); - head = i + 1; + top += static_cast(f(top, str, i - str)); + str = i + 1; } } - if(head != end) - f(top, head, end - head); + if(str != end) + f(top, str, end - str); } struct draw_string { drawable_type dw; const int x, endpos; - nana::unicode_bidi bidi; - std::vector reordered; align text_align; draw_string(drawable_type dw, int x, int endpos, align ta) @@ -41,9 +38,13 @@ namespace nana unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) { + auto const reordered = unicode_reorder(buf, bufsize); + if (reordered.empty()) + return 0; + nana::point pos{ x, top }; unsigned pixels = 0; - bidi.linestr(buf, bufsize, reordered); + switch(text_align) { case align::left: @@ -64,23 +65,28 @@ namespace nana case align::center: { unsigned lenpx = 0; - std::vector widths; + std::unique_ptr entity_pxs(new unsigned[reordered.size()]); + + auto ent_px = entity_pxs.get(); + for(auto & ent : reordered) { auto ts = detail::text_extent_size(dw, ent.begin, ent.end - ent.begin); if(ts.height > pixels) pixels = ts.height; lenpx += ts.width; - widths.emplace_back(ts.width); + *ent_px++ = ts.width; } pos.x += (endpos - pos.x - static_cast(lenpx))/2; - auto ipx = widths.begin(); + ent_px = entity_pxs.get(); + for(auto & ent : reordered) { - if(pos.x + static_cast(*ipx) > 0) + if (pos.x + static_cast(*ent_px) > 0) detail::draw_string(dw, pos, ent.begin, ent.end - ent.begin); - pos.x += static_cast(*ipx); + pos.x += static_cast(*ent_px++); + if(pos.x >= endpos) break; } @@ -90,7 +96,7 @@ namespace nana { int xend = endpos; std::swap(pos.x, xend); - for(auto i = reordered.rbegin(), end = reordered.rend(); i != end; ++i) + for(auto i = reordered.crbegin(); i != reordered.crend(); ++i) { auto & ent = *i; std::size_t len = ent.end - ent.begin; @@ -118,13 +124,11 @@ namespace nana graphics & graph; int x, endpos; unsigned omitted_pixels; - nana::unicode_bidi bidi; - std::vector reordered; draw_string_omitted(graphics& graph, int x, int endpos, bool omitted) : graph(graph), x(x), endpos(endpos) { - omitted_pixels = (omitted ? graph.text_extent_size(L"...", 3).width : 0); + omitted_pixels = (omitted ? graph.text_extent_size("...", 3).width : 0); if (endpos - x > static_cast(omitted_pixels)) this->endpos -= omitted_pixels; else @@ -136,7 +140,9 @@ namespace nana drawable_type dw = graph.handle(); ::nana::point pos{ x, top }; unsigned pixels = 0; - bidi.linestr(buf, bufsize, reordered); + + auto const reordered = unicode_reorder(buf, bufsize); + for(auto & i : reordered) { std::size_t len = i.end - i.begin; @@ -178,9 +184,6 @@ namespace nana { graphics & graph; int x, endpos; - nana::unicode_bidi bidi; - std::vector reordered; - std::vector ts_keeper; align text_align; draw_string_auto_changing_lines(graphics& graph, int x, int endpos, align ta) @@ -191,10 +194,13 @@ namespace nana { unsigned pixels = 0; - drawable_type dw = graph.handle(); + auto const dw = graph.handle(); unsigned str_w = 0; - ts_keeper.clear(); - bidi.linestr(buf, bufsize, reordered); + + std::vector ts_keeper; + + auto const reordered = unicode_reorder(buf, bufsize); + for(auto & i : reordered) { nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin); @@ -213,28 +219,33 @@ namespace nana auto i_ts_keeper = ts_keeper.cbegin(); for(auto & i : reordered) { - if(line_pixels < i_ts_keeper->height) + if(line_pixels < i_ts_keeper->height) line_pixels = i_ts_keeper->height; bool beyond_edge = (pos.x + static_cast(i_ts_keeper->width) > endpos); if(beyond_edge) { - std::size_t len = i.end - i.begin; + const std::size_t len = i.end - i.begin; if(len > 1) { - unsigned * pxbuf = new unsigned[len]; + std::unique_ptr pixel_buf(new unsigned[len]); + //Find the char that should be splitted - graph.glyph_pixels(i.begin, len, pxbuf); + graph.glyph_pixels(i.begin, len, pixel_buf.get()); std::size_t idx_head = 0, idx_splitted; do { + auto pxbuf = pixel_buf.get(); + idx_splitted = find_splitted(idx_head, len, pos.x, endpos, pxbuf); if(idx_splitted == len) { detail::draw_string(dw, pos, i.begin + idx_head, idx_splitted - idx_head); + for(std::size_t i = idx_head; i < len; ++i) pos.x += static_cast(pxbuf[i]); + break; } //Check the word whether it is splittable. @@ -243,7 +254,7 @@ namespace nana detail::draw_string(dw, pos, i.begin + idx_head, idx_splitted - idx_head); idx_head = idx_splitted; pos.x = x; - pos.y += line_pixels; + pos.y += static_cast(line_pixels); line_pixels = i_ts_keeper->height; } else @@ -283,18 +294,16 @@ namespace nana for(std::size_t k = idx_head; k < idx_head + splen; ++k) pos.x += static_cast(pxbuf[k]); + if (pos.x >= endpos) { pos.x = x; pos.y += static_cast(line_pixels); - line_pixels = i_ts_keeper->height; } idx_head += splen; } } }while(idx_head < len); - - delete [] pxbuf; } else { @@ -340,7 +349,7 @@ namespace nana { point pos{ endpos, top }; auto i_ts_keeper = ts_keeper.crbegin(); - for(auto i = reordered.crbegin(), end = reordered.crend(); i != end; ++i) + for(auto i = reordered.crbegin(); i != reordered.crend(); ++i) { auto & ent = *i; std::size_t len = ent.end - ent.begin; @@ -397,9 +406,6 @@ namespace nana { graphics & graph; int x, endpos; - nana::unicode_bidi bidi; - std::vector reordered; - std::vector ts_keeper; unsigned extents; extent_auto_changing_lines(graphics& graph, int x, int endpos) @@ -412,8 +418,10 @@ namespace nana drawable_type dw = graph.handle(); unsigned str_w = 0; - ts_keeper.clear(); - bidi.linestr(buf, bufsize, reordered); + std::vector ts_keeper; + + auto const reordered = unicode_reorder(buf, bufsize); + for(auto & i : reordered) { nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin); @@ -503,7 +511,6 @@ namespace nana { xpos = x; top += line_pixels; - line_pixels = i_ts_keeper->height; } idx_head += splen; } @@ -645,11 +652,9 @@ namespace nana substr_px += *p; } while (p != end); - pos.x += static_cast(width - ellipsis - substr_px); - - graph_.string(pos, "..."); - pos.x += ellipsis; + pos.x += static_cast(width - ellipsis - substr_px) + ellipsis; graph_.bidi_string(pos, text.c_str() + substr_len, text.size() - substr_len); + pos.x -= ellipsis; } else { @@ -669,8 +674,9 @@ namespace nana graph_.bidi_string(pos, text.c_str(), substr_len); pos.x += substr_px; - graph_.string(pos, "..."); } + + graph_.string(pos, "..."); } //end class string diff --git a/source/paint/truetype.hpp b/source/paint/truetype.hpp index bd44c0db..5b94cd34 100644 --- a/source/paint/truetype.hpp +++ b/source/paint/truetype.hpp @@ -66,7 +66,7 @@ namespace nana if (ifs.read(reinterpret_cast(&table_directory), sizeof table_directory).gcount() != sizeof table_directory) return; - if (reinterpret_cast("name") == reinterpret_cast(table_directory.name)) + if (*reinterpret_cast("name") == reinterpret_cast(table_directory.name)) { //const std::size_t length = _m_swap(table_directory.length); const std::size_t directory_offset = _m_swap(table_directory.offset); diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index 3da7d2f0..15b77d12 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -276,8 +276,7 @@ namespace nana if(ch <= 0x1CC7) return L; //N = 141 if(0x1CD3 == ch || 0x1CE1 == ch) return L; if(ch <= 0x1CE8) return NSM; //N = 7 - if(0x1CED == ch) return NSM; - if(0x1CF4 == ch) return NSM; + if(0x1CED == ch || 0x1CF4 == ch) return NSM; if(ch <= 0x1DBF) return L; //N = 203 if(ch <= 0x1DFF) return NSM; //N = 64 if(ch <= 0x1FBC) return L; //N = 445 @@ -504,21 +503,21 @@ namespace nana } //class unicode_bidi - void unicode_bidi::linestr(const char_type* str, std::size_t len, std::vector & reordered) + std::vector unicode_bidi::reorder(const char_type* str, std::size_t len) { levels_.clear(); const char_type * const end = str + len; std::vector stack; - - remember cur = {0, directional_override_status::neutral}; + + remember cur = { 0, directional_override_status::neutral }; cur.level = _m_paragraph_level(str, end); //First character type bidi_char begin_char_type = {}; const char_type * begin_character = nullptr; - for(const char_type * c = str; c < end; ++c) + for (const char_type * c = str; c < end; ++c) { if (PDF == *c) { @@ -579,13 +578,17 @@ namespace nana begin_character = c; } } - if(begin_character) + if (begin_character) _m_push_entity(begin_character, end, cur.level, begin_char_type); _m_resolve_weak_types(); _m_resolve_neutral_types(); _m_resolve_implicit_levels(); + + std::vector reordered; _m_reordering_resolved_levels(reordered); + + return reordered; } unsigned unicode_bidi::_m_paragraph_level(const char_type * begin, const char_type * end) @@ -938,4 +941,9 @@ namespace nana return static_cast(static_cast(type - bidi_charmap::B) + 0x2000); } //end class unicode_bidi + + std::vector unicode_reorder(const wchar_t* text, std::size_t length) + { + return unicode_bidi{}.reorder(text, length); + } }//end namespace nana