905 lines
22 KiB
C++
905 lines
22 KiB
C++
/*
|
|
* A Label Control Implementation
|
|
* Nana C++ Library(http://www.nanapro.org)
|
|
* 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
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
* @file: source/gui/widgets/label.cpp
|
|
* @author: Jinhao
|
|
* @contributors: Ariel Vina-Rodriguez
|
|
*/
|
|
|
|
#include <nana/gui/widgets/label.hpp>
|
|
#include <nana/gui/widgets/skeletons/text_token_stream.hpp>
|
|
#include <nana/gui/detail/widget_content_measurer_interface.hpp>
|
|
#include <nana/unicode_bidi.hpp>
|
|
#include <nana/system/platform.hpp>
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
|
|
namespace nana
|
|
{
|
|
namespace drawerbase
|
|
{
|
|
namespace label
|
|
{
|
|
class renderer
|
|
{
|
|
typedef widgets::skeletons::dstream::linecontainer::iterator iterator;
|
|
|
|
struct pixel_tag
|
|
{
|
|
int x_base; //The x position where this line starts.
|
|
std::size_t pixels;
|
|
std::size_t baseline; //The baseline for drawing text.
|
|
std::vector<iterator> values; //line values
|
|
};
|
|
|
|
//this is a helper variable, it just keeps the status while drawing.
|
|
struct render_status
|
|
{
|
|
unsigned allowed_width;
|
|
align text_align;
|
|
align_v text_align_v;
|
|
|
|
nana::point pos;
|
|
std::vector<pixel_tag> pixels;
|
|
std::size_t index;
|
|
};
|
|
|
|
struct traceable
|
|
{
|
|
nana::rectangle r;
|
|
std::wstring target;
|
|
std::wstring url;
|
|
};
|
|
|
|
public:
|
|
typedef nana::paint::graphics& graph_reference;
|
|
typedef widgets::skeletons::dstream dstream;
|
|
typedef widgets::skeletons::fblock fblock;
|
|
typedef widgets::skeletons::data data;
|
|
|
|
void parse(const std::wstring& s)
|
|
{
|
|
dstream_.parse(s, format_enabled_);
|
|
}
|
|
|
|
bool format(bool fm)
|
|
{
|
|
if (fm == format_enabled_)
|
|
return false;
|
|
|
|
format_enabled_ = fm;
|
|
return true;
|
|
}
|
|
|
|
void render(graph_reference graph, const ::nana::color& fgcolor, align th, align_v tv)
|
|
{
|
|
traceable_.clear();
|
|
|
|
auto pre_font = graph.typeface(); //used for restoring the font
|
|
|
|
const unsigned def_line_pixels = graph.text_extent_size(L" ", 1).height;
|
|
|
|
font_ = pre_font;
|
|
fblock_ = nullptr;
|
|
|
|
_m_set_default(pre_font, fgcolor);
|
|
|
|
_m_measure(graph);
|
|
|
|
render_status rs;
|
|
|
|
rs.allowed_width = graph.size().width;
|
|
rs.text_align = th;
|
|
rs.text_align_v = tv;
|
|
|
|
std::deque<std::vector<pixel_tag> > pixel_lines;
|
|
|
|
std::size_t extent_v_pixels = 0; //the pixels, in height, that text will be painted.
|
|
|
|
for (auto & line : dstream_)
|
|
{
|
|
_m_line_pixels(line, def_line_pixels, rs);
|
|
|
|
for (auto & m : rs.pixels)
|
|
extent_v_pixels += m.pixels;
|
|
|
|
pixel_lines.emplace_back(std::move(rs.pixels));
|
|
|
|
if(extent_v_pixels >= graph.height())
|
|
break;
|
|
}
|
|
|
|
if((tv != align_v::top) && extent_v_pixels < graph.height())
|
|
{
|
|
if(align_v::center == tv)
|
|
rs.pos.y = static_cast<int>(graph.height() - extent_v_pixels) >> 1;
|
|
else if(align_v::bottom == tv)
|
|
rs.pos.y = static_cast<int>(graph.height() - extent_v_pixels);
|
|
}
|
|
else
|
|
rs.pos.y = 0;
|
|
|
|
auto pixels_iterator = pixel_lines.begin();
|
|
|
|
for (auto & line : dstream_)
|
|
{
|
|
if (rs.pos.y >= static_cast<int>(graph.height()))
|
|
break;
|
|
|
|
rs.index = 0;
|
|
rs.pixels.clear();
|
|
|
|
rs.pixels.swap(*pixels_iterator++);
|
|
|
|
rs.pos.x = rs.pixels.front().x_base;
|
|
|
|
//Stop drawing when it goes out of range.
|
|
if(false == _m_each_line(graph, line, rs))
|
|
break;
|
|
|
|
rs.pos.y += static_cast<int>(rs.pixels.back().pixels);
|
|
}
|
|
|
|
graph.typeface(pre_font);
|
|
}
|
|
|
|
bool find(int x, int y, std::wstring& target, std::wstring& url) const
|
|
{
|
|
for (auto & t : traceable_)
|
|
{
|
|
if(t.r.is_hit(x, y))
|
|
{
|
|
target = t.target;
|
|
url = t.url;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
::nana::size measure(graph_reference graph, unsigned limited, align th, align_v tv)
|
|
{
|
|
::nana::size retsize;
|
|
|
|
auto ft = graph.typeface(); //used for restoring the font
|
|
|
|
const unsigned def_line_pixels = graph.text_extent_size(L" ", 1).height;
|
|
|
|
font_ = ft;
|
|
fblock_ = nullptr;
|
|
|
|
_m_set_default(ft, colors::black);
|
|
_m_measure(graph);
|
|
|
|
render_status rs;
|
|
|
|
rs.allowed_width = limited;
|
|
rs.text_align = th;
|
|
rs.text_align_v = tv;
|
|
|
|
for(auto i = dstream_.begin(), end = dstream_.end(); i != end; ++i)
|
|
{
|
|
rs.pixels.clear();
|
|
unsigned w = _m_line_pixels(*i, def_line_pixels, rs);
|
|
|
|
if(limited && (w > limited))
|
|
w = limited;
|
|
|
|
if(retsize.width < w)
|
|
retsize.width = w;
|
|
|
|
for (auto & px : rs.pixels)
|
|
retsize.height += static_cast<unsigned>(px.pixels);
|
|
}
|
|
|
|
return retsize;
|
|
}
|
|
private:
|
|
//Manage the fblock for a specified rectangle if it is a traceable fblock.
|
|
void _m_inser_if_traceable(int x, int y, const nana::size& sz, widgets::skeletons::fblock* fbp)
|
|
{
|
|
if(fbp->target.size() || fbp->url.size())
|
|
{
|
|
traceable_.emplace_back();
|
|
auto & tr = traceable_.back();
|
|
tr.r.x = x;
|
|
tr.r.y = y;
|
|
tr.r.dimension(sz);
|
|
tr.target = fbp->target;
|
|
tr.url = fbp->url;
|
|
}
|
|
}
|
|
|
|
void _m_set_default(const ::nana::paint::font& ft, const ::nana::color& fgcolor)
|
|
{
|
|
def_.font_name = ft.name();
|
|
def_.font_size = ft.size();
|
|
def_.font_bold = ft.bold();
|
|
def_.fgcolor = fgcolor;
|
|
}
|
|
|
|
const ::nana::color& _m_fgcolor(nana::widgets::skeletons::fblock* fp)
|
|
{
|
|
while(fp->fgcolor.invisible())
|
|
{
|
|
fp = fp->parent;
|
|
if(nullptr == fp)
|
|
return def_.fgcolor;
|
|
}
|
|
return fp->fgcolor;
|
|
}
|
|
|
|
std::size_t _m_font_size(nana::widgets::skeletons::fblock* fp)
|
|
{
|
|
while(fp->font_size == 0xFFFFFFFF)
|
|
{
|
|
fp = fp->parent;
|
|
if(nullptr == fp)
|
|
return def_.font_size;
|
|
}
|
|
return fp->font_size;
|
|
}
|
|
|
|
bool _m_bold(nana::widgets::skeletons::fblock* fp)
|
|
{
|
|
while(fp->bold_empty)
|
|
{
|
|
fp = fp->parent;
|
|
if(nullptr == fp)
|
|
return def_.font_bold;
|
|
}
|
|
return fp->bold;
|
|
}
|
|
|
|
const std::string& _m_fontname(nana::widgets::skeletons::fblock* fp)
|
|
{
|
|
while(fp->font.empty())
|
|
{
|
|
fp = fp->parent;
|
|
if(nullptr == fp)
|
|
return def_.font_name;
|
|
}
|
|
return fp->font;
|
|
}
|
|
|
|
void _m_change_font(graph_reference graph, nana::widgets::skeletons::fblock* fp)
|
|
{
|
|
if(fp != fblock_)
|
|
{
|
|
auto& name = _m_fontname(fp);
|
|
auto fontsize = static_cast<unsigned>(_m_font_size(fp));
|
|
bool bold = _m_bold(fp);
|
|
|
|
if((fontsize != font_.size()) || bold != font_.bold() || name != font_.name())
|
|
{
|
|
font_.make(name, fontsize, bold);
|
|
graph.typeface(font_);
|
|
}
|
|
fblock_ = fp;
|
|
}
|
|
}
|
|
|
|
void _m_measure(graph_reference graph)
|
|
{
|
|
nana::paint::font ft = font_;
|
|
for (auto & line : dstream_)
|
|
{
|
|
for (auto & value : line)
|
|
{
|
|
_m_change_font(graph, value.fblock_ptr);
|
|
value.data_ptr->measure(graph);
|
|
}
|
|
}
|
|
if(font_ != ft)
|
|
{
|
|
font_ = ft;
|
|
graph.typeface(ft);
|
|
fblock_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void _m_align_x_base(const render_status& rs, pixel_tag & px, unsigned w)
|
|
{
|
|
switch(rs.text_align)
|
|
{
|
|
case align::left:
|
|
px.x_base = 0;
|
|
break;
|
|
case align::center:
|
|
px.x_base = (static_cast<int>(rs.allowed_width - w) >> 1);
|
|
break;
|
|
case align::right:
|
|
px.x_base = static_cast<int>(rs.allowed_width - w);
|
|
break;
|
|
}
|
|
}
|
|
|
|
unsigned _m_line_pixels(dstream::linecontainer& line, unsigned def_line_pixels, render_status & rs)
|
|
{
|
|
if (line.empty())
|
|
{
|
|
pixel_tag px;
|
|
px.baseline = 0;
|
|
px.pixels = def_line_pixels;
|
|
px.x_base = 0;
|
|
|
|
rs.pixels.emplace_back(px);
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned total_w = 0;
|
|
unsigned w = 0;
|
|
unsigned max_ascent = 0;
|
|
unsigned max_descent = 0;
|
|
unsigned max_px = 0;
|
|
|
|
//Bidi reorder is requried here
|
|
|
|
std::vector<iterator> line_values;
|
|
|
|
for(auto i = line.begin(), end = line.end(); i != end; ++i)
|
|
{
|
|
data * data_ptr = i->data_ptr;
|
|
nana::size sz = data_ptr->size();
|
|
total_w += sz.width;
|
|
|
|
unsigned as = 0; //ascent
|
|
unsigned ds = 0; //descent
|
|
|
|
if(fblock::aligns::baseline == i->fblock_ptr->text_align)
|
|
{
|
|
as = static_cast<unsigned>(data_ptr->ascent());
|
|
ds = static_cast<unsigned>(sz.height - as);
|
|
|
|
if(max_descent < ds)
|
|
max_descent = ds;
|
|
|
|
if((false == data_ptr->is_text()) && (sz.height < max_ascent + max_descent))
|
|
sz.height = max_ascent + max_descent;
|
|
}
|
|
|
|
if(w + sz.width <= rs.allowed_width)
|
|
{
|
|
w += sz.width;
|
|
|
|
if(max_ascent < as) max_ascent = as;
|
|
if(max_descent < ds) max_descent = ds;
|
|
if(max_px < sz.height) max_px = sz.height;
|
|
line_values.emplace_back(i);
|
|
}
|
|
else
|
|
{
|
|
if(w)
|
|
{
|
|
pixel_tag px;
|
|
|
|
_m_align_x_base(rs, px, w);
|
|
|
|
if(max_ascent + max_descent > max_px)
|
|
max_px = max_descent + max_ascent;
|
|
else
|
|
max_ascent = max_px - max_descent;
|
|
|
|
px.pixels = max_px;
|
|
px.baseline = max_ascent;
|
|
px.values.swap(line_values);
|
|
|
|
rs.pixels.emplace_back(px);
|
|
|
|
w = sz.width;
|
|
max_px = sz.height;
|
|
max_ascent = as;
|
|
max_descent = ds;
|
|
line_values.emplace_back(i);
|
|
}
|
|
else
|
|
{
|
|
pixel_tag px;
|
|
|
|
_m_align_x_base(rs, px, sz.width);
|
|
px.pixels = sz.height;
|
|
px.baseline = as;
|
|
|
|
px.values.emplace_back(i);
|
|
|
|
rs.pixels.emplace_back(px);
|
|
max_px = 0;
|
|
max_ascent = max_descent = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (max_px)
|
|
{
|
|
pixel_tag px;
|
|
|
|
_m_align_x_base(rs, px, w);
|
|
|
|
if (max_ascent + max_descent > max_px)
|
|
max_px = max_descent + max_ascent;
|
|
else
|
|
max_ascent = max_px - max_descent;
|
|
|
|
px.pixels = max_px;
|
|
px.baseline = max_ascent;
|
|
px.values.swap(line_values);
|
|
rs.pixels.emplace_back(px);
|
|
}
|
|
return total_w;
|
|
}
|
|
|
|
bool _m_each_line(graph_reference graph, dstream::linecontainer&, render_status& rs)
|
|
{
|
|
std::wstring text;
|
|
iterator block_start;
|
|
|
|
const int lastpos = static_cast<int>(graph.height()) - 1;
|
|
|
|
for(auto i = rs.pixels.begin(), end = rs.pixels.end(); i != end; ++i)
|
|
{
|
|
for (auto & render_iterator : i->values)
|
|
{
|
|
auto & value = *render_iterator;
|
|
if(false == value.data_ptr->is_text())
|
|
{
|
|
if(text.size())
|
|
{
|
|
_m_draw_block(graph, text, block_start, rs);
|
|
if(lastpos <= rs.pos.y)
|
|
return false;
|
|
text.clear();
|
|
}
|
|
nana::size sz = value.data_ptr->size();
|
|
|
|
pixel_tag px = rs.pixels[rs.index];
|
|
if ((rs.allowed_width < rs.pos.x + sz.width) && (rs.pos.x != px.x_base))
|
|
{
|
|
//Change a line.
|
|
rs.pos.y += static_cast<int>(px.pixels);
|
|
px = rs.pixels[++rs.index];
|
|
rs.pos.x = px.x_base;
|
|
}
|
|
|
|
int y = rs.pos.y + _m_text_top(px, value.fblock_ptr, value.data_ptr);
|
|
|
|
value.data_ptr->nontext_render(graph, rs.pos.x, y);
|
|
_m_inser_if_traceable(rs.pos.x, y, sz, value.fblock_ptr);
|
|
rs.pos.x += static_cast<int>(sz.width);
|
|
|
|
if(lastpos < y)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//hold the block while the text is empty,
|
|
//it stands for the first block
|
|
if(text.empty())
|
|
block_start = render_iterator;
|
|
|
|
text += value.data_ptr->text();
|
|
}
|
|
}
|
|
|
|
if(text.size())
|
|
{
|
|
_m_draw_block(graph, text, block_start, rs);
|
|
text.clear();
|
|
}
|
|
}
|
|
return (rs.pos.y <= lastpos);
|
|
}
|
|
|
|
static bool _m_overline(const render_status& rs, int right, bool equal_required)
|
|
{
|
|
if(align::left == rs.text_align)
|
|
return (equal_required ? right >= static_cast<int>(rs.allowed_width) : right > static_cast<int>(rs.allowed_width));
|
|
|
|
return (equal_required ? rs.pixels[rs.index].x_base <= 0 : rs.pixels[rs.index].x_base < 0);
|
|
}
|
|
|
|
static int _m_text_top(const pixel_tag& px, fblock* fblock_ptr, const data* data_ptr)
|
|
{
|
|
switch(fblock_ptr->text_align)
|
|
{
|
|
case fblock::aligns::center:
|
|
return static_cast<int>(px.pixels - data_ptr->size().height) / 2;
|
|
case fblock::aligns::bottom:
|
|
return static_cast<int>(px.pixels - data_ptr->size().height);
|
|
case fblock::aligns::baseline:
|
|
return static_cast<int>(px.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height));
|
|
default: break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void _m_draw_block(graph_reference graph, const std::wstring& s, dstream::linecontainer::iterator block_start, render_status& rs)
|
|
{
|
|
nana::unicode_bidi bidi;
|
|
std::vector<nana::unicode_bidi::entity> reordered;
|
|
bidi.linestr(s.data(), s.length(), reordered);
|
|
|
|
pixel_tag px = rs.pixels[rs.index];
|
|
|
|
for(auto & bidi : reordered)
|
|
{
|
|
std::size_t pos = bidi.begin - s.data();
|
|
std::size_t len = bidi.end - bidi.begin;
|
|
|
|
while (true)
|
|
{
|
|
auto i = block_start;
|
|
|
|
//Text range indicates the position of text where begin to output
|
|
//The output length is the min between len and the second of text range.
|
|
auto text_range = _m_locate(i, pos);
|
|
|
|
if (text_range.second > len)
|
|
text_range.second = len;
|
|
|
|
fblock * fblock_ptr = i->fblock_ptr;
|
|
data * data_ptr = i->data_ptr;
|
|
|
|
const int w = static_cast<int>(rs.allowed_width) - rs.pos.x;
|
|
nana::size sz = data_ptr->size();
|
|
if ((static_cast<int>(sz.width) > w) && (rs.pos.x != px.x_base))
|
|
{
|
|
//Change a new line
|
|
rs.pos.y += static_cast<int>(px.pixels);
|
|
px = rs.pixels[++rs.index];
|
|
rs.pos.x = px.x_base;
|
|
}
|
|
|
|
const int y = rs.pos.y + _m_text_top(px, fblock_ptr, data_ptr);
|
|
|
|
_m_change_font(graph, fblock_ptr);
|
|
|
|
if (text_range.second == data_ptr->text().length())
|
|
{
|
|
graph.string({ rs.pos.x, y }, data_ptr->text(), _m_fgcolor(fblock_ptr));
|
|
}
|
|
else
|
|
{
|
|
auto str = data_ptr->text().substr(text_range.first, text_range.second);
|
|
graph.string({ rs.pos.x, y }, str, _m_fgcolor(fblock_ptr));
|
|
sz = graph.text_extent_size(str);
|
|
}
|
|
_m_inser_if_traceable(rs.pos.x, y, sz, fblock_ptr);
|
|
rs.pos.x += static_cast<int>(sz.width);
|
|
|
|
if(text_range.second < len)
|
|
{
|
|
len -= text_range.second;
|
|
pos += text_range.second;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<std::size_t, std::size_t> _m_locate(dstream::linecontainer::iterator& i, std::size_t pos)
|
|
{
|
|
std::size_t n = i->data_ptr->text().length();
|
|
while(pos >= n)
|
|
{
|
|
pos -= n;
|
|
n = (++i)->data_ptr->text().length();
|
|
}
|
|
|
|
return{ pos, n - pos };
|
|
}
|
|
private:
|
|
dstream dstream_;
|
|
bool format_enabled_ = false;
|
|
::nana::widgets::skeletons::fblock * fblock_ = nullptr;
|
|
std::deque<traceable> traceable_;
|
|
|
|
::nana::paint::font font_;
|
|
struct def_font_tag
|
|
{
|
|
::std::string font_name;
|
|
std::size_t font_size;
|
|
bool font_bold;
|
|
::nana::color fgcolor;
|
|
}def_;
|
|
};
|
|
|
|
//class trigger
|
|
//@brief: Draw the label
|
|
struct trigger::implement
|
|
{
|
|
class measurer;
|
|
|
|
widget * wd{nullptr};
|
|
paint::graphics * graph{nullptr};
|
|
class measurer * measurer{ nullptr };
|
|
|
|
align text_align{align::left};
|
|
align_v text_align_v;
|
|
|
|
class renderer renderer;
|
|
|
|
std::wstring target; //It indicates which target is tracing.
|
|
std::wstring url;
|
|
|
|
window for_associated_wd{ nullptr };
|
|
|
|
void add_listener(std::function<void(command, const std::string&)>&& fn)
|
|
{
|
|
listener_.emplace_back(std::move(fn));
|
|
}
|
|
|
|
void call_listener(command cmd, const std::wstring& tar)
|
|
{
|
|
auto str = to_utf8(tar);
|
|
for (auto & fn : listener_)
|
|
fn(cmd, str);
|
|
}
|
|
private:
|
|
std::vector<std::function<void(command, const std::string&)>> listener_;
|
|
};
|
|
|
|
class trigger::implement::measurer
|
|
: public dev::widget_content_measurer_interface
|
|
{
|
|
public:
|
|
measurer(implement* impl)
|
|
: impl_{ impl }
|
|
{}
|
|
|
|
optional<size> measure(bool limit_width, unsigned limit_pixels) const override
|
|
{
|
|
if (impl_->graph)
|
|
{
|
|
|
|
}
|
|
return{};
|
|
}
|
|
private:
|
|
implement * const impl_;
|
|
};
|
|
|
|
trigger::trigger()
|
|
:impl_(new implement)
|
|
{}
|
|
|
|
trigger::~trigger()
|
|
{
|
|
delete impl_;
|
|
}
|
|
|
|
trigger::implement * trigger::impl() const
|
|
{
|
|
return impl_;
|
|
}
|
|
|
|
void trigger::attached(widget_reference widget, graph_reference graph)
|
|
{
|
|
impl_->graph = &graph;
|
|
impl_->wd = &widget;
|
|
}
|
|
|
|
void trigger::mouse_move(graph_reference, const arg_mouse& arg)
|
|
{
|
|
std::wstring target, url;
|
|
|
|
if(impl_->renderer.find(arg.pos.x, arg.pos.y, target, url))
|
|
{
|
|
int cur_state = 0;
|
|
if(target != impl_->target)
|
|
{
|
|
if(impl_->target.size())
|
|
{
|
|
impl_->call_listener(command::leave, impl_->target);
|
|
cur_state = 1; //Set arrow
|
|
}
|
|
|
|
impl_->target = target;
|
|
|
|
if(target.size())
|
|
{
|
|
impl_->call_listener(command::enter, impl_->target);
|
|
cur_state = 2; //Set hand
|
|
}
|
|
}
|
|
if (url != impl_->url)
|
|
{
|
|
if (impl_->url.size())
|
|
cur_state = 1; //Set arrow
|
|
|
|
impl_->url = url;
|
|
|
|
if (url.size())
|
|
cur_state = 2; //Set hand
|
|
}
|
|
|
|
if (cur_state)
|
|
impl_->wd->cursor(1 == cur_state ? cursor::arrow : cursor::hand);
|
|
}
|
|
else
|
|
{
|
|
bool restore = false;
|
|
if (impl_->target.size())
|
|
{
|
|
impl_->call_listener(command::leave, impl_->target);
|
|
impl_->target.clear();
|
|
restore = true;
|
|
}
|
|
|
|
if (impl_->url.size())
|
|
{
|
|
impl_->url.clear();
|
|
restore = true;
|
|
}
|
|
|
|
if(restore)
|
|
impl_->wd->cursor(cursor::arrow);
|
|
}
|
|
}
|
|
|
|
void trigger::mouse_leave(graph_reference, const arg_mouse&)
|
|
{
|
|
if(impl_->target.size())
|
|
{
|
|
impl_->call_listener(command::leave, impl_->target);
|
|
impl_->target.clear();
|
|
impl_->wd->cursor(cursor::arrow);
|
|
}
|
|
}
|
|
|
|
void trigger::click(graph_reference, const arg_click&)
|
|
{
|
|
//make a copy, because the listener may popup a window, and then
|
|
//user moves the mouse. it will reset the url when the mouse is moving out from the element.
|
|
auto url = impl_->url;
|
|
|
|
if(impl_->target.size())
|
|
impl_->call_listener(command::click, impl_->target);
|
|
|
|
system::open_url(to_utf8(url));
|
|
|
|
API::focus_window(impl_->for_associated_wd);
|
|
}
|
|
|
|
void trigger::refresh(graph_reference graph)
|
|
{
|
|
if(nullptr == impl_->wd) return;
|
|
|
|
window wd = impl_->wd->handle();
|
|
if(bground_mode::basic != API::effects_bground_mode(wd))
|
|
graph.rectangle(true, API::bgcolor(wd));
|
|
|
|
impl_->renderer.render(graph, API::fgcolor(wd), impl_->text_align, impl_->text_align_v);
|
|
}
|
|
|
|
//end class label_drawer
|
|
}//end namespace label
|
|
}//end namespace drawerbase
|
|
|
|
|
|
//
|
|
//class label
|
|
label::label(){}
|
|
|
|
label::label(window wd, bool visible)
|
|
{
|
|
create(wd, rectangle(), visible);
|
|
bgcolor(API::bgcolor(wd));
|
|
}
|
|
|
|
label::label(window wd, const std::string& text, bool visible)
|
|
{
|
|
throw_not_utf8(text);
|
|
create(wd, rectangle(), visible);
|
|
bgcolor(API::bgcolor(wd));
|
|
caption(text);
|
|
}
|
|
|
|
label::label(window wd, const rectangle& r, bool visible)
|
|
{
|
|
create(wd, r, visible);
|
|
bgcolor(API::bgcolor(wd));
|
|
}
|
|
|
|
label& label::transparent(bool enabled)
|
|
{
|
|
if(enabled)
|
|
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
|
|
else
|
|
API::effects_bground_remove(*this);
|
|
return *this;
|
|
}
|
|
|
|
bool label::transparent() const throw()
|
|
{
|
|
return (bground_mode::basic == API::effects_bground_mode(*this));
|
|
}
|
|
|
|
label& label::format(bool f)
|
|
{
|
|
auto impl = get_drawer_trigger().impl();
|
|
|
|
if(impl->renderer.format(f))
|
|
{
|
|
window wd = *this;
|
|
impl->renderer.parse(::nana::to_wstring(API::dev::window_caption(wd)));
|
|
API::refresh_window(wd);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
label& label::add_format_listener(std::function<void(command, const std::string&)> f)
|
|
{
|
|
get_drawer_trigger().impl()->add_listener(std::move(f));
|
|
return *this;
|
|
}
|
|
|
|
label& label::click_for(window associated_window) throw()
|
|
{
|
|
get_drawer_trigger().impl()->for_associated_wd = associated_window;
|
|
return *this;
|
|
}
|
|
|
|
nana::size label::measure(unsigned limited) const
|
|
{
|
|
if(empty())
|
|
return nana::size();
|
|
|
|
auto impl = get_drawer_trigger().impl();
|
|
|
|
//First Check the graph of label
|
|
//Then take a substitute for graph when the graph of label is zero-sized.
|
|
nana::paint::graphics * graph_ptr = impl->graph;
|
|
nana::paint::graphics substitute;
|
|
if(graph_ptr->empty())
|
|
{
|
|
graph_ptr = &substitute;
|
|
graph_ptr->make({ 10, 10 });
|
|
}
|
|
|
|
return impl->renderer.measure(*graph_ptr, limited, impl->text_align, impl->text_align_v);
|
|
}
|
|
|
|
::nana::size label::measure(paint::graphics& graph, const ::std::string& str, unsigned allowed_width_in_pixel, bool format_enabled, align h_align, align_v v_align)
|
|
{
|
|
throw_not_utf8(str);
|
|
drawerbase::label::renderer rd;
|
|
rd.format(format_enabled);
|
|
rd.parse(to_wstring(str));
|
|
return rd.measure(graph, allowed_width_in_pixel, h_align, v_align);
|
|
}
|
|
|
|
label& label::text_align(align th, align_v tv)
|
|
{
|
|
internal_scope_guard lock;
|
|
auto impl = get_drawer_trigger().impl();
|
|
|
|
if (th != impl->text_align || tv != impl->text_align_v)
|
|
{
|
|
impl->text_align = th;
|
|
impl->text_align_v = tv;
|
|
API::refresh_window(*this);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
void label::_m_caption(native_string_type&& str)
|
|
{
|
|
internal_scope_guard lock;
|
|
window wd = *this;
|
|
get_drawer_trigger().impl()->renderer.parse(to_wstring(str));
|
|
API::dev::window_caption(wd, std::move(str));
|
|
API::refresh_window(wd);
|
|
}
|
|
//end class label
|
|
}//end namespace nana
|