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);
|
void bind(window handle);
|
||||||
window window_handle() const;
|
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.
|
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.
|
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)
|
* http://www.boost.org/LICENSE_1_0.txt)
|
||||||
*
|
*
|
||||||
* @file: nana/gui/widgets/listbox.hpp
|
* @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
|
#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()
|
/// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort()
|
||||||
index_pair to_display() const;
|
index_pair to_display() const;
|
||||||
|
|
||||||
|
/// Determines whether the item is displayed on the screen
|
||||||
|
bool displayed() const;
|
||||||
|
|
||||||
bool empty() 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;
|
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;
|
bool selected() const;
|
||||||
|
|
||||||
item_proxy & bgcolor(const nana::color&);
|
item_proxy & bgcolor(const nana::color&);
|
||||||
@ -1130,9 +1144,11 @@ namespace nana
|
|||||||
: public widget_geometrics
|
: public widget_geometrics
|
||||||
{
|
{
|
||||||
color_proxy header_bgcolor{static_cast<color_rgb>(0xf1f2f4)};
|
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_grabbed{ static_cast<color_rgb>(0x8BD6F6)};
|
||||||
color_proxy header_floated{ static_cast<color_rgb>(0xBABBBC)};
|
color_proxy header_floated{ static_cast<color_rgb>(0xBABBBC)};
|
||||||
color_proxy item_selected{ static_cast<color_rgb>(0xD5EFFC) };
|
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.
|
/// 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 };
|
unsigned max_fit_content{ 0 };
|
||||||
@ -1435,7 +1451,7 @@ the nana::detail::basic_window member pointer scheme
|
|||||||
export_options& def_export_options();
|
export_options& def_export_options();
|
||||||
private:
|
private:
|
||||||
drawerbase::listbox::essence & _m_ess() const;
|
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);
|
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*);
|
void _m_erase_key(nana::detail::key_interface*);
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* A Menu implementation
|
* A Menu implementation
|
||||||
* Nana C++ Library(http://www.nanapro.org)
|
* 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.
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
* http://www.boost.org/LICENSE_1_0.txt)
|
* http://www.boost.org/LICENSE_1_0.txt)
|
||||||
*
|
*
|
||||||
* @file: nana/gui/widgets/menu.hpp
|
* @file: nana/gui/widgets/menu.hpp
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef NANA_GUI_WIDGETS_MENU_HPP
|
#ifndef NANA_GUI_WIDGETS_MENU_HPP
|
||||||
@ -122,12 +123,13 @@ namespace nana
|
|||||||
~menu();
|
~menu();
|
||||||
|
|
||||||
/// Appends an item to the 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 append_splitter();
|
||||||
void clear(); ///< Erases all of the items.
|
void clear(); ///< Erases all of the items.
|
||||||
/// Closes the menu. It does not destroy the menu; just close the window for the menu.
|
/// Closes the menu. It does not destroy the menu; just close the window for the menu.
|
||||||
void close();
|
void close();
|
||||||
void image(std::size_t pos, const paint::image& icon);
|
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 check_style(std::size_t pos, checks);
|
||||||
void checked(std::size_t pos, bool);
|
void checked(std::size_t pos, bool);
|
||||||
bool checked(std::size_t pos) const;
|
bool checked(std::size_t pos) const;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <nana/deploy.hpp>
|
||||||
|
|
||||||
namespace nana
|
namespace nana
|
||||||
{
|
{
|
||||||
|
@ -201,7 +201,7 @@ namespace nana
|
|||||||
#if defined(NANA_WINDOWS)
|
#if defined(NANA_WINDOWS)
|
||||||
void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam)
|
void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam)
|
||||||
{
|
{
|
||||||
arg_notifier arg;
|
arg_notifier arg = {};
|
||||||
switch (lparam)
|
switch (lparam)
|
||||||
{
|
{
|
||||||
case WM_LBUTTONDBLCLK:
|
case WM_LBUTTONDBLCLK:
|
||||||
@ -321,6 +321,7 @@ namespace nana
|
|||||||
|
|
||||||
void notifier::icon(const std::string& icon_file)
|
void notifier::icon(const std::string& icon_file)
|
||||||
{
|
{
|
||||||
|
#if defined(NANA_WINDOWS)
|
||||||
paint::image image_ico{ icon_file };
|
paint::image image_ico{ icon_file };
|
||||||
auto icon_handle = paint::image_accessor::icon(image_ico);
|
auto icon_handle = paint::image_accessor::icon(image_ico);
|
||||||
if (icon_handle)
|
if (icon_handle)
|
||||||
@ -330,14 +331,17 @@ namespace nana
|
|||||||
impl_->set_icon(image_ico);
|
impl_->set_icon(image_ico);
|
||||||
impl_->icon = image_ico;
|
impl_->icon = image_ico;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifier::insert_icon(const std::string& icon_file)
|
void notifier::insert_icon(const std::string& icon_file)
|
||||||
{
|
{
|
||||||
|
#if defined(NANA_WINDOWS)
|
||||||
paint::image image_ico{ icon_file };
|
paint::image image_ico{ icon_file };
|
||||||
auto icon_handle = paint::image_accessor::icon(image_ico);
|
auto icon_handle = paint::image_accessor::icon(image_ico);
|
||||||
if (icon_handle)
|
if (icon_handle)
|
||||||
impl_->icons.emplace_back(static_cast<paint::image&&>(image_ico));
|
impl_->icons.emplace_back(static_cast<paint::image&&>(image_ico));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifier::period(unsigned ms)
|
void notifier::period(unsigned ms)
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
* http://www.boost.org/LICENSE_1_0.txt)
|
* http://www.boost.org/LICENSE_1_0.txt)
|
||||||
*
|
*
|
||||||
* @file: nana/gui/place.cpp
|
* @file: nana/gui/place.cpp
|
||||||
* @contributors: Ariel Vina-Rodriguez
|
* @contributors: Ariel Vina-Rodriguez
|
||||||
|
* dankan1890(PR#156)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
@ -56,7 +57,7 @@ namespace nana
|
|||||||
{
|
{
|
||||||
div_start, div_end, splitter,
|
div_start, div_end, splitter,
|
||||||
identifier, dock, vert, grid, number, array, reparray,
|
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,
|
collapse, parameters,
|
||||||
equal,
|
equal,
|
||||||
eof, error
|
eof, error
|
||||||
@ -275,7 +276,7 @@ namespace nana
|
|||||||
_m_throw_error("a parameter list is required after 'collapse'");
|
_m_throw_error("a parameter list is required after 'collapse'");
|
||||||
return token::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())
|
switch (idstr_.front())
|
||||||
{
|
{
|
||||||
@ -283,8 +284,11 @@ namespace nana
|
|||||||
case 'r': return token::right;
|
case 'r': return token::right;
|
||||||
case 't': return token::top;
|
case 't': return token::top;
|
||||||
case 'b': return token::bottom;
|
case 'b': return token::bottom;
|
||||||
|
case 'u': return token::undisplayed;
|
||||||
|
case 'i': return token::invisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return token::identifier;
|
return token::identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,8 +411,18 @@ namespace nana
|
|||||||
sp = _m_eat_whitespace(sp);
|
sp = _m_eat_whitespace(sp);
|
||||||
if ('%' == *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());
|
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 + 1;
|
||||||
}
|
}
|
||||||
return sp - allstart;
|
return sp - allstart;
|
||||||
@ -427,6 +441,87 @@ namespace nana
|
|||||||
}; //end class tokenizer
|
}; //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 implement
|
||||||
struct place::implement
|
struct place::implement
|
||||||
{
|
{
|
||||||
@ -442,6 +537,8 @@ namespace nana
|
|||||||
|
|
||||||
window window_handle{nullptr};
|
window window_handle{nullptr};
|
||||||
event_handle event_size_handle{nullptr};
|
event_handle event_size_handle{nullptr};
|
||||||
|
|
||||||
|
std::string div_text;
|
||||||
std::unique_ptr<division> root_division;
|
std::unique_ptr<division> root_division;
|
||||||
std::map<std::string, field_gather*> fields;
|
std::map<std::string, field_gather*> fields;
|
||||||
std::map<std::string, field_dock*> docks;
|
std::map<std::string, field_dock*> docks;
|
||||||
@ -1309,8 +1406,9 @@ namespace nana
|
|||||||
|
|
||||||
enum{splitter_px = 4};
|
enum{splitter_px = 4};
|
||||||
public:
|
public:
|
||||||
div_splitter(place_parts::number_t init_weight)
|
div_splitter(place_parts::number_t init_weight, implement* impl):
|
||||||
: division(kind::splitter, std::string()),
|
division(kind::splitter, std::string()),
|
||||||
|
impl_(impl),
|
||||||
init_weight_(init_weight)
|
init_weight_(init_weight)
|
||||||
{
|
{
|
||||||
this->weight.assign(splitter_px);
|
this->weight.assign(splitter_px);
|
||||||
@ -1335,7 +1433,7 @@ namespace nana
|
|||||||
|
|
||||||
auto grab_fn = [this](const arg_mouse& arg)
|
auto grab_fn = [this](const arg_mouse& arg)
|
||||||
{
|
{
|
||||||
if (false == arg.left_button)
|
if ((false == arg.left_button) && (mouse::left_button != arg.button))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event_code::mouse_down == arg.evt_code)
|
if (event_code::mouse_down == arg.evt_code)
|
||||||
@ -1368,6 +1466,7 @@ namespace nana
|
|||||||
else if(event_code::mouse_up == arg.evt_code)
|
else if(event_code::mouse_up == arg.evt_code)
|
||||||
{
|
{
|
||||||
grabbed_ = false;
|
grabbed_ = false;
|
||||||
|
this->_m_update_div(impl_->div_text);
|
||||||
}
|
}
|
||||||
else if (event_code::mouse_move == arg.evt_code)
|
else if (event_code::mouse_move == arg.evt_code)
|
||||||
{
|
{
|
||||||
@ -1466,6 +1565,231 @@ namespace nana
|
|||||||
splitter_.move(this->field_area);
|
splitter_.move(this->field_area);
|
||||||
}
|
}
|
||||||
private:
|
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
|
division * _m_leaf_left() const
|
||||||
{
|
{
|
||||||
return previous();
|
return previous();
|
||||||
@ -1526,6 +1850,7 @@ namespace nana
|
|||||||
return area;
|
return area;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
implement* const impl_;
|
||||||
nana::cursor splitter_cursor_{nana::cursor::arrow};
|
nana::cursor splitter_cursor_{nana::cursor::arrow};
|
||||||
place_parts::splitter<true> splitter_;
|
place_parts::splitter<true> splitter_;
|
||||||
nana::point begin_point_;
|
nana::point begin_point_;
|
||||||
@ -1885,7 +2210,7 @@ namespace nana
|
|||||||
unsigned vert_count = 0, horz_count = 0;
|
unsigned vert_count = 0, horz_count = 0;
|
||||||
|
|
||||||
bool is_first = true;
|
bool is_first = true;
|
||||||
bool prev_attr;
|
bool prev_attr = false;
|
||||||
|
|
||||||
for (auto & child : children)
|
for (auto & child : children)
|
||||||
{
|
{
|
||||||
@ -1962,7 +2287,7 @@ namespace nana
|
|||||||
child_dv->splitter.reset();
|
child_dv->splitter.reset();
|
||||||
|
|
||||||
::nana::rectangle child_r;
|
::nana::rectangle child_r;
|
||||||
double split_range_begin = -1, split_range_end;
|
double split_range_begin = -1, split_range_end = 0;
|
||||||
switch (child->dir)
|
switch (child->dir)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
@ -2147,6 +2472,9 @@ namespace nana
|
|||||||
std::vector<std::unique_ptr<division>> children;
|
std::vector<std::unique_ptr<division>> children;
|
||||||
::nana::direction div_dir = ::nana::direction::west;
|
::nana::direction div_dir = ::nana::direction::west;
|
||||||
|
|
||||||
|
bool undisplayed = false;
|
||||||
|
bool invisible = false;
|
||||||
|
|
||||||
for (token tk = tknizer.read(); tk != token::eof; tk = tknizer.read())
|
for (token tk = tknizer.read(); tk != token::eof; tk = tknizer.read())
|
||||||
{
|
{
|
||||||
bool exit_for = false;
|
bool exit_for = false;
|
||||||
@ -2162,7 +2490,7 @@ namespace nana
|
|||||||
//Ignore the splitter when there is not a division.
|
//Ignore the splitter when there is not a division.
|
||||||
if (!children.empty() && (division::kind::splitter != children.back()->kind_of_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.back()->div_next = splitter;
|
||||||
children.emplace_back(std::unique_ptr<division>{ splitter });
|
children.emplace_back(std::unique_ptr<division>{ splitter });
|
||||||
}
|
}
|
||||||
@ -2305,6 +2633,10 @@ namespace nana
|
|||||||
div_dir = ::nana::direction::north; break;
|
div_dir = ::nana::direction::north; break;
|
||||||
case token::bottom:
|
case token::bottom:
|
||||||
div_dir = ::nana::direction::south; break;
|
div_dir = ::nana::direction::south; break;
|
||||||
|
case token::undisplayed:
|
||||||
|
undisplayed = true; break;
|
||||||
|
case token::invisible:
|
||||||
|
invisible = true; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
if (exit_for)
|
if (exit_for)
|
||||||
@ -2430,6 +2762,9 @@ namespace nana
|
|||||||
div->children.swap(children);
|
div->children.swap(children);
|
||||||
div->margin = std::move(margin);
|
div->margin = std::move(margin);
|
||||||
div->dir = div_dir;
|
div->dir = div_dir;
|
||||||
|
|
||||||
|
div->display = !undisplayed;
|
||||||
|
div->visible = !(undisplayed || invisible);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2593,6 +2928,7 @@ namespace nana
|
|||||||
impl_->connect(div.get());
|
impl_->connect(div.get());
|
||||||
impl_->root_division.reset(); //clear atachments div-fields
|
impl_->root_division.reset(); //clear atachments div-fields
|
||||||
impl_->root_division.swap(div);
|
impl_->root_division.swap(div);
|
||||||
|
impl_->div_text.assign(s);
|
||||||
}
|
}
|
||||||
catch (...)
|
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)
|
void place::modify(const char* name, const char* div_text)
|
||||||
{
|
{
|
||||||
if (nullptr == div_text)
|
if (nullptr == div_text)
|
||||||
@ -2655,6 +3001,7 @@ namespace nana
|
|||||||
impl_->check_unique(impl_->root_division.get());
|
impl_->check_unique(impl_->root_division.get());
|
||||||
impl_->connect(impl_->root_division.get());
|
impl_->connect(impl_->root_division.get());
|
||||||
impl_->tmp_replaced.reset();
|
impl_->tmp_replaced.reset();
|
||||||
|
update_div(impl_->div_text, name, div_text, update_operation::replace);
|
||||||
|
|
||||||
modified_ptr->div_owner = div_owner;
|
modified_ptr->div_owner = div_owner;
|
||||||
modified_ptr->div_next = div_next;
|
modified_ptr->div_next = div_next;
|
||||||
@ -2698,6 +3045,71 @@ namespace nana
|
|||||||
return *p;
|
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)
|
void place::field_visible(const char* name, bool vsb)
|
||||||
{
|
{
|
||||||
if (!name) name = "";
|
if (!name) name = "";
|
||||||
@ -2707,7 +3119,10 @@ namespace nana
|
|||||||
|
|
||||||
auto div = impl_->search_div_name(impl_->root_division.get(), name);
|
auto div = impl_->search_div_name(impl_->root_division.get(), name);
|
||||||
if (div)
|
if (div)
|
||||||
|
{
|
||||||
div->set_visible(vsb);
|
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
|
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);
|
auto div = impl_->search_div_name(impl_->root_division.get(), name);
|
||||||
if (div)
|
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);
|
div->set_display(dsp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool place::field_display(const char* name) const
|
bool place::field_display(const char* name) const
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
* leobackes(pr#86,pr#97)
|
* leobackes(pr#86,pr#97)
|
||||||
* Benjamin Navarro(pr#81)
|
* Benjamin Navarro(pr#81)
|
||||||
* besh81(pr#130)
|
* besh81(pr#130)
|
||||||
*
|
* dankan1890(pr#158)
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <nana/gui/widgets/listbox.hpp>
|
#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);
|
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;
|
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;
|
auto state = item_state::normal;
|
||||||
//check whether grabing an item, if item_spliter_ != npos, that indicates the grab item is a spliter.
|
//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)
|
if (item.flags.selected)
|
||||||
bgcolor = bgcolor.blend(colors::black, 0.98); // or "selected"
|
bgcolor = bgcolor.blend(colors::black, 0.98); // or "selected"
|
||||||
else
|
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());
|
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_);
|
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
|
bool item_proxy::empty() const
|
||||||
{
|
{
|
||||||
return !ess_;
|
return !ess_;
|
||||||
}
|
}
|
||||||
|
|
||||||
item_proxy & item_proxy::check(bool ck)
|
item_proxy & item_proxy::check(bool ck, bool scroll_view)
|
||||||
{
|
{
|
||||||
internal_scope_guard lock;
|
internal_scope_guard lock;
|
||||||
auto & m = cat_->items.at(pos_.item);
|
auto & m = cat_->items.at(pos_.item);
|
||||||
@ -4520,6 +4538,15 @@ namespace nana
|
|||||||
m.flags.checked = ck;
|
m.flags.checked = ck;
|
||||||
ess_->lister.emit_checked(pos_);
|
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();
|
ess_->update();
|
||||||
}
|
}
|
||||||
return *this;
|
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
|
/// 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;
|
internal_scope_guard lock;
|
||||||
|
|
||||||
//pos_ never represents a category if this item_proxy is available.
|
//pos_ never represents a category if this item_proxy is available.
|
||||||
auto & m = cat_->items.at(pos_.item); // a ref to the real item
|
auto & m = cat_->items.at(pos_.item); // a ref to the real item
|
||||||
if(m.flags.selected == s) return *this; // ignore if no change
|
if (m.flags.selected != sel)
|
||||||
m.flags.selected = s; // 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.
|
m.flags.selected = sel; // actually change selection
|
||||||
ess_->lister.last_selected_abs = pos_;
|
|
||||||
}
|
ess_->lister.emit_selected(this->pos_);
|
||||||
else if (ess_->lister.last_selected_abs == 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_->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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
* @file: nana/gui/widgets/menu.cpp
|
* @file: nana/gui/widgets/menu.cpp
|
||||||
* @contributors:
|
* @contributors:
|
||||||
* kmribti(pr#102)
|
* kmribti(pr#102)
|
||||||
|
* dankan1890(pr#158)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <nana/gui/widgets/menu.hpp>
|
#include <nana/gui/widgets/menu.hpp>
|
||||||
@ -1116,9 +1117,9 @@ namespace nana
|
|||||||
delete impl_;
|
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());
|
return item_proxy(size() - 1, *impl_->mbuilder.data().items.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1156,6 +1157,11 @@ namespace nana
|
|||||||
impl_->mbuilder.data().items.at(index)->image = img;
|
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)
|
bool menu::link(std::size_t index, menu& menu_obj)
|
||||||
{
|
{
|
||||||
if(impl_->mbuilder.set_sub_menu(index, menu_obj.impl_->mbuilder.data()))
|
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 "detail/image_ico.hpp"
|
||||||
|
|
||||||
#include "image_accessor.hpp"
|
#include "image_accessor.hpp"
|
||||||
|
#include "detail/image_ico_ex.hpp"
|
||||||
|
|
||||||
namespace fs = std::experimental::filesystem;
|
namespace fs = std::experimental::filesystem;
|
||||||
|
|
||||||
@ -264,11 +265,7 @@ namespace paint
|
|||||||
{
|
{
|
||||||
if (ext_ico == ext)
|
if (ext_ico == ext)
|
||||||
{
|
{
|
||||||
#if defined(NANA_WINDOWS)
|
ptr = std::make_shared<detail::image_ico_ex>();
|
||||||
ptr = std::make_shared<detail::image_ico>(true);
|
|
||||||
#else
|
|
||||||
return ptr;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,6 +364,8 @@ namespace paint
|
|||||||
ptr = std::make_shared<detail::image_ico>(true);
|
ptr = std::make_shared<detail::image_ico>(true);
|
||||||
}
|
}
|
||||||
#endif
|
#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