From e1992fb0d48abf35cd12d0454ff78d289a7e65a6 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 26 Aug 2018 16:42:53 +0800 Subject: [PATCH] fix crash when drawing in text_changed event with new line inserted(#332) --- .../gui/widgets/skeletons/text_editor.hpp | 10 +- .../nana/gui/widgets/skeletons/textbase.hpp | 52 ++++++---- source/gui/widgets/skeletons/text_editor.cpp | 98 ++++++++++++------- source/gui/widgets/textbox.cpp | 4 +- 4 files changed, 101 insertions(+), 63 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index d3536182..dd24d365 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -191,14 +191,14 @@ namespace nana{ namespace widgets void draw_corner(); void render(bool focused); public: - void put(std::wstring); + void put(std::wstring, bool perform_event); void put(wchar_t); void copy() const; void cut(); void paste(); - void enter(bool record_undo = true); + void enter(bool record_undo, bool perform_event); void del(); - void backspace(bool record_undo = true); + void backspace(bool record_undo, bool perform_event); void undo(bool reverse); void set_undo_queue_length(std::size_t len); void move_ns(bool to_north); //Moves up and down @@ -243,9 +243,9 @@ namespace nana{ namespace widgets void _m_reset(); //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; static bool _m_resolve_text(const ::std::wstring&, std::vector> & lines); diff --git a/include/nana/gui/widgets/skeletons/textbase.hpp b/include/nana/gui/widgets/skeletons/textbase.hpp index 751d9888..d11bc872 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-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (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 { return text_cont_.size(); @@ -330,7 +353,7 @@ namespace skeletons _m_at(pos).swap(text); _m_make_max(pos); - _m_edited(); + edited_ = true; } void insert(upoint pos, string_type && str) @@ -351,7 +374,7 @@ namespace skeletons } _m_make_max(pos.y); - _m_edited(); + edited_ = true; } void insertln(size_type pos, string_type&& str) @@ -362,7 +385,7 @@ namespace skeletons text_cont_.emplace_back(new string_type(std::move(str))); _m_make_max(pos); - _m_edited(); + edited_ = true; } void erase(size_type line, size_type pos, size_type count) @@ -378,7 +401,7 @@ namespace skeletons if (attr_max_.line == line) _m_scan_for_max(); - _m_edited(); + edited_ = true; } } @@ -398,7 +421,7 @@ namespace skeletons else if (pos < attr_max_.line) attr_max_.line -= n; - _m_edited(); + edited_ = true; return true; } @@ -426,7 +449,7 @@ namespace skeletons if(pos < attr_max_.line) --attr_max_.line; - _m_edited(); + edited_ = true; } } @@ -514,23 +537,12 @@ namespace skeletons changed_ = false; } - - void _m_edited() - { - if(!changed_) - { - _m_first_change(); - changed_ = true; - } - - if (evt_agent_) - evt_agent_->text_changed(); - } private: std::deque> text_cont_; 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. const string_type nullstr_; diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index e04c4ab8..0510acd9 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -199,7 +199,7 @@ namespace nana{ namespace widgets { editor_.select_.a = sel_a_; editor_.select_.b = sel_b_; - editor_._m_erase_select(); + editor_._m_erase_select(false); editor_.select_.a = editor_.select_.b; editor_.points_.caret = sel_a_; } @@ -208,7 +208,7 @@ namespace nana{ namespace widgets if (is_enter) { editor_.points_.caret = nana::upoint(0, pos_.y + 1); - editor_.backspace(false); + editor_.backspace(false, false); } else editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size()); @@ -218,11 +218,11 @@ namespace nana{ namespace widgets { if (is_enter) { - editor_.enter(false); + editor_.enter(false, false); } else { - editor_._m_put(selected_text_); + editor_._m_put(selected_text_, false); if (sel_a_ != sel_b_) { editor_.select_.a = sel_a_; @@ -234,6 +234,8 @@ namespace nana{ namespace widgets } } + editor_.textbase().text_changed(); + editor_.reset_caret(); } }; @@ -258,7 +260,7 @@ namespace nana{ namespace widgets { if (is_enter) { - editor_.enter(false); + editor_.enter(false, false); } else { @@ -266,9 +268,9 @@ namespace nana{ namespace widgets { editor_.select_.a = sel_a_; 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 @@ -277,7 +279,7 @@ namespace nana{ namespace widgets { editor_.points_.caret.x = 0; ++editor_.points_.caret.y; - editor_.backspace(false); + editor_.backspace(false, false); } else { @@ -286,7 +288,7 @@ namespace nana{ namespace widgets { editor_.select_.a = pos_; editor_.select_.b = upoint(static_cast(lines.back().second - lines.back().first), static_cast(pos_.y + lines.size() - 1)); - editor_.backspace(false); + editor_.backspace(false, false); editor_.select_.a = editor_.select_.b; } else @@ -296,12 +298,14 @@ namespace nana{ namespace widgets if (!selected_text_.empty()) { 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_.select_.a = sel_a_; //Reset the selected text editor_.select_.b = sel_b_; } } + + editor_.textbase().text_changed(); editor_.reset_caret(); } private: @@ -333,8 +337,8 @@ namespace nana{ namespace widgets const auto text = editor_._m_make_select_string(); - editor_._m_erase_select(); - editor_._m_put(text); + editor_._m_erase_select(false); + editor_._m_put(text, false); editor_.select_.a = sel_a_; editor_.select_.b = sel_b_; @@ -342,6 +346,7 @@ namespace nana{ namespace widgets editor_.points_.caret = sel_b_; editor_.reset_caret(); } + editor_.textbase().text_changed(); } void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b) @@ -1192,9 +1197,9 @@ namespace nana{ namespace widgets switch (key) { case '\b': - backspace(); break; + backspace(true, true); break; case '\n': case '\r': - enter(); break; + enter(true, true); break; case keyboard::sync_idel: paste(); break; case keyboard::tab: @@ -1681,7 +1686,7 @@ namespace nana{ namespace widgets auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, str) }; undo_ptr->set_caret_pos(); - _m_put(std::move(str)); + _m_put(std::move(str), false); impl_->undo.push(std::move(undo_ptr)); @@ -1698,7 +1703,9 @@ namespace nana{ namespace widgets } } else - put(std::move(str)); + put(std::move(str), false); + + textbase().text_changed(); } std::wstring text_editor::text() const @@ -1904,6 +1911,7 @@ namespace nana{ namespace widgets if (_m_move_select(true)) { + textbase().text_changed(); this->_m_adjust_view(); impl_->try_refresh = sync_graph::refresh; return true; @@ -2021,7 +2029,7 @@ namespace nana{ namespace widgets impl_->try_refresh = sync_graph::none; } //public: - void text_editor::put(std::wstring text) + void text_editor::put(std::wstring text, bool perform_event) { if (text.empty()) return; @@ -2032,14 +2040,16 @@ namespace nana{ namespace widgets //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. - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); 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)); _m_reset_content_size(true); + if (perform_event) + textbase().text_changed(); if(graph_) { @@ -2060,7 +2070,7 @@ namespace nana{ namespace widgets undo_ptr->set_selected_text(); if(refresh) - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); @@ -2070,6 +2080,8 @@ namespace nana{ namespace widgets textbase().insert(points_.caret, std::move(ch_str)); _m_pre_calc_lines(points_.caret.y, 1); + textbase().text_changed(); + points_.caret.x ++; _m_reset_content_size(); @@ -2107,7 +2119,7 @@ namespace nana{ namespace widgets if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive) { - put(move(text)); + put(move(text), true); return; } @@ -2125,13 +2137,13 @@ namespace nana{ namespace widgets if (accepts::no_restrict != impl_->capacities.acceptive) { text.erase(i, text.end()); - put(move(text)); + put(move(text), true); } break; } } - void text_editor::enter(bool record_undo) + void text_editor::enter(bool record_undo, bool perform_event) { if(false == attributes_.multi_lines) return; @@ -2139,7 +2151,7 @@ namespace nana{ namespace widgets auto undo_ptr = std::unique_ptr(new undo_input_text(*this, std::wstring(1, '\n'))); undo_ptr->set_selected_text(); - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); @@ -2177,21 +2189,24 @@ namespace nana{ namespace widgets { if (impl_->indent.generator) { - put(to_wstring(impl_->indent.generator())); + put(nana::to_wstring(impl_->indent.generator()), false); } else { auto & text = textbase.getline(points_.caret.y - 1); auto indent_pos = text.find_first_not_of(L"\t "); if (indent_pos != std::wstring::npos) - put(text.substr(0, indent_pos)); + put(text.substr(0, indent_pos), false); else - put(text); + put(text, false); } } else _m_reset_content_size(); + if (perform_event) + textbase.text_changed(); + auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin()); if (this->_m_adjust_view() || origin_moved) @@ -2215,10 +2230,10 @@ namespace nana{ namespace widgets 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(new undo_backspace(*this)); bool has_to_redraw = true; @@ -2258,7 +2273,7 @@ namespace nana{ namespace widgets else { undo_ptr->set_selected_text(); - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); } @@ -2267,6 +2282,11 @@ namespace nana{ namespace widgets _m_reset_content_size(false); + if (perform_event) + textbase().text_changed(); + + textbase().text_changed(); + if(has_to_redraw) { this->_m_adjust_view(); @@ -2960,7 +2980,7 @@ namespace nana{ namespace widgets 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 crtpos = points_.caret; @@ -3002,10 +3022,13 @@ namespace nana{ namespace widgets _m_pre_calc_lines(crtpos.y, 1); } + if (perform_event) + textbase.text_changed(); + return crtpos; } - nana::upoint text_editor::_m_erase_select() + nana::upoint text_editor::_m_erase_select(bool perform_event) { nana::upoint a, b; if (get_selected_points(a, b)) @@ -3027,6 +3050,9 @@ namespace nana{ namespace widgets _m_pre_calc_lines(a.y, 1); } + if (perform_event) + textbase.text_changed(); + select_.a = select_.b; return a; } @@ -3239,8 +3265,8 @@ namespace nana{ namespace widgets {//forward undo_ptr->set_caret_pos(); - _m_erase_select(); - _m_put(text); + _m_erase_select(false); + _m_put(text, false); select_.a = caret; select_.b.y = b.y + (caret.y - a.y); @@ -3249,8 +3275,8 @@ namespace nana{ namespace widgets { undo_ptr->set_caret_pos(); - _m_put(text); - _m_erase_select(); + _m_put(text, false); + _m_erase_select(false); select_.b.y = caret.y; select_.a.y = caret.y - (b.y - a.y); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index db57ae82..6697e567 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -428,7 +428,7 @@ namespace drawerbase { if(at_caret == false) editor->move_caret_end(false); - editor->put(to_wstring(text)); + editor->put(to_wstring(text), true); editor->try_refresh(); API::update_window(this->handle()); @@ -445,7 +445,7 @@ namespace drawerbase { if(at_caret == false) editor->move_caret_end(false); - editor->put(text); + editor->put(text, true); editor->try_refresh(); API::update_window(this->handle());