Merge remote-tracking branch 'nana_jinhao/hotfixes-1.0.2' into hotfixes-1.0.2

This commit is contained in:
qPCR4vir 2015-07-10 12:57:08 +02:00
commit 6f101f73b4
14 changed files with 342 additions and 303 deletions

View File

@ -84,6 +84,7 @@ namespace nana
end_of_medium = 0x19, //Ctrl+Y
substitute = 0x1A, //Ctrl+Z
escape = 0x1B,
space = 0x20, //Space
//The following names are intuitive name of ASCII control codes
select_all = start_of_headline,

View File

@ -118,9 +118,11 @@ namespace detail
bool is_ancestor_of(const basic_window* wd) const;
bool visible_parents() const;
bool displayed() const;
bool belong_to_lazy() const;
const basic_window * child_caret() const; //Returns a child which owns a caret
bool is_draw_through() const; ///< Determines whether it is a draw-through window.
bool is_draw_through() const; // Determines whether it is a draw-through window.
public:
//Override event_holder
bool set_events(const std::shared_ptr<general_events>&) override;

View File

@ -23,12 +23,18 @@ namespace nana
{
class widget;
namespace detail
{
class drawer;
}
class drawer_trigger
: ::nana::noncopyable, ::nana::nonmovable
{
friend class detail::drawer;
public:
typedef widget& widget_reference;
typedef paint::graphics& graph_reference;
using widget_reference = widget&;
using graph_reference = paint::graphics&;
virtual ~drawer_trigger();
virtual void attached(widget_reference, graph_reference); //none-const
@ -56,10 +62,11 @@ namespace nana
virtual void key_release(graph_reference, const arg_keyboard&);
virtual void shortkey(graph_reference, const arg_keyboard&);
void _m_reset_overrided();
bool _m_overrided() const;
private:
bool overrided_{false};
void _m_reset_overrided();
bool _m_overrided(event_code) const;
private:
unsigned overrided_{ 0xFFFFFFFF };
};
namespace detail
@ -83,7 +90,7 @@ namespace nana
enum class method_state
{
unknown,
pending,
overrided,
not_overrided
};
@ -128,31 +135,27 @@ namespace nana
template<typename Arg, typename Mfptr>
void _m_emit(event_code evt_code, const Arg& arg, Mfptr mfptr)
{
if (realizer_)
const int pos = static_cast<int>(evt_code);
if (realizer_ && (method_state::not_overrided != mth_state_[pos]))
{
const int pos = static_cast<int>(evt_code);
if (method_state::not_overrided != mth_state_[pos])
_m_bground_pre();
if (method_state::pending == mth_state_[pos])
{
_m_bground_pre();
(realizer_->*mfptr)(graphics, arg);
//Check realizer, when the window is closed in that event handler, the drawer will be
//detached and realizer will be a nullptr
if(realizer_)
mth_state_[pos] = (realizer_->_m_overrided(evt_code) ? method_state::overrided : method_state::not_overrided);
}
else
(realizer_->*mfptr)(graphics, arg);
if (method_state::unknown == mth_state_[pos])
{
realizer_->_m_reset_overrided();
(realizer_->*mfptr)(graphics, arg);
//Check realizer, when the window is closed in that event handler, the drawer will be
//detached and realizer will be a nullptr
if(realizer_)
mth_state_[pos] = (realizer_->_m_overrided() ? method_state::overrided : method_state::not_overrided);
}
else
(realizer_->*mfptr)(graphics, arg);
if (_m_lazy_decleared())
{
_m_draw_dynamic_drawing_object();
_m_bground_end();
}
if (_m_lazy_decleared())
{
_m_draw_dynamic_drawing_object();
_m_bground_end();
}
}
}

View File

@ -23,7 +23,7 @@ namespace nana{
return object;
}
std::size_t weight() const
unsigned weight() const
{
return 2;
}
@ -55,22 +55,23 @@ namespace nana{
}
}
bool render(core_window_t * wd, bool forced, const rectangle* update_area = nullptr)
void render(core_window_t * wd, bool forced, const rectangle* update_area = nullptr)
{
bool rendered = false;
core_window_t * root_wd = wd->root_widget;
auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus;
bool copy_separately = true;
std::vector<std::pair<rectangle, core_window_t*>> rd_set;
if(nimbus.size())
if (wd->root_widget->other.attribute.root->effects_edge_nimbus.size())
{
core_window_t * focused = root_wd->other.attribute.root->focus;
native_window_type native = root_wd->root;
std::size_t pixels = weight();
auto root_wd = wd->root_widget;
auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus;
auto focused = root_wd->other.attribute.root->focus;
const unsigned pixels = weight();
auto graph = root_wd->root_graph;
std::vector<core_window_t*> erase;
std::vector<std::pair<rectangle,core_window_t*>> rd_set;
nana::rectangle r;
for(auto & action : nimbus)
{
@ -80,11 +81,11 @@ namespace nana{
{
if (update_area)
::nana::overlap(*update_area, rectangle(r), r);
rendered = true;
copy_separately = false;
}
//Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered.
if ((forced && (action.window == wd)) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh))
if ((forced && (action.window == wd)) || (focused == action.window) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh))
{
rd_set.emplace_back(r, action.window);
action.rendered = true;
@ -93,29 +94,36 @@ namespace nana{
else if(action.rendered)
{
action.rendered = false;
erase.push_back(action.window);
if (action.window == wd)
copy_separately = false;
::nana::rectangle erase_r(
action.window->pos_root.x - static_cast<int>(pixels),
action.window->pos_root.y - static_cast<int>(pixels),
static_cast<unsigned>(action.window->dimension.width + (pixels << 1)),
static_cast<unsigned>(action.window->dimension.height + (pixels << 1))
);
graph->paste(root_wd->root, erase_r, erase_r.x, erase_r.y);
}
}
//Erase
for(auto el : erase)
{
if(el == wd)
rendered = true;
r.x = el->pos_root.x - static_cast<int>(pixels);
r.y = el->pos_root.y - static_cast<int>(pixels);
r.width = static_cast<unsigned>(el->dimension.width + (pixels << 1));
r.height = static_cast<unsigned>(el->dimension.height + (pixels << 1));
graph->paste(native, r, r.x, r.y);
}
//Render
for (auto & rd : rd_set)
_m_render_edge_nimbus(rd.second, rd.first);
}
return rendered;
if (copy_separately)
{
rectangle vr;
if (window_layer::read_visual_rectangle(wd, vr))
{
if (update_area)
::nana::overlap(*update_area, rectangle(vr), vr);
wd->root_graph->paste(wd->root, vr, vr.x, vr.y);
}
}
//Render
for (auto & rd : rd_set)
_m_render_edge_nimbus(rd.second, rd.first);
}
private:
static bool _m_edge_nimbus(core_window_t * focused_wd, core_window_t * wd)
@ -134,8 +142,8 @@ namespace nana{
nana::rectangle good_r;
if(overlap(r, wd->root_graph->size(), good_r))
{
if( (good_r.x < wd->pos_root.x) || (good_r.y < wd->pos_root.y) ||
(good_r.x + good_r.width > visual.x + visual.width) || (good_r.y + good_r.height > visual.y + visual.height))
if ((good_r.x < wd->pos_root.x) || (good_r.y < wd->pos_root.y) ||
(good_r.right() > visual.right()) || (good_r.bottom() > visual.bottom()))
{
auto graph = wd->root_graph;
nana::paint::pixel_buffer pixbuf(graph->handle(), r);

View File

@ -109,7 +109,7 @@ namespace nana
if(real_visible_state_ != isshow)
{
real_visible_state_ = isshow;
native_interface::caret_visible(wd_->root, isshow);
native_interface::caret_visible(wd_->root, isshow && wd_->displayed());
}
}
@ -282,6 +282,11 @@ namespace nana
return true;
}
bool basic_window::displayed() const
{
return (visible && visible_parents());
}
bool basic_window::belong_to_lazy() const
{
for (auto wd = this; wd; wd = wd->parent)
@ -292,6 +297,26 @@ namespace nana
return false;
}
const basic_window* get_child_caret(const basic_window* wd, bool this_is_a_child)
{
if (this_is_a_child && wd->together.caret)
return wd;
for (auto child : wd->children)
{
auto caret_wd = get_child_caret(child, true);
if (caret_wd)
return caret_wd;
}
return nullptr;
}
const basic_window * basic_window::child_caret() const
{
return get_child_caret(this, false);
}
bool basic_window::is_draw_through() const
{
if (::nana::category::flags::root == this->other.category)

View File

@ -63,15 +63,16 @@ namespace nana
arg.window_handle = reinterpret_cast<window>(wd);
if (emit(event_code::expose, wd, arg, false, get_thread_context()))
{
if (wd->together.caret)
const core_window_t * caret_wd = (wd->together.caret ? wd : wd->child_caret());
if (caret_wd)
{
if (exposed)
{
if (wd->root_widget->other.attribute.root->focus == wd)
wd->together.caret->visible(true);
if (wd->root_widget->other.attribute.root->focus == caret_wd)
caret_wd->together.caret->visible(true);
}
else
wd->together.caret->visible(false);
caret_wd->together.caret->visible(false);
}
if (!exposed)
@ -102,7 +103,7 @@ namespace nana
arg.x = x;
arg.y = y;
if (emit(event_code::move, wd, arg, false, get_thread_context()))
wd_manager.update(wd, true, true);
wd_manager.update(wd, false, true);
}
}

View File

@ -34,99 +34,99 @@ namespace nana
void drawer_trigger::resizing(graph_reference, const arg_resizing&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::resizing));
}
void drawer_trigger::resized(graph_reference graph, const arg_resized&)
{
overrided_ = true;
overrided_ |= (1 << static_cast<int>(event_code::resized));
this->refresh(graph);
detail::bedrock::instance().thread_context_lazy_refresh();
}
void drawer_trigger::move(graph_reference, const arg_move&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::move));
}
void drawer_trigger::click(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::click));
}
void drawer_trigger::dbl_click(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::dbl_click));
}
void drawer_trigger::mouse_enter(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_enter));
}
void drawer_trigger::mouse_move(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_move));
}
void drawer_trigger::mouse_leave(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_leave));
}
void drawer_trigger::mouse_down(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_down));
}
void drawer_trigger::mouse_up(graph_reference, const arg_mouse&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_up));
}
void drawer_trigger::mouse_wheel(graph_reference, const arg_wheel&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_wheel));
}
void drawer_trigger::mouse_dropfiles(graph_reference, const arg_dropfiles&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::mouse_drop));
}
void drawer_trigger::focus(graph_reference, const arg_focus&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::focus));
}
void drawer_trigger::key_press(graph_reference, const arg_keyboard&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::key_press));
}
void drawer_trigger::key_char(graph_reference, const arg_keyboard&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::key_char));
}
void drawer_trigger::key_release(graph_reference, const arg_keyboard&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::key_release));
}
void drawer_trigger::shortkey(graph_reference, const arg_keyboard&)
{
overrided_ = false;
overrided_ &= ~(1 << static_cast<int>(event_code::shortkey));
}
void drawer_trigger::_m_reset_overrided()
{
overrided_ = true;
overrided_ = 0xFFFFFFFF;
}
bool drawer_trigger::_m_overrided() const
bool drawer_trigger::_m_overrided(event_code evt_code) const
{
return overrided_;
return 0 != (overrided_ & (1 << static_cast<int>(evt_code)));
}
//end class drawer_trigger
@ -244,8 +244,8 @@ namespace nana
{
if(wd)
{
bedrock_type::core_window_t* iwd = reinterpret_cast<bedrock_type::core_window_t*>(wd);
bedrock_type::core_window_t * caret_wd = iwd->root_widget->other.attribute.root->focus;
auto iwd = reinterpret_cast<bedrock_type::core_window_t*>(wd);
auto caret_wd = iwd->root_widget->other.attribute.root->focus;
bool owns_caret = (caret_wd && (caret_wd->together.caret) && (caret_wd->together.caret->visible()));
@ -262,16 +262,7 @@ namespace nana
#endif
}
if (false == edge_nimbus_renderer_t::instance().render(iwd, forced, update_area))
{
rectangle vr;
if (bedrock_type::window_manager_t::window_layer::read_visual_rectangle(iwd, vr))
{
if (update_area)
::nana::overlap(*update_area, rectangle(vr), vr);
iwd->root_graph->paste(iwd->root, vr, vr.x, vr.y);
}
}
edge_nimbus_renderer_t::instance().render(iwd, forced, update_area);
if(owns_caret)
{
@ -306,9 +297,10 @@ namespace nana
void drawer::attached(widget& wd, drawer_trigger& realizer)
{
for (auto i = std::begin(mth_state_), end = std::end(mth_state_); i != end; ++i)
*i = method_state::unknown;
*i = method_state::pending;
realizer_ = &realizer;
realizer._m_reset_overrided();
realizer.attached(wd, graphics);
}

View File

@ -937,11 +937,10 @@ namespace detail
if(msgwnd->visible && (msgwnd->root_graph->empty() == false))
{
nana::detail::platform_scope_guard psg;
nana::detail::drawable_impl_type* drawer_impl = msgwnd->root_graph->handle();
::XCopyArea(display, drawer_impl->pixmap, reinterpret_cast<Window>(native_window), drawer_impl->context,
xevent.xexpose.x, xevent.xexpose.y,
xevent.xexpose.width, xevent.xexpose.height,
xevent.xexpose.x, xevent.xexpose.y);
//Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed.
::nana::rectangle update_area(xevent.xexpose.x, xevent.xexpose.y, xevent.xexpose.width, xevent.xexpose.height);
if (!update_area.empty())
msgwnd->drawer.map(reinterpret_cast<window>(msgwnd), true, &update_area);
}
break;
case KeyPress:
@ -1149,7 +1148,9 @@ namespace detail
brock.get_key_state(arg);
brock.emit(event_code::key_release, msgwnd, arg, true, &context);
}
brock.delay_restore(2); //Restores while key release
if (context.platform.keychar < keyboard::os_arrow_left || keyboard::os_arrow_down < wParam)
brock.delay_restore(2); //Restores while key release
}
else
{

View File

@ -363,7 +363,7 @@ namespace detail
void bedrock::pump_event(window modal_window, bool is_modal)
{
const unsigned tid = ::GetCurrentThreadId();
thread_context * context = this->open_thread_context(tid);
auto context = this->open_thread_context(tid);
if(0 == context->window_count)
{
//test if there is not a window
@ -1310,13 +1310,11 @@ namespace detail
::PAINTSTRUCT ps;
::HDC dc = ::BeginPaint(root_window, &ps);
if((ps.rcPaint.left != ps.rcPaint.right) && (ps.rcPaint.bottom != ps.rcPaint.top))
{
::BitBlt(dc,
ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
reinterpret_cast<HDC>(msgwnd->root_graph->handle()->context),
ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
}
//Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed.
::nana::rectangle update_area(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
if (!update_area.empty())
msgwnd->drawer.map(reinterpret_cast<window>(msgwnd), true, &update_area);
::EndPaint(root_window, &ps);
}
break;
@ -1474,7 +1472,10 @@ namespace detail
else
brock.set_keyboard_shortkey(false);
brock.delay_restore(2); //Restores while key release
//Do delay restore if key is not arrow_left/right/up/down, otherwise
//A menubar will be restored if the item is empty(not have a menu item)
if (wParam < 37 || 40 < wParam)
brock.delay_restore(2); //Restores while key release
break;
case WM_CLOSE:
{

View File

@ -59,7 +59,6 @@ namespace nana
if (wd->parent)
{
std::vector<wd_rectangle> blocks;
blocks.reserve(10);
if (read_overlaps(wd, vr, blocks))
{
nana::point p_src;
@ -99,7 +98,7 @@ namespace nana
// The result is a rectangle that is a visible area for its ancesters.
bool window_layout::read_visual_rectangle(core_window_t* wd, nana::rectangle& visual)
{
if (false == wd->visible) return false;
if (! wd->displayed()) return false;
visual = rectangle{ wd->pos_root, wd->dimension };
@ -354,7 +353,7 @@ namespace nana
nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension);
for (auto wd : data_sect.effects_bground_windows)
{
if (wd == sigwd || !wd->visible || !wd->visible_parents() ||
if (wd == sigwd || !wd->displayed() ||
(false == overlap(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd)))
continue;

View File

@ -698,7 +698,7 @@ namespace detail
std::lock_guard<decltype(mutex_)> lock(mutex_);
if (impl_->wd_register.available(wd) == false) return false;
if (wd->visible && wd->visible_parents())
if (wd->displayed())
{
if(forced || (false == wd->belong_to_lazy()))
{
@ -722,7 +722,7 @@ namespace detail
std::lock_guard<decltype(mutex_)> lock(mutex_);
//It's not worthy to redraw if visible is false
if (impl_->wd_register.available(wd) && wd->visible && wd->visible_parents())
if (impl_->wd_register.available(wd) && wd->displayed())
window_layer::paint(wd, true, true);
}
@ -1060,7 +1060,7 @@ namespace detail
bool precondition = false;
for (auto & tab_wd : tabs)
{
if (tab_wd->visible)
if (tab_wd->displayed())
{
precondition = true;
break;
@ -1073,7 +1073,7 @@ namespace detail
while (new_stop && (wd != new_stop))
{
if (new_stop->flags.enabled && new_stop->visible)
if (new_stop->flags.enabled && new_stop->displayed())
return new_stop;
new_stop = get_tabstop(new_stop, forward);

View File

@ -151,7 +151,7 @@ namespace nana{ namespace drawerbase
void trigger::key_char(graph_reference, const arg_keyboard& arg)
{
if(arg.key == static_cast<char_t>(keyboard::enter))
if (static_cast<char_t>(keyboard::enter) == arg.key || static_cast<char_t>(keyboard::space) == arg.key)
emit_click();
}

View File

@ -3512,6 +3512,7 @@ namespace nana
m.flags.checked = ck;
arg_listbox arg{*this, ck};
ess_->lister.wd_ptr()->events().checked.emit(arg);
ess_->update();
}
return *this;
}
@ -3539,6 +3540,7 @@ namespace nana
else if (ess_->lister.last_selected_abs == pos_)
ess_->lister.last_selected_abs.set_both(npos);
ess_->update();
return *this;
}

View File

@ -114,7 +114,7 @@ namespace nana
{
if(at.item_state == state::active)
{
graph.rectangle(r, false, {0xa8, 0xd8, 0xeb});
graph.rectangle(r, false, static_cast<color_rgb>(0xa8d8eb));
nana::point points[4] = {
nana::point(r.x, r.y),
nana::point(r.x + r.width - 1, r.y),
@ -200,35 +200,35 @@ namespace nana
void checked(std::size_t index, bool check)
{
if(root_.items.size() > index)
if (root_.items.size() <= index)
return;
item_type & m = root_.items[index];
if(check && (checks::option == m.style))
{
item_type & m = root_.items[index];
if(check && (checks::option == m.style))
if(index)
{
if(index)
std::size_t i = index;
do
{
std::size_t i = index;
do
{
item_type& el = root_.items[--i];
if(el.flags.splitter) break;
if(checks::option == el.style)
el.flags.checked = false;
}while(i);
}
for(std::size_t i = index + 1; i < root_.items.size(); ++i)
{
item_type & el = root_.items[i];
item_type& el = root_.items[--i];
if(el.flags.splitter) break;
if(checks::option == el.style)
el.flags.checked = false;
}
}while(i);
}
for(std::size_t i = index + 1; i < root_.items.size(); ++i)
{
item_type & el = root_.items[i];
if(el.flags.splitter) break;
if(checks::option == el.style)
el.flags.checked = false;
}
m.flags.checked = check;
}
m.flags.checked = check;
}
menu_type& data()
@ -304,7 +304,7 @@ namespace nana
: public drawer_trigger
{
public:
typedef menu_item_type::item_proxy item_proxy;
using item_proxy = menu_item_type::item_proxy;
renderer_interface * renderer;
@ -330,12 +330,12 @@ namespace nana
detail_.monitor_pos = API::cursor_position();
}
void mouse_move(graph_reference, const arg_mouse& arg)
void mouse_move(graph_reference graph, const arg_mouse& arg)
{
state_.nullify_mouse = false;
if(track_mouse(arg.pos.x, arg.pos.y))
if(track_mouse(arg.pos))
{
draw();
refresh(graph);
API::lazy_refresh();
}
}
@ -350,9 +350,70 @@ namespace nana
state_.nullify_mouse = false;
}
void refresh(graph_reference)
void refresh(graph_reference graph)
{
draw();
if (nullptr == menu_) return;
_m_adjust_window_size();
renderer->background(graph, *widget_);
const unsigned item_h_px = _m_item_height();
const unsigned image_px = item_h_px - 2;
nana::rectangle item_r(2, 2, graph_->width() - 4, item_h_px);
unsigned strpixels = item_r.width - 60;
int text_top_off = (item_h_px - graph.text_extent_size(STR("jh({[")).height) / 2;
std::size_t pos = 0;
for (auto & m : menu_->items)
{
if (m.flags.splitter)
{
graph_->set_color(colors::gray_border);
graph_->line({ item_r.x + 40, item_r.y }, { static_cast<int>(graph.width()) - 1, item_r.y });
item_r.y += 2;
++pos;
continue;
}
renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m);
//Draw item background
renderer->item(*graph_, item_r, attr);
//Draw text, the text is transformed from orignal for hotkey character
nana::char_t hotkey;
nana::string::size_type hotkey_pos;
nana::string text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos);
if (m.image.empty() == false)
renderer->item_image(graph, nana::point(item_r.x + 5, item_r.y + static_cast<int>(item_h_px - image_px) / 2 - 1), image_px, m.image);
renderer->item_text(graph, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr);
if (hotkey)
{
m.hotkey = hotkey;
if (m.flags.enabled)
{
unsigned off_w = (hotkey_pos ? graph.text_extent_size(text, static_cast<unsigned>(hotkey_pos)).width : 0);
nana::size hotkey_size = graph.text_extent_size(text.c_str() + hotkey_pos, 1);
int x = item_r.x + 40 + off_w;
int y = item_r.y + text_top_off + hotkey_size.height;
graph_->set_color(colors::black);
graph_->line({ x, y }, { x + static_cast<int>(hotkey_size.width) - 1, y });
}
}
if (m.sub_menu)
renderer->sub_arrow(graph, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr);
item_r.y += item_r.height + 1;
++pos;
}
}
std::size_t active() const
@ -411,21 +472,21 @@ namespace nana
state_.active = pos;
state_.sub_window = false;
draw();
refresh(*graph_);
return true;
}
return false;
}
bool track_mouse(int x, int y)
bool track_mouse(const ::nana::point& pos)
{
if(state_.nullify_mouse == false)
if (!state_.nullify_mouse)
{
std::size_t index = _m_get_index_by_pos(x, y);
if(index != state_.active)
std::size_t index = _m_get_index_by_pos(pos.x, pos.y);
if (index != state_.active)
{
if((index == npos) && menu_->items.at(state_.active).sub_menu && state_.sub_window)
if ((index == npos) && menu_->items.at(state_.active).sub_menu && state_.sub_window)
return false;
state_.active = (index != npos && menu_->items.at(index).flags.splitter) ? npos : index;
@ -433,6 +494,7 @@ namespace nana
return true;
}
}
return false;
}
@ -451,29 +513,35 @@ namespace nana
state_.sub_window = subw;
}
menu_type* retrive_sub_menu(nana::point& pos, std::size_t interval) const
menu_type* get_sub(nana::point& pos, unsigned long& tmstamp) const
{
if(state_.active != npos && (nana::system::timestamp() - state_.active_timestamp >= interval))
if (npos == state_.active)
return nullptr;
auto sub = menu_->items.at(state_.active).sub_menu;
if (sub)
{
pos.x = graph_->width() - 2;
pos.x = static_cast<int>(graph_->width()) - 2;
pos.y = 2;
std::size_t index = 0;
for(auto & m : menu_->items)
auto index = state_.active;
for (auto & m : menu_->items)
{
if(false == m.flags.splitter)
if (m.flags.splitter)
{
if(index == state_.active)
break;
pos.y += _m_item_height() + 1;
}
else
pos.y += 2;
continue;
}
++index;
if (0 == index)
break;
pos.y += _m_item_height() + 1;
--index;
}
return (menu_->items.at(state_.active).sub_menu);
tmstamp = state_.active_timestamp;
return sub;
}
return nullptr;
}
@ -498,8 +566,7 @@ namespace nana
state_.active = index;
state_.active_timestamp = nana::system::timestamp();
draw();
API::update_window(*widget_);
API::refresh_window(*widget_);
return 2;
}
else if(m.flags.enabled)
@ -514,72 +581,6 @@ namespace nana
}
return 0;
}
void draw() const
{
if(nullptr == menu_) return;
_m_adjust_window_size();
renderer->background(*graph_, *widget_);
const unsigned item_h_px = _m_item_height();
const unsigned image_px = item_h_px - 2;
nana::rectangle item_r(2, 2, graph_->width() - 4, item_h_px);
unsigned strpixels = item_r.width - 60;
int text_top_off = (item_h_px - graph_->text_extent_size(STR("jh({[")).height) / 2;
std::size_t pos = 0;
for(auto & m : menu_->items)
{
if(m.flags.splitter)
{
graph_->set_color(colors::gray_border);
graph_->line({ item_r.x + 40, item_r.y }, { static_cast<int>(graph_->width()) - 1, item_r.y });
item_r.y += 2;
++pos;
continue;
}
renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m);
//Draw item background
renderer->item(*graph_, item_r, attr);
//Draw text, the text is transformed from orignal for hotkey character
nana::char_t hotkey;
nana::string::size_type hotkey_pos;
nana::string text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos);
if(m.image.empty() == false)
renderer->item_image(*graph_, nana::point(item_r.x + 5, item_r.y + static_cast<int>(item_h_px - image_px) / 2 - 1), image_px, m.image);
renderer->item_text(*graph_, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr);
if(hotkey)
{
m.hotkey = hotkey;
if(m.flags.enabled)
{
unsigned off_w = (hotkey_pos ? graph_->text_extent_size(text, static_cast<unsigned>(hotkey_pos)).width : 0);
nana::size hotkey_size = graph_->text_extent_size(text.c_str() + hotkey_pos, 1);
int x = item_r.x + 40 + off_w;
int y = item_r.y + text_top_off + hotkey_size.height;
graph_->set_color(colors::black);
graph_->line({ x, y }, { x + static_cast<int>(hotkey_size.width) - 1, y });
}
}
if(m.sub_menu)
renderer->sub_arrow(*graph_, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr);
item_r.y += item_r.height + 1;
++pos;
}
}
private:
static renderer_interface::attr _m_make_renderer_attr(bool active, const menu_item_type & m)
{
@ -684,10 +685,10 @@ namespace nana
struct state
{
std::size_t active;
unsigned long active_timestamp;
unsigned long sub_window: 1;
unsigned long nullify_mouse: 1;
std::size_t active;
unsigned active_timestamp;
bool sub_window: 1;
bool nullify_mouse: 1;
}state_;
struct widget_detail
@ -722,7 +723,19 @@ namespace nana
submenu_.child = submenu_.parent = nullptr;
submenu_.object = nullptr;
_m_make_mouse_event();
state_.mouse_pos = API::cursor_position();
events().mouse_move.connect_unignorable([this]{
nana::point pos = API::cursor_position();
if (pos != state_.mouse_pos)
{
menu_window * root = this;
while (root->submenu_.parent)
root = root->submenu_.parent;
root->state_.auto_popup_submenu = true;
state_.mouse_pos = pos;
}
});
}
void popup(menu_type& menu, bool owner_menubar)
@ -749,13 +762,19 @@ namespace nana
_m_key_down(arg);
});
events().mouse_up.connect_unignorable([this]{
pick();
events().mouse_down.connect_unignorable([this](const arg_mouse& arg)
{
this->_m_open_sub(0); //Try to open submenu immediately
});
events().mouse_up.connect_unignorable([this](const arg_mouse& arg){
if (arg.left_button)
pick();
});
timer_.interval(100);
timer_.elapse([this]{
this->_m_check_repeatly();
this->_m_open_sub(500); //Try to open submenu
});
timer_.start();
@ -801,29 +820,27 @@ namespace nana
bool submenu(bool enter)
{
menu_window * object = this;
while (object->submenu_.child)
object = object->submenu_.child;
menu_window * menu_wd = this;
while (menu_wd->submenu_.child)
menu_wd = menu_wd->submenu_.child;
state_.auto_popup_submenu = false;
if (enter)
if (!enter)
{
if (object->submenu_.parent)
if (menu_wd->submenu_.parent)
{
auto & sub = object->submenu_.parent->submenu_;
auto & sub = menu_wd->submenu_.parent->submenu_;
sub.child = nullptr;
sub.object = nullptr;
object->close();
menu_wd->close();
return true;
}
return false;
}
nana::point pos;
menu_type * sbm = object->get_drawer_trigger().retrive_sub_menu(pos, 0);
return object->_m_show_submenu(sbm, pos, true);
return menu_wd->_m_manipulate_sub(0, true);
}
int send_shortkey(nana::char_t key)
@ -969,63 +986,51 @@ namespace nana
}
}
void _m_make_mouse_event()
bool _m_manipulate_sub(unsigned long delay_ms, bool forced)
{
state_.mouse_pos = API::cursor_position();
events().mouse_move.connect_unignorable([this]{
_m_mouse_event();
});
}
auto & drawer = get_drawer_trigger();
::nana::point pos;
unsigned long tmstamp;
void _m_mouse_event()
{
nana::point pos = API::cursor_position();
if(pos != state_.mouse_pos)
auto menu_ptr = drawer.get_sub(pos, tmstamp);
if (menu_ptr == submenu_.object)
return false;
if (menu_ptr && (::nana::system::timestamp() - tmstamp < delay_ms))
return false;
if (submenu_.object && (menu_ptr != submenu_.object))
{
menu_window * root = this;
while(root->submenu_.parent)
root = root->submenu_.parent;
root->state_.auto_popup_submenu = true;
state_.mouse_pos = pos;
}
}
bool _m_show_submenu(menu_type* sbm, nana::point pos, bool forced)
{
auto & mdtrigger = get_drawer_trigger();
if(submenu_.object && (sbm != submenu_.object))
{
mdtrigger.set_sub_window(false);
drawer.set_sub_window(false);
submenu_.child->close();
submenu_.child = nullptr;
submenu_.object = nullptr;
}
if(sbm)
if (menu_ptr)
{
menu_window * root = this;
while(root->submenu_.parent)
while (root->submenu_.parent)
root = root->submenu_.parent;
if((submenu_.object == nullptr) && sbm && (forced || root->state_.auto_popup_submenu))
if ((submenu_.object == nullptr) && menu_ptr && (forced || root->state_.auto_popup_submenu))
{
sbm->item_pixels = mdtrigger.data()->item_pixels;
sbm->gaps = mdtrigger.data()->gaps;
pos.x += sbm->gaps.x;
pos.y += sbm->gaps.y;
menu_ptr->item_pixels = drawer.data()->item_pixels;
menu_ptr->gaps = drawer.data()->gaps;
pos += menu_ptr->gaps;
menu_window & mwnd = form_loader<menu_window, false>()(handle(), true, pos, mdtrigger.renderer);
menu_window & mwnd = form_loader<menu_window, false>()(handle(), true, pos, drawer.renderer);
mwnd.state_.self_submenu = true;
submenu_.child = & mwnd;
submenu_.child = &mwnd;
submenu_.child->submenu_.parent = this;
submenu_.object = sbm;
submenu_.object = menu_ptr;
API::set_window_z_order(handle(), mwnd.handle(), z_order_action::none);
mwnd.popup(*sbm, state_.owner_menubar);
mdtrigger.set_sub_window(true);
if(forced)
mwnd.popup(*menu_ptr, state_.owner_menubar);
drawer.set_sub_window(true);
if (forced)
mwnd.goto_next(true);
return true;
@ -1034,17 +1039,16 @@ namespace nana
return false;
}
void _m_check_repeatly()
void _m_open_sub(unsigned delay_ms) //check_repeatly
{
if(state_.auto_popup_submenu)
{
nana::point pos = API::cursor_position();
auto pos = API::cursor_position();
drawer_type& drawer = get_drawer_trigger();
API::calc_window_point(handle(), pos);
drawer.track_mouse(pos.x, pos.y);
menu_type* sbm = drawer.retrive_sub_menu(pos, 500);
_m_show_submenu(sbm, pos, false);
get_drawer_trigger().track_mouse(pos);
_m_manipulate_sub(delay_ms, false);
}
}
private: