diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 19e58978..a4eb16d8 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -90,6 +90,8 @@ namespace nana{ namespace widgets void set_keyword(const ::std::wstring& kw, const std::string& name, bool case_sensitive, bool whole_word_matched); void erase_keyword(const ::std::wstring& kw); + colored_area_access_interface& colored_area(); + void set_accept(std::function); void set_accept(accepts); bool respond_char(const arg_keyboard& arg); @@ -211,6 +213,7 @@ namespace nana{ namespace widgets bool try_refresh(); private: + nana::color _m_draw_colored_area(paint::graphics& graph, std::size_t line_pos); std::vector _m_render_text(const ::nana::color& text_color); void _m_pre_calc_lines(std::size_t line_off, std::size_t lines); diff --git a/include/nana/gui/widgets/skeletons/text_editor_part.hpp b/include/nana/gui/widgets/skeletons/text_editor_part.hpp index 1e3f6e4d..4e77a48f 100644 --- a/include/nana/gui/widgets/skeletons/text_editor_part.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor_part.hpp @@ -40,6 +40,29 @@ namespace nana virtual void text_exposed(const std::vector&) = 0; }; + + struct colored_area_type + { + const ::std::size_t begin; ///< The begin line position + ::std::size_t count; ///< The number of lines + + ::nana::color bgcolor; + ::nana::color fgcolor; + }; + + class colored_area_access_interface + { + public: + using colored_area_type = skeletons::colored_area_type; + + virtual ~colored_area_access_interface(); + + virtual std::shared_ptr get(std::size_t line_pos) = 0; + virtual bool clear() = 0; + virtual bool remove(std::size_t line_pos) = 0; + virtual std::size_t size() const = 0; + virtual std::shared_ptr at(std::size_t index) = 0; + }; } } } diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 8a439cd7..e37ed6fc 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -100,9 +100,12 @@ namespace nana :public widget_object { public: - using text_focus_behavior = widgets::skeletons::text_focus_behavior; + using colored_area_type = widgets::skeletons::colored_area_type; + using colored_area_access_interface = widgets::skeletons::colored_area_access_interface; + using text_focus_behavior = widgets::skeletons::text_focus_behavior; using text_positions = std::vector; + /// The default constructor without creating the widget. textbox(); @@ -134,6 +137,8 @@ namespace nana void store(std::string file); void store(std::string file, nana::unicode encoding); + colored_area_access_interface* colored_area_access(); + /// Enables/disables the textbox to indent a line. Idents a new line when it is created by pressing enter. /// @param generator generates text for identing a line. If it is empty, textbox indents the line according to last line. textbox& indention(bool, std::function generator = {}); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 168a4635..69b1d79e 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -15,8 +15,6 @@ #include #include #include - - #include #include "content_view.hpp" @@ -395,6 +393,91 @@ namespace nana{ namespace widgets lazy_refresh }; + colored_area_access_interface::~colored_area_access_interface(){} + + class colored_area_access + : public colored_area_access_interface + { + public: + void set_window(window handle) + { + window_handle_ = handle; + } + + std::shared_ptr find(std::size_t pos) const + { + for (auto i = colored_areas_.cbegin(); i != colored_areas_.cend(); ++i) + { + if (i->get()->begin <= pos && pos < i->get()->begin + i->get()->count) + return *i; + else if (i->get()->begin > pos) + break; + } + return{}; + } + public: + //Overrides methods of colored_area_access_interface + std::shared_ptr get(std::size_t line_pos) override + { + auto i = colored_areas_.cbegin(); + for (; i != colored_areas_.cend(); ++i) + { + auto & area = *(i->get()); + if (area.begin <= line_pos && line_pos < area.begin + area.count) + return *i; + + if (area.begin > line_pos) + break; + } + + auto iter = colored_areas_.emplace(i, std::make_shared(colored_area_type{line_pos})); + auto & area = *(iter->get()); + area.count = 1; + return *iter; + } + + bool clear() + { + if (colored_areas_.empty()) + return false; + + colored_areas_.clear(); + API::refresh_window(window_handle_); + return true; + } + + bool remove(std::size_t pos) override + { + bool changed = false; + for (auto i = colored_areas_.cbegin(); i != colored_areas_.cend();) + { + if (i->get()->begin <= pos && pos < i->get()->begin + i->get()->count) + { + i = colored_areas_.erase(i); + changed = true; + } + else if (i->get()->begin > pos) + break; + } + if (changed) + API::refresh_window(window_handle_); + + return changed; + } + + std::size_t size() const override + { + return colored_areas_.size(); + } + + std::shared_ptr at(std::size_t index) override + { + return colored_areas_.at(index); + } + private: + window window_handle_; + std::vector> colored_areas_; + }; struct text_editor::implementation { @@ -406,6 +489,8 @@ namespace nana{ namespace widgets sync_graph try_refresh{ sync_graph::none }; + colored_area_access colored_area; + struct inner_capacities { editor_behavior_interface * behavior; @@ -1202,6 +1287,11 @@ namespace nana{ namespace widgets } } } + + colored_area_access_interface& text_editor::colored_area() + { + return impl_->colored_area; + } void text_editor::set_accept(std::function pred) { @@ -2558,6 +2648,29 @@ namespace nana{ namespace widgets return false; } + color text_editor::_m_draw_colored_area(paint::graphics& graph, std::size_t line_pos) + { + auto area = impl_->colored_area.find(line_pos); + if (area) + { + if (!area->bgcolor.invisible()) + { + auto top = _m_caret_to_screen(upoint{ 0, static_cast(line_pos) }).y; + + const unsigned pixels = line_height(); + const rectangle area_r = { text_area_.area.x, top, width_pixels(), static_cast(pixels * impl_->capacities.behavior->take_lines(line_pos))}; + + if (API::is_transparent_background(this->window_)) + graph.blend(area_r, area->bgcolor, 1); + else + graph.rectangle(area_r, true, area->bgcolor); + } + + return area->fgcolor; + } + return{}; + } + std::vector text_editor::_m_render_text(const color& text_color) { std::vector line_indexes; @@ -2584,12 +2697,16 @@ namespace nana{ namespace widgets if (row.first >= line_count) break; + auto fgcolor = _m_draw_colored_area(graph_, row.first); + if (fgcolor.invisible()) + fgcolor = text_color; + sections = behavior->line(row.first); if (row.second < sections.size()) { auto const & sct = sections[row.second]; - _m_draw_string(top, text_color, str_pos, sct, true); + _m_draw_string(top, fgcolor, str_pos, sct, true); line_indexes.emplace_back(str_pos); ++row.second; if (row.second >= sections.size()) @@ -2817,8 +2934,14 @@ namespace nana{ namespace widgets const unsigned pixels = line_height(); const rectangle update_area = { text_area_.area.x, top, width_pixels(), static_cast(pixels * secondary_count_before) }; + if (!API::dev::copy_transparent_background(window_, update_area, graph_, update_area.position())) + { + _m_draw_colored_area(graph_, pos); graph_.rectangle(update_area, true, API::bgcolor(window_)); + } + else + _m_draw_colored_area(graph_, pos); auto fgcolor = API::fgcolor(window_); auto text_ptr = textbase().getline(pos).c_str(); @@ -3407,7 +3530,6 @@ namespace nana{ namespace widgets helper_pencil pencil(graph_, *this, parser); - graph_.palette(true, clr); graph_.palette(false, scheme_->selection.get_color()); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 6fc29494..909cde92 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -251,6 +251,15 @@ namespace drawerbase { editor->textbase().store(std::move(file), true, encoding); } + textbox::colored_area_access_interface* textbox::colored_area_access() + { + auto editor = get_drawer_trigger().editor(); + if (editor) + return &editor->colored_area(); + + return nullptr; + } + /// Enables/disables the textbox to indent a line. Idents a new line when it is created by pressing enter. /// @param generator generates text for identing a line. If it is empty, textbox indents the line according to last line. textbox& textbox::indention(bool enb, std::function generator)