highlight certain lines in a textbox(#194)

This commit is contained in:
Jinhao 2017-04-15 11:13:32 +08:00
parent 94bb4103f8
commit f261fa296e
5 changed files with 167 additions and 5 deletions

View File

@ -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<bool(char_type)>);
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<upoint> _m_render_text(const ::nana::color& text_color);
void _m_pre_calc_lines(std::size_t line_off, std::size_t lines);

View File

@ -40,6 +40,29 @@ namespace nana
virtual void text_exposed(const std::vector<upoint>&) = 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<colored_area_type> 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<colored_area_type> at(std::size_t index) = 0;
};
}
}
}

View File

@ -100,9 +100,12 @@ namespace nana
:public widget_object<category::widget_tag, drawerbase::textbox::drawer, drawerbase::textbox::textbox_events, ::nana::widgets::skeletons::text_editor_scheme>
{
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<upoint>;
/// 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<std::string()> generator = {});

View File

@ -15,8 +15,6 @@
#include <nana/gui/element.hpp>
#include <nana/system/dataexch.hpp>
#include <nana/unicode_bidi.hpp>
#include <nana/gui/widgets/widget.hpp>
#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<colored_area_type> 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<colored_area_type> 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>(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<colored_area_type> at(std::size_t index) override
{
return colored_areas_.at(index);
}
private:
window window_handle_;
std::vector<std::shared_ptr<colored_area_type>> 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<bool(char_type)> 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<unsigned>(line_pos) }).y;
const unsigned pixels = line_height();
const rectangle area_r = { text_area_.area.x, top, width_pixels(), static_cast<unsigned>(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<upoint> text_editor::_m_render_text(const color& text_color)
{
std::vector<upoint> 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<unsigned>(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());

View File

@ -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<std::string()> generator)