refactor text_editor
fix issues that caret works incorrectly in line-wrapped mode.
This commit is contained in:
parent
df6c707356
commit
a4f15f7bb0
@ -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);
|
||||
@ -217,8 +219,10 @@ namespace nana{ namespace widgets
|
||||
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);
|
||||
|
||||
::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;
|
||||
@ -243,8 +247,9 @@ namespace nana{ namespace widgets
|
||||
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;
|
||||
|
||||
@ -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<string_type&&>(nana::charset{ str_mbs }));
|
||||
if(text_cont_.back().size() > attr_max_.size)
|
||||
text_cont_.emplace_back(new string_type(static_cast<string_type&&>(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<string_type&&>(nana::charset{ str, encoding }));
|
||||
text_cont_.emplace_back(new string_type(static_cast<string_type&&>(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<string_type&&>(nana::charset{ str, encoding }));
|
||||
if(text_cont_.back().size() > attr_max_.size)
|
||||
text_cont_.emplace_back(new string_type(static_cast<string_type&&>(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<int>(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<std::streamsize>(mbs.size()));
|
||||
}
|
||||
auto mbs = nana::charset(**(i++)).to_bytes(encoding);
|
||||
ofs.write(mbs.c_str(), static_cast<std::streamsize>(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<std::streamsize>(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<unsigned>(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<std::unique_ptr<string_type>>::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<string_type> text_cont_;
|
||||
std::deque<std::unique_ptr<string_type>> text_cont_;
|
||||
textbase_event_agent_interface* evt_agent_{ nullptr };
|
||||
|
||||
mutable bool changed_{ false };
|
||||
|
||||
@ -319,31 +319,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 +388,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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <nana/gui/widgets/widget.hpp>
|
||||
#include "content_view.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <numeric>
|
||||
#include <cwctype>
|
||||
#include <cstring>
|
||||
@ -45,7 +46,7 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
public:
|
||||
using command = EnumCommand;
|
||||
using container = std::deque < std::unique_ptr<undoable_command_interface<command>> >;
|
||||
using container = std::deque<std::unique_ptr<undoable_command_interface<command>>>;
|
||||
|
||||
void clear()
|
||||
{
|
||||
@ -555,7 +556,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)
|
||||
@ -685,62 +685,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<decltype(points.caret.x)>(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<int>(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<int>(editor_.line_height());
|
||||
if (points.caret.y >= row + scrlines) //implicit condition scrlines > 0
|
||||
{
|
||||
row = static_cast<int>(points.caret.y - scrlines) + 1;
|
||||
}
|
||||
else if (static_cast<int>(points.caret.y) < row)
|
||||
{
|
||||
if (scrlines >= static_cast<unsigned>(row))
|
||||
row = 0;
|
||||
else
|
||||
row = static_cast<int>(row - scrlines);
|
||||
}
|
||||
else if (row && (textbase.lines() <= scrlines))
|
||||
row = 0;
|
||||
|
||||
if(row != origin.y / static_cast<int>(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<text_section> sections_;
|
||||
@ -773,9 +717,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<std::size_t>(screen_rows));
|
||||
coord = _m_textline(static_cast<std::size_t>(text_row));
|
||||
if (linemtr_.size() <= coord.first)
|
||||
{
|
||||
coord.first = linemtr_.size() - 1;
|
||||
@ -795,22 +739,26 @@ 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();
|
||||
|
||||
pre_calc_line(first, width_px);
|
||||
|
||||
/*
|
||||
//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_)
|
||||
for (auto & mtr: linemtr_) //deprecated
|
||||
{
|
||||
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());
|
||||
pre_calc_line(line, width_px);
|
||||
|
||||
++line;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void add_lines(std::size_t pos, std::size_t lines) override
|
||||
@ -942,42 +890,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<std::size_t>(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<int>(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<int>(scrlines - 1), bottom);
|
||||
|
||||
_m_set_offset_by_secondary(bottom);
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
void _m_text_section(const std::wstring& str, std::vector<text_section>& tsec)
|
||||
{
|
||||
@ -1014,90 +926,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<int>(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<std::size_t>(-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<std::size_t>(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 +1064,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 +1083,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())
|
||||
@ -1680,7 +1519,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 +1571,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,7 +1637,8 @@ 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;
|
||||
@ -1832,7 +1672,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<int>(line_pixels);
|
||||
|
||||
@ -1863,7 +1703,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;
|
||||
@ -1949,7 +1789,7 @@ namespace nana{ namespace widgets
|
||||
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 +1825,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 +1834,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;
|
||||
}
|
||||
@ -2117,7 +1957,8 @@ 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;
|
||||
_m_reset_content_size(true);
|
||||
@ -2269,7 +2110,8 @@ namespace nana{ namespace widgets
|
||||
}
|
||||
|
||||
impl_->cview->move_origin(origin - impl_->cview->origin());
|
||||
if (impl_->capacities.behavior->adjust_caret_into_screen() || need_refresh)
|
||||
|
||||
if (this->_m_adjust_view() || need_refresh)
|
||||
impl_->cview->sync(true);
|
||||
|
||||
_m_reset_content_size();
|
||||
@ -2320,7 +2162,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;
|
||||
@ -2349,11 +2191,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 +2209,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 +2233,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,7 +2243,7 @@ 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;
|
||||
@ -2412,25 +2252,23 @@ namespace nana{ namespace widgets
|
||||
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;
|
||||
@ -2444,155 +2282,123 @@ 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<decltype(caret.y)>(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<decltype(caret.x)>(textbase().getline(caret.y).size());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
--pos.x;
|
||||
}
|
||||
else if (pos.y != 0) {
|
||||
--pos.y;
|
||||
pos.x = static_cast<decltype(pos.x)>(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<int>(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<int>(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<decltype(caret.x)>(lnsz);
|
||||
changed = true;
|
||||
}
|
||||
//move the caret to the end of the line
|
||||
pos.x = static_cast<decltype(pos.x)>(text_length);
|
||||
|
||||
//move the caret to the end of the text if Ctrl is pressed
|
||||
if(arg.ctrl)
|
||||
pos.y = (line_count - 1) * line_px;
|
||||
break;
|
||||
case keyboard::os_pageup:
|
||||
if (caret.y >= screen_lines() && origin.y >= static_cast<int>(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<int>(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<int>(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<int>(impl_->cview->view_area().height), static_cast<int>(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);
|
||||
else {
|
||||
select_.b = pos;
|
||||
select_.a = pos;
|
||||
}
|
||||
points_.caret = pos;
|
||||
points_.xpos = points_.caret.x;
|
||||
impl_->try_refresh = sync_graph::refresh;
|
||||
this->_m_adjust_view();
|
||||
impl_->cview->sync(true);
|
||||
this->reset_caret();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2633,9 +2439,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 +2455,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 +2473,7 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
auto const height = line_height();
|
||||
|
||||
auto top = _m_caret_to_screen(upoint{ 0, static_cast<unsigned>(row.first) }).y;
|
||||
auto top = _m_caret_to_coordinate(upoint{ 0, static_cast<unsigned>(row.first) }).y;
|
||||
std::size_t lines = 1;
|
||||
|
||||
if (whole_line)
|
||||
@ -2751,7 +2557,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 +2565,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 +2573,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 +2584,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 +2600,8 @@ namespace nana{ namespace widgets
|
||||
pos.x -= static_cast<unsigned>(chsize);
|
||||
++lines;
|
||||
}
|
||||
|
||||
++sct_pos;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2803,13 +2615,26 @@ namespace nana{ namespace widgets
|
||||
else
|
||||
scrpos.x += _m_text_x(*sct_ptr);
|
||||
|
||||
scrpos.y = static_cast<int>(lines * line_height()) - impl_->cview->origin().y + this->_m_text_top_base();
|
||||
if (!to_screen_coordinate)
|
||||
{
|
||||
scrpos.y = static_cast<int>(lines * line_height());
|
||||
//_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 = static_cast<int>(lines * line_height()) - impl_->cview->origin().y + this->_m_text_top_base();
|
||||
|
||||
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);
|
||||
@ -2833,10 +2658,8 @@ namespace nana{ namespace widgets
|
||||
unicode_bidi{}.linestr(text_ptr, text_size, reordered);
|
||||
|
||||
nana::upoint res(static_cast<unsigned>(real_str.begin - sections.front().begin), static_cast<unsigned>(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)
|
||||
{
|
||||
@ -2849,7 +2672,12 @@ namespace nana{ namespace widgets
|
||||
}
|
||||
scrpos.x -= str_px;
|
||||
}
|
||||
res.x = static_cast<unsigned>(textbase().getline(res.y).size());
|
||||
|
||||
//move the caret to the end of this section.
|
||||
res.x = text_size;
|
||||
for (std::size_t i = 0; i < row.second; ++i)
|
||||
res.x += static_cast<int>(sections[i].end - sections[i].begin);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -2939,7 +2767,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 +2775,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<unsigned>(pos) }).y;
|
||||
auto top = _m_caret_to_coordinate(upoint{ 0, static_cast<unsigned>(pos) }).y;
|
||||
|
||||
const unsigned pixels = line_height();
|
||||
const rectangle update_area = { text_area_.area.x, top, width_pixels(), static_cast<unsigned>(pixels * secondary_count_before) };
|
||||
@ -3010,12 +2838,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,11 +2862,12 @@ 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
|
||||
{
|
||||
@ -3046,7 +2875,7 @@ namespace nana{ namespace widgets
|
||||
impl_->capacities.behavior->pre_calc_lines(0);
|
||||
|
||||
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<unsigned>(impl_->capacities.behavior->take_lines() * line_height());
|
||||
@ -3237,11 +3066,13 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
case 1:
|
||||
points_.caret = a;
|
||||
_m_move_offset_x_while_over_border(-2);
|
||||
//_m_move_offset_x_while_over_border(-2); //deprecated
|
||||
this->_m_adjust_view();
|
||||
break;
|
||||
case 2:
|
||||
points_.caret = b;
|
||||
_m_move_offset_x_while_over_border(2);
|
||||
//_m_move_offset_x_while_over_border(2); //deprecated
|
||||
this->_m_adjust_view();
|
||||
break;
|
||||
}
|
||||
select_.a = select_.b = points_.caret;
|
||||
@ -3271,50 +3102,63 @@ namespace nana{ namespace widgets
|
||||
return graph_.text_extent_size(str, static_cast<unsigned>(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<int>(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<unsigned>(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<int>(width)))
|
||||
{ //Out of screen text area
|
||||
if (points_.caret.x > count)
|
||||
origin.x = static_cast<int>(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<int>(caret_size().width) >= origin.x + static_cast<int>(view_area.width))
|
||||
{
|
||||
extra.x = (std::min)(textbase().getline(points_.caret.y).size(), points_.caret.x + extra_count_horz);
|
||||
auto new_origin = _m_caret_to_coordinate(extra, false).x + static_cast<int>(caret_size().width) - static_cast<int>(view_area.width);
|
||||
moved_origin.x = new_origin - origin.x;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
//upoint pos2nd;
|
||||
//this->_m_pos_secondary(points_.caret, pos2nd); //deprecated
|
||||
|
||||
auto extra_px = static_cast<int>(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<int>(width) - origin.x >= right_pos)
|
||||
{ //Out of screen text area
|
||||
origin.x = static_cast<int>(width) - right_pos + 1;
|
||||
auto rest_size = lnstr.size() - points_.caret.x;
|
||||
origin.x += static_cast<int>(_m_text_extent_size(lnstr.c_str() + points_.caret.x, (rest_size >= static_cast<unsigned>(many) ? static_cast<unsigned>(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<int>(view_area.height))
|
||||
{
|
||||
//Bottom of caret is greater than the bottom of view
|
||||
|
||||
auto const bottom = static_cast<int>(impl_->capacities.behavior->take_lines() * line_px);
|
||||
auto new_origin = (std::min)(coord.y + line_px + extra_px, bottom) - static_cast<int>(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)
|
||||
@ -3696,9 +3540,9 @@ 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;
|
||||
@ -3789,4 +3633,3 @@ namespace nana{ namespace widgets
|
||||
}//end namespace skeletons
|
||||
}//end namespace widgets
|
||||
}//end namespace nana
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user