fix crash when drawing in text_changed event with new line inserted(#332)

This commit is contained in:
Jinhao 2018-08-26 16:42:53 +08:00
parent e8e7ad543c
commit e1992fb0d4
4 changed files with 101 additions and 63 deletions

View File

@ -191,14 +191,14 @@ namespace nana{ namespace widgets
void draw_corner(); void draw_corner();
void render(bool focused); void render(bool focused);
public: public:
void put(std::wstring); void put(std::wstring, bool perform_event);
void put(wchar_t); void put(wchar_t);
void copy() const; void copy() const;
void cut(); void cut();
void paste(); void paste();
void enter(bool record_undo = true); void enter(bool record_undo, bool perform_event);
void del(); void del();
void backspace(bool record_undo = true); void backspace(bool record_undo, bool perform_event);
void undo(bool reverse); void undo(bool reverse);
void set_undo_queue_length(std::size_t len); void set_undo_queue_length(std::size_t len);
void move_ns(bool to_north); //Moves up and down void move_ns(bool to_north); //Moves up and down
@ -243,9 +243,9 @@ namespace nana{ namespace widgets
void _m_reset(); void _m_reset();
//Inserts text at position where the caret is //Inserts text at position where the caret is
::nana::upoint _m_put(::std::wstring); ::nana::upoint _m_put(::std::wstring, bool perform_event);
::nana::upoint _m_erase_select(); ::nana::upoint _m_erase_select(bool perform_event);
::std::wstring _m_make_select_string() const; ::std::wstring _m_make_select_string() const;
static bool _m_resolve_text(const ::std::wstring&, std::vector<std::pair<std::size_t, std::size_t>> & lines); static bool _m_resolve_text(const ::std::wstring&, std::vector<std::pair<std::size_t, std::size_t>> & lines);

View File

@ -1,7 +1,7 @@
/* /*
* A textbase class implementation * A textbase class implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -301,6 +301,29 @@ namespace skeletons
} }
} }
//Triggers the text_changed event.
//It is exposed for outter classes. For a outter class(eg. text_editor), a changing text content operation
//may contains multiple textbase operations, therefore, the outter class determines when an event should be triggered.
//
//Addtional, using text_changed() method, it is possible to allow a outter class performing some updating operations
//before triggering text_changed event.
void text_changed()
{
if (!changed_)
{
_m_first_change();
changed_ = true;
}
if (edited_)
{
if (evt_agent_)
evt_agent_->text_changed();
edited_ = false;
}
}
size_type lines() const size_type lines() const
{ {
return text_cont_.size(); return text_cont_.size();
@ -330,7 +353,7 @@ namespace skeletons
_m_at(pos).swap(text); _m_at(pos).swap(text);
_m_make_max(pos); _m_make_max(pos);
_m_edited(); edited_ = true;
} }
void insert(upoint pos, string_type && str) void insert(upoint pos, string_type && str)
@ -351,7 +374,7 @@ namespace skeletons
} }
_m_make_max(pos.y); _m_make_max(pos.y);
_m_edited(); edited_ = true;
} }
void insertln(size_type pos, string_type&& str) void insertln(size_type pos, string_type&& str)
@ -362,7 +385,7 @@ namespace skeletons
text_cont_.emplace_back(new string_type(std::move(str))); text_cont_.emplace_back(new string_type(std::move(str)));
_m_make_max(pos); _m_make_max(pos);
_m_edited(); edited_ = true;
} }
void erase(size_type line, size_type pos, size_type count) void erase(size_type line, size_type pos, size_type count)
@ -378,7 +401,7 @@ namespace skeletons
if (attr_max_.line == line) if (attr_max_.line == line)
_m_scan_for_max(); _m_scan_for_max();
_m_edited(); edited_ = true;
} }
} }
@ -398,7 +421,7 @@ namespace skeletons
else if (pos < attr_max_.line) else if (pos < attr_max_.line)
attr_max_.line -= n; attr_max_.line -= n;
_m_edited(); edited_ = true;
return true; return true;
} }
@ -426,7 +449,7 @@ namespace skeletons
if(pos < attr_max_.line) if(pos < attr_max_.line)
--attr_max_.line; --attr_max_.line;
_m_edited(); edited_ = true;
} }
} }
@ -514,23 +537,12 @@ namespace skeletons
changed_ = false; changed_ = false;
} }
void _m_edited()
{
if(!changed_)
{
_m_first_change();
changed_ = true;
}
if (evt_agent_)
evt_agent_->text_changed();
}
private: private:
std::deque<std::unique_ptr<string_type>> text_cont_; std::deque<std::unique_ptr<string_type>> text_cont_;
textbase_event_agent_interface* evt_agent_{ nullptr }; textbase_event_agent_interface* evt_agent_{ nullptr };
mutable bool changed_{ false }; mutable bool changed_{ false };
mutable bool edited_{ false };
mutable std::string filename_; //A string for the saved filename. mutable std::string filename_; //A string for the saved filename.
const string_type nullstr_; const string_type nullstr_;

View File

@ -199,7 +199,7 @@ namespace nana{ namespace widgets
{ {
editor_.select_.a = sel_a_; editor_.select_.a = sel_a_;
editor_.select_.b = sel_b_; editor_.select_.b = sel_b_;
editor_._m_erase_select(); editor_._m_erase_select(false);
editor_.select_.a = editor_.select_.b; editor_.select_.a = editor_.select_.b;
editor_.points_.caret = sel_a_; editor_.points_.caret = sel_a_;
} }
@ -208,7 +208,7 @@ namespace nana{ namespace widgets
if (is_enter) if (is_enter)
{ {
editor_.points_.caret = nana::upoint(0, pos_.y + 1); editor_.points_.caret = nana::upoint(0, pos_.y + 1);
editor_.backspace(false); editor_.backspace(false, false);
} }
else else
editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size()); editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size());
@ -218,11 +218,11 @@ namespace nana{ namespace widgets
{ {
if (is_enter) if (is_enter)
{ {
editor_.enter(false); editor_.enter(false, false);
} }
else else
{ {
editor_._m_put(selected_text_); editor_._m_put(selected_text_, false);
if (sel_a_ != sel_b_) if (sel_a_ != sel_b_)
{ {
editor_.select_.a = sel_a_; editor_.select_.a = sel_a_;
@ -234,6 +234,8 @@ namespace nana{ namespace widgets
} }
} }
editor_.textbase().text_changed();
editor_.reset_caret(); editor_.reset_caret();
} }
}; };
@ -258,7 +260,7 @@ namespace nana{ namespace widgets
{ {
if (is_enter) if (is_enter)
{ {
editor_.enter(false); editor_.enter(false, false);
} }
else else
{ {
@ -266,9 +268,9 @@ namespace nana{ namespace widgets
{ {
editor_.select_.a = sel_a_; editor_.select_.a = sel_a_;
editor_.select_.b = sel_b_; editor_.select_.b = sel_b_;
editor_._m_erase_select(); editor_._m_erase_select(false);
} }
editor_.points_.caret = editor_._m_put(text_); //redo editor_.points_.caret = editor_._m_put(text_, false); //redo
} }
} }
else else
@ -277,7 +279,7 @@ namespace nana{ namespace widgets
{ {
editor_.points_.caret.x = 0; editor_.points_.caret.x = 0;
++editor_.points_.caret.y; ++editor_.points_.caret.y;
editor_.backspace(false); editor_.backspace(false, false);
} }
else else
{ {
@ -286,7 +288,7 @@ namespace nana{ namespace widgets
{ {
editor_.select_.a = pos_; editor_.select_.a = pos_;
editor_.select_.b = upoint(static_cast<unsigned>(lines.back().second - lines.back().first), static_cast<unsigned>(pos_.y + lines.size() - 1)); editor_.select_.b = upoint(static_cast<unsigned>(lines.back().second - lines.back().first), static_cast<unsigned>(pos_.y + lines.size() - 1));
editor_.backspace(false); editor_.backspace(false, false);
editor_.select_.a = editor_.select_.b; editor_.select_.a = editor_.select_.b;
} }
else else
@ -296,12 +298,14 @@ namespace nana{ namespace widgets
if (!selected_text_.empty()) if (!selected_text_.empty())
{ {
editor_.points_.caret = (std::min)(sel_a_, sel_b_); editor_.points_.caret = (std::min)(sel_a_, sel_b_);
editor_._m_put(selected_text_); editor_._m_put(selected_text_, false);
editor_.points_.caret = sel_b_; editor_.points_.caret = sel_b_;
editor_.select_.a = sel_a_; //Reset the selected text editor_.select_.a = sel_a_; //Reset the selected text
editor_.select_.b = sel_b_; editor_.select_.b = sel_b_;
} }
} }
editor_.textbase().text_changed();
editor_.reset_caret(); editor_.reset_caret();
} }
private: private:
@ -333,8 +337,8 @@ namespace nana{ namespace widgets
const auto text = editor_._m_make_select_string(); const auto text = editor_._m_make_select_string();
editor_._m_erase_select(); editor_._m_erase_select(false);
editor_._m_put(text); editor_._m_put(text, false);
editor_.select_.a = sel_a_; editor_.select_.a = sel_a_;
editor_.select_.b = sel_b_; editor_.select_.b = sel_b_;
@ -342,6 +346,7 @@ namespace nana{ namespace widgets
editor_.points_.caret = sel_b_; editor_.points_.caret = sel_b_;
editor_.reset_caret(); editor_.reset_caret();
} }
editor_.textbase().text_changed();
} }
void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b) void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b)
@ -1192,9 +1197,9 @@ namespace nana{ namespace widgets
switch (key) switch (key)
{ {
case '\b': case '\b':
backspace(); break; backspace(true, true); break;
case '\n': case '\r': case '\n': case '\r':
enter(); break; enter(true, true); break;
case keyboard::sync_idel: case keyboard::sync_idel:
paste(); break; paste(); break;
case keyboard::tab: case keyboard::tab:
@ -1681,7 +1686,7 @@ namespace nana{ namespace widgets
auto undo_ptr = std::unique_ptr<undo_input_text>{ new undo_input_text(*this, str) }; auto undo_ptr = std::unique_ptr<undo_input_text>{ new undo_input_text(*this, str) };
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
_m_put(std::move(str)); _m_put(std::move(str), false);
impl_->undo.push(std::move(undo_ptr)); impl_->undo.push(std::move(undo_ptr));
@ -1698,7 +1703,9 @@ namespace nana{ namespace widgets
} }
} }
else else
put(std::move(str)); put(std::move(str), false);
textbase().text_changed();
} }
std::wstring text_editor::text() const std::wstring text_editor::text() const
@ -1904,6 +1911,7 @@ namespace nana{ namespace widgets
if (_m_move_select(true)) if (_m_move_select(true))
{ {
textbase().text_changed();
this->_m_adjust_view(); this->_m_adjust_view();
impl_->try_refresh = sync_graph::refresh; impl_->try_refresh = sync_graph::refresh;
return true; return true;
@ -2021,7 +2029,7 @@ namespace nana{ namespace widgets
impl_->try_refresh = sync_graph::none; impl_->try_refresh = sync_graph::none;
} }
//public: //public:
void text_editor::put(std::wstring text) void text_editor::put(std::wstring text, bool perform_event)
{ {
if (text.empty()) if (text.empty())
return; return;
@ -2032,14 +2040,16 @@ namespace nana{ namespace widgets
//Do not forget to assign the _m_erase_select() to caret //Do not forget to assign the _m_erase_select() to caret
//because _m_put() will insert the text at the position where the caret is. //because _m_put() will insert the text at the position where the caret is.
points_.caret = _m_erase_select(); points_.caret = _m_erase_select(false);
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
points_.caret = _m_put(std::move(text)); points_.caret = _m_put(std::move(text), false);
impl_->undo.push(std::move(undo_ptr)); impl_->undo.push(std::move(undo_ptr));
_m_reset_content_size(true); _m_reset_content_size(true);
if (perform_event)
textbase().text_changed();
if(graph_) if(graph_)
{ {
@ -2060,7 +2070,7 @@ namespace nana{ namespace widgets
undo_ptr->set_selected_text(); undo_ptr->set_selected_text();
if(refresh) if(refresh)
points_.caret = _m_erase_select(); points_.caret = _m_erase_select(false);
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
@ -2070,6 +2080,8 @@ namespace nana{ namespace widgets
textbase().insert(points_.caret, std::move(ch_str)); textbase().insert(points_.caret, std::move(ch_str));
_m_pre_calc_lines(points_.caret.y, 1); _m_pre_calc_lines(points_.caret.y, 1);
textbase().text_changed();
points_.caret.x ++; points_.caret.x ++;
_m_reset_content_size(); _m_reset_content_size();
@ -2107,7 +2119,7 @@ namespace nana{ namespace widgets
if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive) if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive)
{ {
put(move(text)); put(move(text), true);
return; return;
} }
@ -2125,13 +2137,13 @@ namespace nana{ namespace widgets
if (accepts::no_restrict != impl_->capacities.acceptive) if (accepts::no_restrict != impl_->capacities.acceptive)
{ {
text.erase(i, text.end()); text.erase(i, text.end());
put(move(text)); put(move(text), true);
} }
break; break;
} }
} }
void text_editor::enter(bool record_undo) void text_editor::enter(bool record_undo, bool perform_event)
{ {
if(false == attributes_.multi_lines) if(false == attributes_.multi_lines)
return; return;
@ -2139,7 +2151,7 @@ namespace nana{ namespace widgets
auto undo_ptr = std::unique_ptr<undo_input_text>(new undo_input_text(*this, std::wstring(1, '\n'))); auto undo_ptr = std::unique_ptr<undo_input_text>(new undo_input_text(*this, std::wstring(1, '\n')));
undo_ptr->set_selected_text(); undo_ptr->set_selected_text();
points_.caret = _m_erase_select(); points_.caret = _m_erase_select(false);
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
@ -2177,21 +2189,24 @@ namespace nana{ namespace widgets
{ {
if (impl_->indent.generator) if (impl_->indent.generator)
{ {
put(to_wstring(impl_->indent.generator())); put(nana::to_wstring(impl_->indent.generator()), false);
} }
else else
{ {
auto & text = textbase.getline(points_.caret.y - 1); auto & text = textbase.getline(points_.caret.y - 1);
auto indent_pos = text.find_first_not_of(L"\t "); auto indent_pos = text.find_first_not_of(L"\t ");
if (indent_pos != std::wstring::npos) if (indent_pos != std::wstring::npos)
put(text.substr(0, indent_pos)); put(text.substr(0, indent_pos), false);
else else
put(text); put(text, false);
} }
} }
else else
_m_reset_content_size(); _m_reset_content_size();
if (perform_event)
textbase.text_changed();
auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin()); auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin());
if (this->_m_adjust_view() || origin_moved) if (this->_m_adjust_view() || origin_moved)
@ -2215,10 +2230,10 @@ namespace nana{ namespace widgets
return; //No characters behind the caret return; //No characters behind the caret
} }
backspace(); backspace(true, true);
} }
void text_editor::backspace(bool record_undo) void text_editor::backspace(bool record_undo, bool perform_event)
{ {
auto undo_ptr = std::unique_ptr<undo_backspace>(new undo_backspace(*this)); auto undo_ptr = std::unique_ptr<undo_backspace>(new undo_backspace(*this));
bool has_to_redraw = true; bool has_to_redraw = true;
@ -2258,7 +2273,7 @@ namespace nana{ namespace widgets
else else
{ {
undo_ptr->set_selected_text(); undo_ptr->set_selected_text();
points_.caret = _m_erase_select(); points_.caret = _m_erase_select(false);
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
} }
@ -2267,6 +2282,11 @@ namespace nana{ namespace widgets
_m_reset_content_size(false); _m_reset_content_size(false);
if (perform_event)
textbase().text_changed();
textbase().text_changed();
if(has_to_redraw) if(has_to_redraw)
{ {
this->_m_adjust_view(); this->_m_adjust_view();
@ -2960,7 +2980,7 @@ namespace nana{ namespace widgets
select_.a = select_.b; select_.a = select_.b;
} }
nana::upoint text_editor::_m_put(std::wstring text) nana::upoint text_editor::_m_put(std::wstring text, bool perform_event)
{ {
auto & textbase = this->textbase(); auto & textbase = this->textbase();
auto crtpos = points_.caret; auto crtpos = points_.caret;
@ -3002,10 +3022,13 @@ namespace nana{ namespace widgets
_m_pre_calc_lines(crtpos.y, 1); _m_pre_calc_lines(crtpos.y, 1);
} }
if (perform_event)
textbase.text_changed();
return crtpos; return crtpos;
} }
nana::upoint text_editor::_m_erase_select() nana::upoint text_editor::_m_erase_select(bool perform_event)
{ {
nana::upoint a, b; nana::upoint a, b;
if (get_selected_points(a, b)) if (get_selected_points(a, b))
@ -3027,6 +3050,9 @@ namespace nana{ namespace widgets
_m_pre_calc_lines(a.y, 1); _m_pre_calc_lines(a.y, 1);
} }
if (perform_event)
textbase.text_changed();
select_.a = select_.b; select_.a = select_.b;
return a; return a;
} }
@ -3239,8 +3265,8 @@ namespace nana{ namespace widgets
{//forward {//forward
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
_m_erase_select(); _m_erase_select(false);
_m_put(text); _m_put(text, false);
select_.a = caret; select_.a = caret;
select_.b.y = b.y + (caret.y - a.y); select_.b.y = b.y + (caret.y - a.y);
@ -3249,8 +3275,8 @@ namespace nana{ namespace widgets
{ {
undo_ptr->set_caret_pos(); undo_ptr->set_caret_pos();
_m_put(text); _m_put(text, false);
_m_erase_select(); _m_erase_select(false);
select_.b.y = caret.y; select_.b.y = caret.y;
select_.a.y = caret.y - (b.y - a.y); select_.a.y = caret.y - (b.y - a.y);

View File

@ -428,7 +428,7 @@ namespace drawerbase {
if(at_caret == false) if(at_caret == false)
editor->move_caret_end(false); editor->move_caret_end(false);
editor->put(to_wstring(text)); editor->put(to_wstring(text), true);
editor->try_refresh(); editor->try_refresh();
API::update_window(this->handle()); API::update_window(this->handle());
@ -445,7 +445,7 @@ namespace drawerbase {
if(at_caret == false) if(at_caret == false)
editor->move_caret_end(false); editor->move_caret_end(false);
editor->put(text); editor->put(text, true);
editor->try_refresh(); editor->try_refresh();
API::update_window(this->handle()); API::update_window(this->handle());