Merge branch 'toborrobot-develop' into hotfix-1.4.1
This commit is contained in:
commit
8594d175e1
@ -112,7 +112,8 @@ namespace nana
|
||||
void bind(window handle);
|
||||
window window_handle() const;
|
||||
|
||||
void div(const char* s); ///< Divides the attached widget into fields.
|
||||
void div(const char* s); ///< Divides the attached widget into fields.
|
||||
const std::string& div() const noexcept; ///< Returns div-text that depends on fields status.
|
||||
void modify(const char* field_name, const char* div_text); ///< Modifies a specified field.
|
||||
|
||||
field_reference field(const char* name);///< Returns a field with the specified name.
|
||||
|
@ -8,12 +8,7 @@
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* @file: nana/gui/widgets/listbox.hpp
|
||||
* @contributors:
|
||||
* Hiroshi Seki
|
||||
* Ariel Vina-Rodriguez
|
||||
* leobackes(pr#86,pr#97)
|
||||
* Benjamin Navarro(pr#81)
|
||||
* besh81(pr#130)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANA_GUI_WIDGETS_LISTBOX_HPP
|
||||
@ -800,12 +795,31 @@ namespace nana
|
||||
/// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort()
|
||||
index_pair to_display() const;
|
||||
|
||||
/// Determines whether the item is displayed on the screen
|
||||
bool displayed() const;
|
||||
|
||||
bool empty() const;
|
||||
|
||||
item_proxy & check(bool ck);
|
||||
/// Checks/unchecks the item
|
||||
/**
|
||||
* @param chk Indicates whether to check or uncheck the item
|
||||
* @param scroll_view Indicates whether to scroll the view to the item. It is ignored if the item is displayed.
|
||||
* @return the reference of *this.
|
||||
*/
|
||||
item_proxy & check(bool chk, bool scroll_view = false);
|
||||
|
||||
/// Determines whether the item is checked
|
||||
bool checked() const;
|
||||
|
||||
item_proxy & select(bool);
|
||||
/// Selects/unselects the item
|
||||
/**
|
||||
* @param sel Indicates whether to select or unselect the item
|
||||
* @param scroll_view Indicates whether to scroll the view to the item. It is ignored if the item is displayed.
|
||||
* @return the reference of *this.
|
||||
*/
|
||||
item_proxy & select(bool sel, bool scroll_view = false);
|
||||
|
||||
/// Determines whether he item is selected
|
||||
bool selected() const;
|
||||
|
||||
item_proxy & bgcolor(const nana::color&);
|
||||
@ -1130,9 +1144,11 @@ namespace nana
|
||||
: public widget_geometrics
|
||||
{
|
||||
color_proxy header_bgcolor{static_cast<color_rgb>(0xf1f2f4)};
|
||||
color_proxy header_fgcolor{ colors::black };
|
||||
color_proxy header_grabbed{ static_cast<color_rgb>(0x8BD6F6)};
|
||||
color_proxy header_floated{ static_cast<color_rgb>(0xBABBBC)};
|
||||
color_proxy item_selected{ static_cast<color_rgb>(0xD5EFFC) };
|
||||
color_proxy item_highlighted{ static_cast<color_rgb>(0xD5EFFC) };
|
||||
|
||||
/// The max column width which is generated by fit_content is allowed. It is ignored when it is 0, or a max value is passed to fit_content.
|
||||
unsigned max_fit_content{ 0 };
|
||||
@ -1435,7 +1451,7 @@ the nana::detail::basic_window member pointer scheme
|
||||
export_options& def_export_options();
|
||||
private:
|
||||
drawerbase::listbox::essence & _m_ess() const;
|
||||
nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const;
|
||||
nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const override;
|
||||
drawerbase::listbox::category_t* _m_assoc(std::shared_ptr<nana::detail::key_interface>, bool create_if_not_exists);
|
||||
void _m_erase_key(nana::detail::key_interface*);
|
||||
};
|
||||
|
@ -1,13 +1,14 @@
|
||||
/**
|
||||
* A Menu implementation
|
||||
* Nana C++ Library(http://www.nanapro.org)
|
||||
* Copyright(C) 2009-2014 Jinhao(cnjinhao@hotmail.com)
|
||||
* Copyright(C) 2009-2016 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: nana/gui/widgets/menu.hpp
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANA_GUI_WIDGETS_MENU_HPP
|
||||
@ -122,12 +123,13 @@ namespace nana
|
||||
~menu();
|
||||
|
||||
/// Appends an item to the menu.
|
||||
item_proxy append(const std::string& text, const event_fn_t& callback= event_fn_t());
|
||||
item_proxy append(std::string text_utf8, const event_fn_t& callback= event_fn_t());
|
||||
void append_splitter();
|
||||
void clear(); ///< Erases all of the items.
|
||||
/// Closes the menu. It does not destroy the menu; just close the window for the menu.
|
||||
void close();
|
||||
void image(std::size_t pos, const paint::image& icon);
|
||||
void text(std::size_t pos, std::string text_utf8);
|
||||
void check_style(std::size_t pos, checks);
|
||||
void checked(std::size_t pos, bool);
|
||||
bool checked(std::size_t pos) const;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <nana/deploy.hpp>
|
||||
|
||||
namespace nana
|
||||
{
|
||||
|
@ -201,7 +201,7 @@ namespace nana
|
||||
#if defined(NANA_WINDOWS)
|
||||
void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
arg_notifier arg;
|
||||
arg_notifier arg = {};
|
||||
switch (lparam)
|
||||
{
|
||||
case WM_LBUTTONDBLCLK:
|
||||
@ -321,6 +321,7 @@ namespace nana
|
||||
|
||||
void notifier::icon(const std::string& icon_file)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
paint::image image_ico{ icon_file };
|
||||
auto icon_handle = paint::image_accessor::icon(image_ico);
|
||||
if (icon_handle)
|
||||
@ -330,14 +331,17 @@ namespace nana
|
||||
impl_->set_icon(image_ico);
|
||||
impl_->icon = image_ico;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void notifier::insert_icon(const std::string& icon_file)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
paint::image image_ico{ icon_file };
|
||||
auto icon_handle = paint::image_accessor::icon(image_ico);
|
||||
if (icon_handle)
|
||||
impl_->icons.emplace_back(static_cast<paint::image&&>(image_ico));
|
||||
#endif
|
||||
}
|
||||
|
||||
void notifier::period(unsigned ms)
|
||||
|
@ -8,7 +8,8 @@
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* @file: nana/gui/place.cpp
|
||||
* @contributors: Ariel Vina-Rodriguez
|
||||
* @contributors: Ariel Vina-Rodriguez
|
||||
* dankan1890(PR#156)
|
||||
*/
|
||||
|
||||
#include <cfloat>
|
||||
@ -56,7 +57,7 @@ namespace nana
|
||||
{
|
||||
div_start, div_end, splitter,
|
||||
identifier, dock, vert, grid, number, array, reparray,
|
||||
weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom,
|
||||
weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, undisplayed, invisible,
|
||||
collapse, parameters,
|
||||
equal,
|
||||
eof, error
|
||||
@ -275,7 +276,7 @@ namespace nana
|
||||
_m_throw_error("a parameter list is required after 'collapse'");
|
||||
return token::collapse;
|
||||
}
|
||||
else if ("left" == idstr_ || "right" == idstr_ || "top" == idstr_ || "bottom" == idstr_)
|
||||
else if ("left" == idstr_ || "right" == idstr_ || "top" == idstr_ || "bottom" == idstr_ || "undisplayed" == idstr_ || "invisible" == idstr_)
|
||||
{
|
||||
switch (idstr_.front())
|
||||
{
|
||||
@ -283,8 +284,11 @@ namespace nana
|
||||
case 'r': return token::right;
|
||||
case 't': return token::top;
|
||||
case 'b': return token::bottom;
|
||||
case 'u': return token::undisplayed;
|
||||
case 'i': return token::invisible;
|
||||
}
|
||||
}
|
||||
|
||||
return token::identifier;
|
||||
}
|
||||
|
||||
@ -407,8 +411,18 @@ namespace nana
|
||||
sp = _m_eat_whitespace(sp);
|
||||
if ('%' == *sp)
|
||||
{
|
||||
if (number_t::kind::integer == number_.kind_of())
|
||||
switch (number_.kind_of())
|
||||
{
|
||||
case number_t::kind::integer:
|
||||
number_.assign_percent(number_.integer());
|
||||
break;
|
||||
case number_t::kind::real:
|
||||
number_.assign_percent(number_.real());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return sp - allstart + 1;
|
||||
}
|
||||
return sp - allstart;
|
||||
@ -427,6 +441,87 @@ namespace nana
|
||||
}; //end class tokenizer
|
||||
}
|
||||
|
||||
|
||||
inline bool is_idchar(int ch)
|
||||
{
|
||||
return ('_' == ch || isalnum(ch));
|
||||
}
|
||||
|
||||
std::size_t find_idstr(const std::string& text, const char* idstr, std::size_t off = 0)
|
||||
{
|
||||
const auto len = std::strlen(idstr);
|
||||
|
||||
size_t pos;
|
||||
while ((pos = text.find(idstr, off)) != text.npos)
|
||||
{
|
||||
if (!is_idchar(text[pos + len]))
|
||||
{
|
||||
if (pos == 0 || !is_idchar(text[pos - 1]))
|
||||
return pos;
|
||||
}
|
||||
|
||||
off = pos + len; // occurrence not found, advancing the offset and try again
|
||||
}
|
||||
return text.npos;
|
||||
}
|
||||
|
||||
std::pair<std::size_t, std::size_t> get_field_bound(const std::string& div, const char* idstr, int depth)
|
||||
{
|
||||
auto start_pos = find_idstr(div, idstr);
|
||||
|
||||
if (depth < 0 || start_pos >= div.length())
|
||||
return{};
|
||||
|
||||
const char* p = div.c_str() + start_pos;
|
||||
|
||||
while (depth >= 0)
|
||||
{
|
||||
auto pos = div.find_last_of("<>", start_pos);
|
||||
if (div.npos == pos)
|
||||
return{};
|
||||
|
||||
if (div[pos] == '>')
|
||||
{
|
||||
++depth;
|
||||
start_pos = pos - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == depth)
|
||||
{
|
||||
start_pos = pos;
|
||||
break;
|
||||
}
|
||||
|
||||
--depth;
|
||||
start_pos = pos - 1;
|
||||
}
|
||||
|
||||
auto off = start_pos + 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto pos = div.find_first_of("<>", off);
|
||||
|
||||
if (div.npos == pos)
|
||||
return{};
|
||||
|
||||
if ('<' == div[pos])
|
||||
{
|
||||
++depth;
|
||||
off = pos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == depth)
|
||||
return{ start_pos, pos + 1 };
|
||||
|
||||
--depth;
|
||||
off = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//struct implement
|
||||
struct place::implement
|
||||
{
|
||||
@ -442,6 +537,8 @@ namespace nana
|
||||
|
||||
window window_handle{nullptr};
|
||||
event_handle event_size_handle{nullptr};
|
||||
|
||||
std::string div_text;
|
||||
std::unique_ptr<division> root_division;
|
||||
std::map<std::string, field_gather*> fields;
|
||||
std::map<std::string, field_dock*> docks;
|
||||
@ -1309,8 +1406,9 @@ namespace nana
|
||||
|
||||
enum{splitter_px = 4};
|
||||
public:
|
||||
div_splitter(place_parts::number_t init_weight)
|
||||
: division(kind::splitter, std::string()),
|
||||
div_splitter(place_parts::number_t init_weight, implement* impl):
|
||||
division(kind::splitter, std::string()),
|
||||
impl_(impl),
|
||||
init_weight_(init_weight)
|
||||
{
|
||||
this->weight.assign(splitter_px);
|
||||
@ -1335,7 +1433,7 @@ namespace nana
|
||||
|
||||
auto grab_fn = [this](const arg_mouse& arg)
|
||||
{
|
||||
if (false == arg.left_button)
|
||||
if ((false == arg.left_button) && (mouse::left_button != arg.button))
|
||||
return;
|
||||
|
||||
if (event_code::mouse_down == arg.evt_code)
|
||||
@ -1368,6 +1466,7 @@ namespace nana
|
||||
else if(event_code::mouse_up == arg.evt_code)
|
||||
{
|
||||
grabbed_ = false;
|
||||
this->_m_update_div(impl_->div_text);
|
||||
}
|
||||
else if (event_code::mouse_move == arg.evt_code)
|
||||
{
|
||||
@ -1466,6 +1565,231 @@ namespace nana
|
||||
splitter_.move(this->field_area);
|
||||
}
|
||||
private:
|
||||
static int _m_search_name(const division* div, std::string& name)
|
||||
{
|
||||
if (div->name.size())
|
||||
{
|
||||
name = div->name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto & child : div->children)
|
||||
{
|
||||
if (child->name.size())
|
||||
{
|
||||
name = child->name;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & child : div->children)
|
||||
{
|
||||
auto depth = _m_search_name(child.get(), name);
|
||||
if (depth >= 0)
|
||||
return depth + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::pair<std::size_t, std::size_t> _m_field_bound(const std::string& div, std::size_t start_pos)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
if ('<' == div[start_pos])
|
||||
{
|
||||
auto off = start_pos + 1;
|
||||
while (off < div.length())
|
||||
{
|
||||
auto pos = div.find_first_of("<>", off);
|
||||
if (div.npos == pos)
|
||||
break;
|
||||
|
||||
if ('<' == div[pos])
|
||||
{
|
||||
++depth;
|
||||
off = pos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == depth)
|
||||
return{ start_pos, pos + 1};
|
||||
|
||||
--depth;
|
||||
off = pos + 1;
|
||||
}
|
||||
}
|
||||
else if (('>' == div[start_pos]) && (start_pos > 0))
|
||||
{
|
||||
auto off = start_pos - 1;
|
||||
while (true)
|
||||
{
|
||||
auto pos = div.find_last_of("<>", off);
|
||||
if (div.npos == pos)
|
||||
break;
|
||||
|
||||
if ('>' == div[pos])
|
||||
{
|
||||
++depth;
|
||||
if (0 == pos)
|
||||
break;
|
||||
|
||||
off = pos - 1;
|
||||
}
|
||||
|
||||
if (0 == depth)
|
||||
return{ pos, start_pos + 1};
|
||||
|
||||
if (0 == pos)
|
||||
break;
|
||||
|
||||
off = pos - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return{};
|
||||
}
|
||||
|
||||
static void _m_remove_attr(std::string& div, const char* attr)
|
||||
{
|
||||
auto attr_pos = div.find(attr);
|
||||
if (div.npos == attr_pos)
|
||||
return;
|
||||
|
||||
std::size_t off = 1;
|
||||
while (true)
|
||||
{
|
||||
auto pos = div.find('<', off);
|
||||
if (div.npos == pos)
|
||||
break;
|
||||
|
||||
if (attr_pos < pos)
|
||||
break;
|
||||
|
||||
int depth = 0;
|
||||
off = pos + 1;
|
||||
std::size_t endpos = 0;
|
||||
while (true)
|
||||
{
|
||||
endpos = div.find_first_of("<>", off);
|
||||
if (div.npos == endpos)
|
||||
return;
|
||||
|
||||
if ('<' == div[endpos])
|
||||
{
|
||||
++depth;
|
||||
off = endpos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == depth)
|
||||
break;
|
||||
|
||||
--depth;
|
||||
off = endpos + 1;
|
||||
}
|
||||
|
||||
if (attr_pos < endpos)
|
||||
{
|
||||
attr_pos = div.find(attr, endpos + 1);
|
||||
if (div.npos == attr_pos)
|
||||
return;
|
||||
}
|
||||
|
||||
off = endpos + 1;
|
||||
}
|
||||
|
||||
auto len = std::strlen(attr);
|
||||
|
||||
auto endpos = div.find_first_not_of(" ", attr_pos + len);
|
||||
if (div.npos != endpos && div[endpos] == '=')
|
||||
{
|
||||
endpos = div.find_first_not_of(" 0123456789.%", endpos + 1);
|
||||
if (div.npos == endpos)
|
||||
throw std::runtime_error("please report an issue if throws");
|
||||
}
|
||||
else
|
||||
endpos = attr_pos + len;
|
||||
|
||||
div.erase(attr_pos, endpos - attr_pos);
|
||||
}
|
||||
|
||||
void _m_update_div(std::string& div)
|
||||
{
|
||||
std::string name;
|
||||
bool left = true;
|
||||
|
||||
//Search a name recursively from a specified leaf field.
|
||||
//It returns the depth from the leaf div to the div which has a name.
|
||||
auto depth = _m_search_name(_m_leaf_left(), name);
|
||||
if (-1 == depth)
|
||||
{
|
||||
left = false;
|
||||
depth = _m_search_name(_m_leaf_right(), name);
|
||||
if (-1 == depth)
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the bound of field div-text through reverse recursion.
|
||||
auto bound = get_field_bound(div, name.c_str(), depth);
|
||||
if (bound.first == bound.second)
|
||||
return;
|
||||
|
||||
auto fieldstr = div.substr(bound.first, bound.second - bound.first);
|
||||
_m_remove_attr(fieldstr, "weight");
|
||||
|
||||
decltype(bound) other_bound;
|
||||
if (left)
|
||||
{
|
||||
//Get the bound of leaf right
|
||||
auto pos = div.find('<', bound.second + 1);
|
||||
if (div.npos == pos)
|
||||
throw std::runtime_error("please report an issue if it throws");
|
||||
|
||||
other_bound = _m_field_bound(div, pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Get the bound of leaf left
|
||||
auto pos = div.rfind('>', bound.first - 1);
|
||||
if (div.npos == pos)
|
||||
throw std::runtime_error("place report an issue if it throws");
|
||||
|
||||
other_bound = _m_field_bound(div, pos);
|
||||
}
|
||||
|
||||
auto other_fieldstr = div.substr(other_bound.first, other_bound.second - other_bound.first);
|
||||
_m_remove_attr(other_fieldstr, "weight");
|
||||
|
||||
const bool vert = (::nana::cursor::size_we != splitter_cursor_);
|
||||
|
||||
rectangle_rotator r_left(vert, _m_leaf_left()->field_area);
|
||||
rectangle_rotator r_right(vert, _m_leaf_right()->field_area);
|
||||
rectangle_rotator r_owner(vert, this->div_owner->field_area);
|
||||
|
||||
double percent = double((left ? r_left : r_right).w()) / double(r_owner.w());
|
||||
|
||||
std::string weight = "weight=" + std::to_string(percent * 100) + "% ";
|
||||
|
||||
fieldstr.insert(1, weight);
|
||||
|
||||
//Replaces the 'right' field before 'left' in order to make the bound consistent
|
||||
if (left)
|
||||
{
|
||||
if (other_fieldstr.length() != (other_bound.second - other_bound.first))
|
||||
div.replace(other_bound.first, other_bound.second - other_bound.first, other_fieldstr);
|
||||
|
||||
div.replace(bound.first, bound.second - bound.first, fieldstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
div.replace(bound.first, bound.second - bound.first, fieldstr);
|
||||
|
||||
if (other_fieldstr.length() != (other_bound.second - other_bound.first))
|
||||
div.replace(other_bound.first, other_bound.second - other_bound.first, other_fieldstr);
|
||||
}
|
||||
}
|
||||
|
||||
division * _m_leaf_left() const
|
||||
{
|
||||
return previous();
|
||||
@ -1526,6 +1850,7 @@ namespace nana
|
||||
return area;
|
||||
}
|
||||
private:
|
||||
implement* const impl_;
|
||||
nana::cursor splitter_cursor_{nana::cursor::arrow};
|
||||
place_parts::splitter<true> splitter_;
|
||||
nana::point begin_point_;
|
||||
@ -1885,7 +2210,7 @@ namespace nana
|
||||
unsigned vert_count = 0, horz_count = 0;
|
||||
|
||||
bool is_first = true;
|
||||
bool prev_attr;
|
||||
bool prev_attr = false;
|
||||
|
||||
for (auto & child : children)
|
||||
{
|
||||
@ -1962,7 +2287,7 @@ namespace nana
|
||||
child_dv->splitter.reset();
|
||||
|
||||
::nana::rectangle child_r;
|
||||
double split_range_begin = -1, split_range_end;
|
||||
double split_range_begin = -1, split_range_end = 0;
|
||||
switch (child->dir)
|
||||
{
|
||||
default:
|
||||
@ -2147,6 +2472,9 @@ namespace nana
|
||||
std::vector<std::unique_ptr<division>> children;
|
||||
::nana::direction div_dir = ::nana::direction::west;
|
||||
|
||||
bool undisplayed = false;
|
||||
bool invisible = false;
|
||||
|
||||
for (token tk = tknizer.read(); tk != token::eof; tk = tknizer.read())
|
||||
{
|
||||
bool exit_for = false;
|
||||
@ -2162,7 +2490,7 @@ namespace nana
|
||||
//Ignore the splitter when there is not a division.
|
||||
if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division))
|
||||
{
|
||||
auto splitter = new div_splitter(tknizer.number());
|
||||
auto splitter = new div_splitter(tknizer.number(), this);
|
||||
children.back()->div_next = splitter;
|
||||
children.emplace_back(std::unique_ptr<division>{ splitter });
|
||||
}
|
||||
@ -2305,6 +2633,10 @@ namespace nana
|
||||
div_dir = ::nana::direction::north; break;
|
||||
case token::bottom:
|
||||
div_dir = ::nana::direction::south; break;
|
||||
case token::undisplayed:
|
||||
undisplayed = true; break;
|
||||
case token::invisible:
|
||||
invisible = true; break;
|
||||
default: break;
|
||||
}
|
||||
if (exit_for)
|
||||
@ -2430,6 +2762,9 @@ namespace nana
|
||||
div->children.swap(children);
|
||||
div->margin = std::move(margin);
|
||||
div->dir = div_dir;
|
||||
|
||||
div->display = !undisplayed;
|
||||
div->visible = !(undisplayed || invisible);
|
||||
return div;
|
||||
}
|
||||
|
||||
@ -2593,6 +2928,7 @@ namespace nana
|
||||
impl_->connect(div.get());
|
||||
impl_->root_division.reset(); //clear atachments div-fields
|
||||
impl_->root_division.swap(div);
|
||||
impl_->div_text.assign(s);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -2601,6 +2937,16 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& place::div() const noexcept
|
||||
{
|
||||
return impl_->div_text;
|
||||
}
|
||||
|
||||
//Contributed by dankan1890(PR#156)
|
||||
enum class update_operation { erase = 0, insert, replace };
|
||||
|
||||
void update_div(std::string& div, const char* field, const char* attr, update_operation operation);
|
||||
|
||||
void place::modify(const char* name, const char* div_text)
|
||||
{
|
||||
if (nullptr == div_text)
|
||||
@ -2655,6 +3001,7 @@ namespace nana
|
||||
impl_->check_unique(impl_->root_division.get());
|
||||
impl_->connect(impl_->root_division.get());
|
||||
impl_->tmp_replaced.reset();
|
||||
update_div(impl_->div_text, name, div_text, update_operation::replace);
|
||||
|
||||
modified_ptr->div_owner = div_owner;
|
||||
modified_ptr->div_next = div_next;
|
||||
@ -2698,6 +3045,71 @@ namespace nana
|
||||
return *p;
|
||||
}
|
||||
|
||||
void update_div(std::string& div, const char* field, const char* attr, update_operation operation)
|
||||
{
|
||||
const auto fieldname_pos = find_idstr(div, field);
|
||||
if (div.npos == fieldname_pos)
|
||||
return;
|
||||
|
||||
auto bound = get_field_bound(div, field, 0);
|
||||
|
||||
auto fieldstr = div.substr(bound.first + 1, bound.second - bound.first - 2);
|
||||
//Search the attribute
|
||||
std::size_t pos = 0;
|
||||
int depth = 0;
|
||||
for (; true; ++pos)
|
||||
{
|
||||
pos = find_idstr(fieldstr, attr, pos);
|
||||
if (fieldstr.npos == pos)
|
||||
break;
|
||||
|
||||
//Check if the attr is belong to this field.
|
||||
depth = 0;
|
||||
std::size_t off = pos;
|
||||
while (true)
|
||||
{
|
||||
off = fieldstr.find_last_of("<>", off);
|
||||
if (fieldstr.npos == off)
|
||||
break;
|
||||
|
||||
if ('>' == fieldstr[off])
|
||||
++depth;
|
||||
else
|
||||
--depth;
|
||||
|
||||
if (0 == off)
|
||||
break;
|
||||
--off;
|
||||
}
|
||||
|
||||
if (0 == depth)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fieldstr.npos == pos)
|
||||
{
|
||||
//There is not an attribute
|
||||
if (operation == update_operation::insert)
|
||||
div.insert(fieldname_pos + std::strlen(field), " " + std::string(attr));
|
||||
else if (operation == update_operation::replace)
|
||||
{
|
||||
div.erase(bound.first + 1, fieldstr.length());
|
||||
div.insert(bound.first + 1, std::string(attr) + " " + std::string(field));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//There is an attribute
|
||||
if (operation == update_operation::erase)
|
||||
{
|
||||
div.erase(bound.first + pos + 1, std::strlen(attr));
|
||||
|
||||
if ((div[bound.first + pos] == div[bound.first + pos + 1]) && (' ' == div[bound.first + pos]))
|
||||
div.erase(bound.first + pos, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void place::field_visible(const char* name, bool vsb)
|
||||
{
|
||||
if (!name) name = "";
|
||||
@ -2707,7 +3119,10 @@ namespace nana
|
||||
|
||||
auto div = impl_->search_div_name(impl_->root_division.get(), name);
|
||||
if (div)
|
||||
{
|
||||
div->set_visible(vsb);
|
||||
update_div(impl_->div_text, name, "invisible", !vsb ? update_operation::insert : update_operation::erase);
|
||||
}
|
||||
}
|
||||
|
||||
bool place::field_visible(const char* name) const
|
||||
@ -2730,7 +3145,11 @@ namespace nana
|
||||
|
||||
auto div = impl_->search_div_name(impl_->root_division.get(), name);
|
||||
if (div)
|
||||
{
|
||||
update_div(impl_->div_text, name, "invisible", update_operation::erase);
|
||||
update_div(impl_->div_text, name, "undisplayed", !dsp ? update_operation::insert : update_operation::erase);
|
||||
div->set_display(dsp);
|
||||
}
|
||||
}
|
||||
|
||||
bool place::field_display(const char* name) const
|
||||
|
@ -14,7 +14,8 @@
|
||||
* leobackes(pr#86,pr#97)
|
||||
* Benjamin Navarro(pr#81)
|
||||
* besh81(pr#130)
|
||||
*
|
||||
* dankan1890(pr#158)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <nana/gui/widgets/listbox.hpp>
|
||||
@ -3393,7 +3394,8 @@ namespace nana
|
||||
const auto border_color = essence_->scheme_ptr->header_bgcolor.get_color().blend(colors::black, 0.8);
|
||||
|
||||
int text_top = (r.height - essence_->scheme_ptr->text_height) / 2 + r.y;
|
||||
auto text_color = essence_->lister.wd_ptr()->fgcolor();
|
||||
auto text_color = essence_->scheme_ptr->header_fgcolor.get_color();
|
||||
|
||||
|
||||
auto state = item_state::normal;
|
||||
//check whether grabing an item, if item_spliter_ != npos, that indicates the grab item is a spliter.
|
||||
@ -3784,7 +3786,7 @@ namespace nana
|
||||
if (item.flags.selected)
|
||||
bgcolor = bgcolor.blend(colors::black, 0.98); // or "selected"
|
||||
else
|
||||
bgcolor = bgcolor.blend(essence_->scheme_ptr->item_selected, 0.7); /// \todo create a parametre for amount of blend
|
||||
bgcolor = bgcolor.blend(essence_->scheme_ptr->item_highlighted, 0.7); /// \todo create a parametre for amount of blend
|
||||
}
|
||||
|
||||
unsigned show_w = (std::min)(content_r.width, width - essence_->scroll.x_offset());
|
||||
@ -4506,12 +4508,28 @@ namespace nana
|
||||
return ess_->lister.relative_pair(pos_);
|
||||
}
|
||||
|
||||
bool item_proxy::displayed() const
|
||||
{
|
||||
if (!ess_->lister.get(pos_.cat)->expand)
|
||||
return false;
|
||||
|
||||
auto pos = to_display();
|
||||
if (ess_->scroll.offset_y_dpl > pos)
|
||||
return false;
|
||||
|
||||
auto size = ess_->number_of_lister_items(false);
|
||||
|
||||
auto last = ess_->lister.advance(ess_->scroll.offset_y_dpl, size);
|
||||
|
||||
return (last > pos || last == pos);
|
||||
}
|
||||
|
||||
bool item_proxy::empty() const
|
||||
{
|
||||
return !ess_;
|
||||
}
|
||||
|
||||
item_proxy & item_proxy::check(bool ck)
|
||||
item_proxy & item_proxy::check(bool ck, bool scroll_view)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & m = cat_->items.at(pos_.item);
|
||||
@ -4520,6 +4538,15 @@ namespace nana
|
||||
m.flags.checked = ck;
|
||||
ess_->lister.emit_checked(pos_);
|
||||
|
||||
if (scroll_view)
|
||||
{
|
||||
if (ess_->lister.get(pos_.cat)->expand)
|
||||
ess_->lister.get(pos_.cat)->expand = false;
|
||||
|
||||
if (!this->displayed())
|
||||
ess_->lister.scroll(pos_, !(ess_->scroll.offset_y_dpl > this->to_display()));
|
||||
}
|
||||
|
||||
ess_->update();
|
||||
}
|
||||
return *this;
|
||||
@ -4531,26 +4558,37 @@ namespace nana
|
||||
}
|
||||
|
||||
/// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected
|
||||
item_proxy & item_proxy::select(bool s)
|
||||
item_proxy & item_proxy::select(bool sel, bool scroll_view)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
|
||||
//pos_ never represents a category if this item_proxy is available.
|
||||
auto & m = cat_->items.at(pos_.item); // a ref to the real item
|
||||
if(m.flags.selected == s) return *this; // ignore if no change
|
||||
m.flags.selected = s; // actually change selection
|
||||
|
||||
ess_->lister.emit_selected(this->pos_);
|
||||
|
||||
if (m.flags.selected)
|
||||
if (m.flags.selected != sel)
|
||||
{
|
||||
ess_->lister.cancel_others_if_single_enabled(true, pos_); //Cancel all selections except pos_ if single_selection is enabled.
|
||||
ess_->lister.last_selected_abs = pos_;
|
||||
}
|
||||
else if (ess_->lister.last_selected_abs == pos_)
|
||||
m.flags.selected = sel; // actually change selection
|
||||
|
||||
ess_->lister.emit_selected(this->pos_);
|
||||
|
||||
if (m.flags.selected)
|
||||
{
|
||||
ess_->lister.cancel_others_if_single_enabled(true, pos_); //Cancel all selections except pos_ if single_selection is enabled.
|
||||
ess_->lister.last_selected_abs = pos_;
|
||||
}
|
||||
else if (ess_->lister.last_selected_abs == pos_)
|
||||
ess_->lister.last_selected_abs.set_both(npos);
|
||||
|
||||
ess_->update();
|
||||
if (scroll_view)
|
||||
{
|
||||
if (ess_->lister.get(pos_.cat)->expand)
|
||||
ess_->lister.get(pos_.cat)->expand = false;
|
||||
|
||||
if (!this->displayed())
|
||||
ess_->lister.scroll(pos_, !(ess_->scroll.offset_y_dpl > this->to_display()));
|
||||
}
|
||||
|
||||
ess_->update();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
* @file: nana/gui/widgets/menu.cpp
|
||||
* @contributors:
|
||||
* kmribti(pr#102)
|
||||
* dankan1890(pr#158)
|
||||
*/
|
||||
|
||||
#include <nana/gui/widgets/menu.hpp>
|
||||
@ -1116,9 +1117,9 @@ namespace nana
|
||||
delete impl_;
|
||||
}
|
||||
|
||||
auto menu::append(const std::string& text, const event_fn_t& callback) -> item_proxy
|
||||
auto menu::append(std::string text_utf8, const menu::event_fn_t& callback) -> item_proxy
|
||||
{
|
||||
impl_->mbuilder.data().items.emplace_back(new item_type(text, callback));
|
||||
impl_->mbuilder.data().items.emplace_back(new item_type(std::move(text_utf8), callback));
|
||||
return item_proxy(size() - 1, *impl_->mbuilder.data().items.back());
|
||||
}
|
||||
|
||||
@ -1156,6 +1157,11 @@ namespace nana
|
||||
impl_->mbuilder.data().items.at(index)->image = img;
|
||||
}
|
||||
|
||||
void menu::text(std::size_t index, std::string text_utf8)
|
||||
{
|
||||
impl_->mbuilder.data().items.at(index)->text.swap(text_utf8);
|
||||
}
|
||||
|
||||
bool menu::link(std::size_t index, menu& menu_obj)
|
||||
{
|
||||
if(impl_->mbuilder.set_sub_menu(index, menu_obj.impl_->mbuilder.data()))
|
||||
|
262
source/paint/detail/image_ico_ex.hpp
Normal file
262
source/paint/detail/image_ico_ex.hpp
Normal file
@ -0,0 +1,262 @@
|
||||
#ifndef NANA_PAINT_DETAIL_IMAGE_ICO_EX_HPP
|
||||
#define NANA_PAINT_DETAIL_IMAGE_ICO_EX_HPP
|
||||
|
||||
#include "image_pixbuf.hpp"
|
||||
|
||||
namespace nana {
|
||||
namespace paint {
|
||||
namespace detail {
|
||||
// These next two structs represent how the icon information is stored
|
||||
// in an ICO file.
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bWidth; // Width of the image
|
||||
uint8_t bHeight; // Height of the image (times 2)
|
||||
uint8_t bColorCount; // Number of colors in image (0 if >=8bpp)
|
||||
uint8_t bReserved; // Reserved
|
||||
uint16_t wPlanes; // Color Planes
|
||||
uint16_t wBitCount; // Bits per pixel
|
||||
uint32_t dwBytesInRes; // how many bytes in this resource?
|
||||
uint32_t dwImageOffset; // where in the file is this image
|
||||
} ICONDIRENTRY, *LPICONDIRENTRY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t idReserved; // Reserved
|
||||
uint16_t idType; // resource type (1 for icons)
|
||||
uint16_t idCount; // how many images?
|
||||
//ICONDIRENTRY idEntries[1]; // the entries for each image
|
||||
} ICONDIR, *LPICONDIR;
|
||||
|
||||
// size - 40 bytes
|
||||
typedef struct
|
||||
{
|
||||
uint32_t biSize;
|
||||
uint32_t biWidth;
|
||||
uint32_t biHeight; // Icon Height (added height of XOR-Bitmap and AND-Bitmap)
|
||||
uint16_t biPlanes;
|
||||
uint16_t biBitCount;
|
||||
uint32_t biCompression;
|
||||
int32_t biSizeImage;
|
||||
uint32_t biXPelsPerMeter;
|
||||
uint32_t biYPelsPerMeter;
|
||||
uint32_t biClrUsed;
|
||||
uint32_t biClrImportant;
|
||||
} s_BITMAPINFOHEADER, *s_PBITMAPINFOHEADER;
|
||||
|
||||
// 46 bytes
|
||||
typedef struct
|
||||
{
|
||||
s_BITMAPINFOHEADER icHeader; // DIB header
|
||||
uint32_t icColors[1]; // Color table (short 4 bytes) //RGBQUAD
|
||||
uint8_t icXOR[1]; // DIB bits for XOR mask
|
||||
uint8_t icAND[1]; // DIB bits for AND mask
|
||||
} ICONIMAGE, *LPICONIMAGE;
|
||||
|
||||
|
||||
class image_ico_ex
|
||||
: public basic_image_pixbuf
|
||||
{
|
||||
bool _m_read_ico(const void* data, std::size_t size)
|
||||
{
|
||||
auto width = 0;
|
||||
auto height = 0;
|
||||
auto buffer = (unsigned char *)data;
|
||||
auto icoDir = reinterpret_cast<LPICONDIR>(buffer);
|
||||
int iconsCount = icoDir->idCount;
|
||||
if (icoDir->idReserved != 0 || icoDir->idType != 1 || iconsCount == 0 || iconsCount > 20)
|
||||
return false;
|
||||
|
||||
auto cursor = buffer;
|
||||
cursor += 6;
|
||||
auto dirEntry = reinterpret_cast<ICONDIRENTRY*>(cursor);
|
||||
auto maxSize = 0;
|
||||
auto offset = 0;
|
||||
auto maxBitCount = 0;
|
||||
for (auto i = 0; i < iconsCount; i++, ++dirEntry)
|
||||
{
|
||||
int w = dirEntry->bWidth;
|
||||
int h = dirEntry->bHeight;
|
||||
int bitCount = dirEntry->wBitCount;
|
||||
if (w * h > maxSize || bitCount > maxBitCount) // we choose icon with max resolution
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
offset = dirEntry->dwImageOffset;
|
||||
maxSize = w * h;
|
||||
maxBitCount = bitCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == 0) return false;
|
||||
|
||||
cursor = buffer;
|
||||
cursor += offset;
|
||||
auto icon = reinterpret_cast<ICONIMAGE*>(cursor);
|
||||
auto realBitsCount = static_cast<int>(icon->icHeader.biBitCount);
|
||||
auto hasAndMask = (realBitsCount < 32) && (height != icon->icHeader.biHeight);
|
||||
cursor += 40;
|
||||
pixbuf_.open(width, height);
|
||||
|
||||
// rgba + vertical swap
|
||||
if (realBitsCount >= 32)
|
||||
{
|
||||
for (auto x = 0; x < width; ++x)
|
||||
for (auto y = 0; y < height; ++y)
|
||||
{
|
||||
pixbuf_.alpha_channel(true);
|
||||
auto shift2 = 4 * (x + (height - y - 1) * width);
|
||||
pixel_color_t image;
|
||||
image.element.red = cursor[shift2 + 2];
|
||||
image.element.green = cursor[shift2 + 1];
|
||||
image.element.blue = cursor[shift2];
|
||||
image.element.alpha_channel = cursor[shift2 + 3];
|
||||
pixbuf_.pixel(x, y, image);
|
||||
}
|
||||
}
|
||||
|
||||
else if (realBitsCount == 24)
|
||||
{
|
||||
for (auto x = 0; x < width; x++)
|
||||
for (auto y = 0; y < height; y++)
|
||||
{
|
||||
pixbuf_.alpha_channel(true);
|
||||
auto shift2 = 3 * (x + (height - y - 1) * width);
|
||||
pixel_color_t image;
|
||||
image.element.red = cursor[shift2 + 2];
|
||||
image.element.green = cursor[shift2 + 1];
|
||||
image.element.blue = cursor[shift2];
|
||||
image.element.alpha_channel = 255;
|
||||
pixbuf_.pixel(x, y, image);
|
||||
}
|
||||
}
|
||||
|
||||
else if (realBitsCount == 8) /// 256 colors
|
||||
{
|
||||
// 256 color table
|
||||
auto colors = reinterpret_cast<unsigned char *>(cursor);
|
||||
cursor += 256 * 4;
|
||||
for (auto x = 0; x < width; x++)
|
||||
for (auto y = 0; y < height; y++)
|
||||
{
|
||||
pixbuf_.alpha_channel(true);
|
||||
|
||||
auto shift2 = (x + (height - y - 1) * width);
|
||||
auto index = 4 * cursor[shift2];
|
||||
pixel_color_t image;
|
||||
image.element.red = colors[index + 2];
|
||||
image.element.green = colors[index + 1];
|
||||
image.element.blue = colors[index];
|
||||
image.element.alpha_channel = 255;
|
||||
pixbuf_.pixel(x, y, image);
|
||||
}
|
||||
}
|
||||
|
||||
else if (realBitsCount == 4) /// 16 colors
|
||||
{
|
||||
// 16 color table
|
||||
auto colors = reinterpret_cast<unsigned char *>(cursor);
|
||||
cursor += 16 * 4;
|
||||
for (auto x = 0; x < width; x++)
|
||||
for (auto y = 0; y < height; y++)
|
||||
{
|
||||
auto shift2 = (x + (height - y - 1) * width);
|
||||
auto index = cursor[shift2 / 2];
|
||||
if (shift2 % 2 == 0)
|
||||
index = (index >> 4) & 0xF;
|
||||
else
|
||||
index = index & 0xF;
|
||||
index *= 4;
|
||||
pixbuf_.alpha_channel(true);
|
||||
pixel_color_t image;
|
||||
image.element.red = colors[index + 2];
|
||||
image.element.green = colors[index + 1];
|
||||
image.element.blue = colors[index];
|
||||
image.element.alpha_channel = 255;
|
||||
pixbuf_.pixel(x, y, image);
|
||||
}
|
||||
}
|
||||
|
||||
else if (realBitsCount == 1) /// 2 colors
|
||||
{
|
||||
// 2 color table
|
||||
auto colors = reinterpret_cast<unsigned char *>(cursor);
|
||||
cursor += 2 * 4;
|
||||
auto boundary = width; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico)
|
||||
while (boundary % 32 != 0) boundary++;
|
||||
for (auto x = 0; x < width; x++)
|
||||
for (auto y = 0; y < height; y++)
|
||||
{
|
||||
auto shift2 = (x + (height - y - 1) * boundary);
|
||||
auto index = cursor[shift2 / 8];
|
||||
|
||||
// select 1 bit only
|
||||
unsigned char bit = 7 - (x % 8);
|
||||
index = (index >> bit) & 0x01;
|
||||
index *= 4;
|
||||
pixbuf_.alpha_channel(true);
|
||||
pixel_color_t image;
|
||||
image.element.red = colors[index + 2];
|
||||
image.element.green = colors[index + 1];
|
||||
image.element.blue = colors[index];
|
||||
image.element.alpha_channel = 255;
|
||||
pixbuf_.pixel(x, y, image);
|
||||
}
|
||||
}
|
||||
|
||||
// Read AND mask after base color data - 1 BIT MASK
|
||||
if (hasAndMask)
|
||||
{
|
||||
auto boundary = width * realBitsCount; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico)
|
||||
while (boundary % 32 != 0) boundary++;
|
||||
cursor += boundary * height / 8;
|
||||
boundary = width;
|
||||
while (boundary % 32 != 0) boundary++;
|
||||
for (auto y = 0; y < height; y++)
|
||||
for (auto x = 0; x < width; x++)
|
||||
{
|
||||
unsigned char bit = 7 - (x % 8);
|
||||
auto shift2 = (x + (height - y - 1) * boundary) / 8;
|
||||
auto mask = (0x01 & (static_cast<unsigned char>(cursor[shift2]) >> bit));
|
||||
auto pc = pixbuf_.pixel(x, y);
|
||||
auto alpha = pc.element.alpha_channel;
|
||||
alpha *= 1 - mask;
|
||||
pc.element.alpha_channel = alpha;
|
||||
pixbuf_.pixel(x, y, pc);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
|
||||
bool open(const std::experimental::filesystem::path& ico_file) override
|
||||
{
|
||||
std::ifstream file(ico_file.string(), std::ios::binary);
|
||||
if (!file.is_open()) return false;
|
||||
|
||||
// allocates a buffer for the image
|
||||
file.seekg(0, std::ios::end);
|
||||
const auto bytes = static_cast<std::size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
auto buffer = new char[bytes];
|
||||
|
||||
// read data from the file and set them in the buffer
|
||||
file.read(buffer, bytes);
|
||||
auto okret = _m_read_ico(buffer, bytes);
|
||||
|
||||
// delete buffer and return
|
||||
delete[] buffer;
|
||||
return okret;
|
||||
}
|
||||
|
||||
bool open(const void* data, std::size_t bytes) override
|
||||
{
|
||||
return _m_read_ico(data, bytes);
|
||||
}
|
||||
|
||||
};
|
||||
}//end namespace detail
|
||||
}//end namespace paint
|
||||
}//end namespace nana
|
||||
|
||||
#endif
|
@ -35,6 +35,7 @@
|
||||
#include "detail/image_ico.hpp"
|
||||
|
||||
#include "image_accessor.hpp"
|
||||
#include "detail/image_ico_ex.hpp"
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
@ -264,11 +265,7 @@ namespace paint
|
||||
{
|
||||
if (ext_ico == ext)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
ptr = std::make_shared<detail::image_ico>(true);
|
||||
#else
|
||||
return ptr;
|
||||
#endif
|
||||
ptr = std::make_shared<detail::image_ico_ex>();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -367,6 +364,8 @@ namespace paint
|
||||
ptr = std::make_shared<detail::image_ico>(true);
|
||||
}
|
||||
#endif
|
||||
else if (!ptr && bytes > 40 && (0x00010000 == *reinterpret_cast<const unsigned*>(data)))
|
||||
ptr = std::make_shared<detail::image_ico_ex>();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user