Merge branch 'hotfix-1.4.1' into develop
This commit is contained in:
@@ -58,6 +58,15 @@ namespace detail
|
||||
proc_.filter_proc = 0;
|
||||
}
|
||||
|
||||
~msg_dispatcher()
|
||||
{
|
||||
if(thrd_ && thrd_->joinable())
|
||||
{
|
||||
is_work_ = false;
|
||||
thrd_->join();
|
||||
}
|
||||
}
|
||||
|
||||
void set(timer_proc_type timer_proc, event_proc_type event_proc, event_filter_type filter)
|
||||
{
|
||||
proc_.timer_proc = timer_proc;
|
||||
|
||||
@@ -323,7 +323,7 @@ namespace nana
|
||||
{
|
||||
for (auto wd = this; wd; wd = wd->parent)
|
||||
{
|
||||
if (basic_window::update_state::refresh == wd->other.upd_state)
|
||||
if (basic_window::update_state::refreshed == wd->other.upd_state)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -567,9 +567,12 @@ namespace detail
|
||||
|
||||
if(xevent.xbutton.button == Button4 || xevent.xbutton.button == Button5)
|
||||
break;
|
||||
|
||||
|
||||
msgwnd = wd_manager.find_window(native_window, xevent.xbutton.x, xevent.xbutton.y);
|
||||
if(nullptr == msgwnd) break;
|
||||
|
||||
pressed_wd = nullptr;
|
||||
if(nullptr == msgwnd)
|
||||
break;
|
||||
|
||||
if ((msgwnd == msgwnd->root_widget->other.attribute.root->menubar) && brock.get_menu(msgwnd->root, true))
|
||||
brock.erase_menu(true);
|
||||
@@ -578,7 +581,9 @@ namespace detail
|
||||
|
||||
if(msgwnd->flags.enabled)
|
||||
{
|
||||
bool dbl_click = (last_mouse_down_window == msgwnd) && (xevent.xbutton.time - last_mouse_down_time <= 400);
|
||||
pressed_wd = msgwnd;
|
||||
|
||||
const bool dbl_click = (last_mouse_down_window == msgwnd) && (xevent.xbutton.time - last_mouse_down_time <= 400);
|
||||
last_mouse_down_time = xevent.xbutton.time;
|
||||
last_mouse_down_window = msgwnd;
|
||||
|
||||
@@ -597,28 +602,24 @@ namespace detail
|
||||
auto retain = msgwnd->annex.events_ptr;
|
||||
context.event_window = msgwnd;
|
||||
|
||||
pressed_wd = nullptr;
|
||||
|
||||
msgwnd->set_action(mouse_action::pressed);
|
||||
arg_mouse arg;
|
||||
assign_arg(arg, msgwnd, ButtonPress, xevent);
|
||||
arg.evt_code = dbl_click ? event_code::dbl_click : event_code::mouse_down;
|
||||
if(brock.emit(arg.evt_code, msgwnd, arg, true, &context))
|
||||
if (brock.emit(arg.evt_code, msgwnd, arg, true, &context))
|
||||
{
|
||||
if (wd_manager.available(msgwnd))
|
||||
//If a root window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event.
|
||||
if (msgwnd->root != native_interface::get_focus_window())
|
||||
{
|
||||
pressed_wd = msgwnd;
|
||||
//If a root window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event.
|
||||
if (msgwnd->root != native_interface::get_focus_window())
|
||||
{
|
||||
//call the drawer mouse up event for restoring the surface graphics
|
||||
msgwnd->set_action(mouse_action::normal);
|
||||
//call the drawer mouse up event for restoring the surface graphics
|
||||
msgwnd->set_action(mouse_action::normal);
|
||||
|
||||
draw_invoker(&drawer::mouse_up, msgwnd, arg, &context);
|
||||
wd_manager.do_lazy_refresh(msgwnd, false);
|
||||
}
|
||||
draw_invoker(&drawer::mouse_up, msgwnd, arg, &context);
|
||||
wd_manager.do_lazy_refresh(msgwnd, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
pressed_wd = nullptr;
|
||||
}
|
||||
break;
|
||||
case ButtonRelease:
|
||||
@@ -1157,7 +1158,9 @@ namespace detail
|
||||
}
|
||||
|
||||
++(context->event_pump_ref_count);
|
||||
wd_manager().internal_lock().revert();
|
||||
|
||||
auto & lock = wd_manager().internal_lock();
|
||||
lock.revert();
|
||||
|
||||
native_window_type owner_native = 0;
|
||||
core_window_t * owner = 0;
|
||||
@@ -1182,8 +1185,9 @@ namespace detail
|
||||
owner->flags.enabled = true;
|
||||
native_interface::enable_window(owner_native, true);
|
||||
}
|
||||
|
||||
wd_manager().internal_lock().forward();
|
||||
|
||||
lock.forward();
|
||||
|
||||
if(0 == --(context->event_pump_ref_count))
|
||||
{
|
||||
if(0 == modal_window || 0 == context->window_count)
|
||||
@@ -1203,17 +1207,7 @@ namespace detail
|
||||
{
|
||||
thread_context* thrd = get_thread_context(0);
|
||||
if(thrd && thrd->event_window)
|
||||
{
|
||||
//the state none should be tested, becuase in an event, there would be draw after an update,
|
||||
//if the none is not tested, the draw after update will not be refreshed.
|
||||
switch(thrd->event_window->other.upd_state)
|
||||
{
|
||||
case core_window_t::update_state::none:
|
||||
case core_window_t::update_state::lazy:
|
||||
thrd->event_window->other.upd_state = core_window_t::update_state::refresh;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
thrd->event_window->other.upd_state = core_window_t::update_state::refreshed;
|
||||
}
|
||||
|
||||
//Dynamically set a cursor for a window
|
||||
|
||||
@@ -919,7 +919,13 @@ namespace detail
|
||||
break;
|
||||
|
||||
msgwnd = wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y);
|
||||
if ((nullptr == msgwnd) || (pressed_wd && (msgwnd != pressed_wd)))
|
||||
|
||||
//Don't take care about whether msgwnd is equal to the pressed_wd.
|
||||
//
|
||||
//pressed_wd will remains when opens a no-actived window in an mouse_down event(like combox popups the drop-list).
|
||||
//After the no-actived window is closed, the window doesn't respond to the mouse click other than pressed_wd.
|
||||
pressed_wd = nullptr;
|
||||
if (nullptr == msgwnd)
|
||||
break;
|
||||
|
||||
//if event on the menubar, just remove the menu if it is not associating with the menubar
|
||||
@@ -1691,17 +1697,7 @@ namespace detail
|
||||
{
|
||||
auto* thrd = get_thread_context(0);
|
||||
if (thrd && thrd->event_window)
|
||||
{
|
||||
//the state none should be tested, becuase in an event, there would be draw after an update,
|
||||
//if the none is not tested, the draw after update will not be refreshed.
|
||||
switch (thrd->event_window->other.upd_state)
|
||||
{
|
||||
case core_window_t::update_state::none:
|
||||
case core_window_t::update_state::lazy:
|
||||
thrd->event_window->other.upd_state = core_window_t::update_state::refresh;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
thrd->event_window->other.upd_state = core_window_t::update_state::refreshed;
|
||||
}
|
||||
|
||||
//Dynamically set a cursor for a window
|
||||
|
||||
@@ -391,7 +391,7 @@ namespace nana
|
||||
|
||||
bool drawer::_m_lazy_decleared() const
|
||||
{
|
||||
return (basic_window::update_state::refresh == data_impl_->window_handle->other.upd_state);
|
||||
return (basic_window::update_state::refreshed == data_impl_->window_handle->other.upd_state);
|
||||
}
|
||||
|
||||
drawer::method_state& drawer::_m_mth_state(int pos)
|
||||
|
||||
@@ -22,26 +22,26 @@ namespace nana
|
||||
namespace detail
|
||||
{
|
||||
//class window_layout
|
||||
void window_layout::paint(core_window_t* wd, bool is_redraw, bool is_child_refreshed)
|
||||
void window_layout::paint(core_window_t* wd, paint_operation operation, bool req_refresh_children)
|
||||
{
|
||||
if (is_redraw && wd->flags.refreshing)
|
||||
if (wd->flags.refreshing && (paint_operation::try_refresh == operation))
|
||||
return;
|
||||
|
||||
if (nullptr == wd->effect.bground)
|
||||
{
|
||||
if (is_redraw && (!wd->drawer.graphics.empty()))
|
||||
if ((paint_operation::try_refresh == operation) && (!wd->drawer.graphics.empty()))
|
||||
{
|
||||
wd->flags.refreshing = true;
|
||||
wd->drawer.refresh();
|
||||
wd->flags.refreshing = false;
|
||||
}
|
||||
maproot(wd, is_redraw, is_child_refreshed);
|
||||
maproot(wd, (paint_operation::none != operation), req_refresh_children);
|
||||
}
|
||||
else
|
||||
_m_paint_glass_window(wd, is_redraw, is_child_refreshed, false, true);
|
||||
_m_paint_glass_window(wd, (paint_operation::try_refresh == operation), req_refresh_children, false, true);
|
||||
}
|
||||
|
||||
bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool is_child_refreshed)
|
||||
bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool req_refresh_children)
|
||||
{
|
||||
auto check_opaque = wd->seek_non_lite_widget_ancestor();
|
||||
if (check_opaque && check_opaque->flags.refreshing)
|
||||
@@ -56,7 +56,7 @@ namespace nana
|
||||
if (category::flags::lite_widget != wd->other.category)
|
||||
graph.bitblt(vr, wd->drawer.graphics, nana::point(vr.x - wd->pos_root.x, vr.y - wd->pos_root.y));
|
||||
|
||||
_m_paste_children(wd, is_child_refreshed, have_refreshed, vr, graph, nana::point());
|
||||
_m_paste_children(wd, have_refreshed, req_refresh_children, vr, graph, nana::point());
|
||||
|
||||
if (wd->parent)
|
||||
{
|
||||
@@ -81,11 +81,11 @@ namespace nana
|
||||
graph.bitblt(el.r, (el.window->drawer.graphics), p_src);
|
||||
}
|
||||
|
||||
_m_paste_children(el.window, is_child_refreshed, false, el.r, graph, nana::point{});
|
||||
_m_paste_children(el.window, false, req_refresh_children, el.r, graph, nana::point{});
|
||||
}
|
||||
}
|
||||
}
|
||||
_m_notify_glasses(wd, vr);
|
||||
_m_notify_glasses(wd);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -255,8 +255,8 @@ namespace nana
|
||||
if (category::flags::lite_widget != child->other.category)
|
||||
glass_buffer.bitblt(nana::rectangle{ ovlp.x - wd->pos_owner.x, ovlp.y - wd->pos_owner.y, ovlp.width, ovlp.height }, child->drawer.graphics, {ovlp.position() - child->pos_owner});
|
||||
|
||||
ovlp.x += wd->pos_root.x;
|
||||
ovlp.y += wd->pos_root.y;
|
||||
ovlp.x += wd->parent->pos_root.x;
|
||||
ovlp.y += wd->parent->pos_root.y;
|
||||
_m_paste_children(child, false, false, ovlp, glass_buffer, rpos);
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ namespace nana
|
||||
|
||||
//_m_paste_children
|
||||
//@brief:paste children window to the root graphics directly. just paste the visual rectangle
|
||||
void window_layout::_m_paste_children(core_window_t* wd, bool is_child_refreshed, bool have_refreshed, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos)
|
||||
void window_layout::_m_paste_children(core_window_t* wd, bool have_refreshed, bool req_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos)
|
||||
{
|
||||
nana::rectangle rect;
|
||||
for (auto child : wd->children)
|
||||
@@ -278,7 +278,7 @@ namespace nana
|
||||
|
||||
if (category::flags::root == child->other.category)
|
||||
{
|
||||
paint(child, is_child_refreshed, is_child_refreshed);
|
||||
paint(child, (req_refresh_children ? paint_operation::try_refresh : paint_operation::none), req_refresh_children);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace nana
|
||||
bool have_child_refreshed = false;
|
||||
if (category::flags::lite_widget != child->other.category)
|
||||
{
|
||||
if (is_child_refreshed && (false == child->flags.refreshing))
|
||||
if (req_refresh_children && (false == child->flags.refreshing))
|
||||
{
|
||||
have_child_refreshed = true;
|
||||
child->flags.refreshing = true;
|
||||
@@ -300,13 +300,13 @@ namespace nana
|
||||
graph.bitblt(nana::rectangle(rect.x - graph_rpos.x, rect.y - graph_rpos.y, rect.width, rect.height),
|
||||
child->drawer.graphics, nana::point(rect.x - child->pos_root.x, rect.y - child->pos_root.y));
|
||||
}
|
||||
_m_paste_children(child, is_child_refreshed, have_child_refreshed, rect, graph, graph_rpos);
|
||||
_m_paste_children(child, req_refresh_children, have_child_refreshed, rect, graph, graph_rpos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//If have_refreshed, the glass should be notified.
|
||||
_m_paint_glass_window(child, false, is_child_refreshed, have_refreshed, false);
|
||||
//Update the glass window's background if the parent have_refreshed.
|
||||
_m_paint_glass_window(child, have_refreshed, req_refresh_children, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,16 +353,14 @@ namespace nana
|
||||
}
|
||||
|
||||
if (notify_other)
|
||||
_m_notify_glasses(wd, vr);
|
||||
_m_notify_glasses(wd);
|
||||
}
|
||||
}
|
||||
|
||||
//_m_notify_glasses
|
||||
//@brief: Notify the glass windows that are overlapped with the specified vis_rect
|
||||
void window_layout::_m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& /*r_visual*/)
|
||||
/// Notify the glass windows that are overlapped with the specified visual rectangle.
|
||||
/// If a child window of sigwd is a glass window, it doesn't to be notified.
|
||||
void window_layout::_m_notify_glasses(core_window_t* const sigwd)
|
||||
{
|
||||
typedef category::flags cat_flags;
|
||||
|
||||
nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension);
|
||||
for (auto wd : data_sect.effects_bground_windows)
|
||||
{
|
||||
@@ -377,6 +375,8 @@ namespace nana
|
||||
}
|
||||
else if (sigwd != wd->parent)
|
||||
{
|
||||
using cat_flags = category::flags;
|
||||
|
||||
if (wd->parent && (cat_flags::lite_widget == wd->parent->other.category))
|
||||
{
|
||||
//Test if sigwd is an ancestor of the glass window, and there are lite widgets
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace detail
|
||||
|
||||
void window_manager::revertible_mutex::revert()
|
||||
{
|
||||
if(impl_->thread.ref && (impl_->thread.tid == nana::system::this_thread_id()))
|
||||
if(impl_->thread.tid == nana::system::this_thread_id())
|
||||
{
|
||||
std::size_t cnt = impl_->thread.ref;
|
||||
|
||||
@@ -346,24 +346,30 @@ namespace detail
|
||||
for (std::size_t i = 0; i < cnt; ++i)
|
||||
impl_->mutex.unlock();
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("The revert is not allowed");
|
||||
}
|
||||
|
||||
void window_manager::revertible_mutex::forward()
|
||||
{
|
||||
impl_->mutex.lock();
|
||||
|
||||
if(impl_->invoke_stack.size())
|
||||
{
|
||||
auto thr = impl_->invoke_stack.back();
|
||||
|
||||
impl_->invoke_stack.pop_back();
|
||||
|
||||
if(thr.tid == nana::system::this_thread_id())
|
||||
{
|
||||
impl_->invoke_stack.pop_back();
|
||||
for (std::size_t i = 0; i < thr.ref; ++i)
|
||||
impl_->mutex.lock();
|
||||
impl_->thread = thr;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Nana.GUI: The forward is not matched.");
|
||||
throw std::runtime_error("The forward is not matched. Please report this issue");
|
||||
}
|
||||
|
||||
impl_->mutex.unlock();
|
||||
}
|
||||
//end class revertible_mutex
|
||||
@@ -759,6 +765,10 @@ namespace detail
|
||||
arg.window_handle = reinterpret_cast<window>(wd);
|
||||
arg.x = x;
|
||||
arg.y = y;
|
||||
|
||||
if (wd->effect.bground)
|
||||
wd->other.upd_state = basic_window::update_state::request_refresh;
|
||||
|
||||
brock.emit(event_code::move, wd, arg, true, brock.get_thread_context());
|
||||
return true;
|
||||
}
|
||||
@@ -803,6 +813,9 @@ namespace detail
|
||||
_m_move_core(wd, delta);
|
||||
moved = true;
|
||||
|
||||
if ((!size_changed) && wd->effect.bground)
|
||||
wd->other.upd_state = basic_window::update_state::request_refresh;
|
||||
|
||||
arg_move arg;
|
||||
arg.window_handle = reinterpret_cast<window>(wd);
|
||||
arg.x = r.x;
|
||||
@@ -989,26 +1002,28 @@ namespace detail
|
||||
|
||||
if (wd->displayed())
|
||||
{
|
||||
using paint_operation = window_layer::paint_operation;
|
||||
|
||||
if(forced || (false == wd->belong_to_lazy()))
|
||||
{
|
||||
if (!wd->flags.refreshing)
|
||||
{
|
||||
window_layer::paint(wd, redraw, false);
|
||||
window_layer::paint(wd, (redraw ? paint_operation::try_refresh : paint_operation::none), false);
|
||||
this->map(wd, forced, update_area);
|
||||
return true;
|
||||
}
|
||||
else if (forced)
|
||||
{
|
||||
window_layer::paint(wd, false, false);
|
||||
window_layer::paint(wd, paint_operation::none, false);
|
||||
this->map(wd, true, update_area);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (redraw)
|
||||
window_layer::paint(wd, true, false);
|
||||
window_layer::paint(wd, paint_operation::try_refresh, false);
|
||||
|
||||
if (wd->other.upd_state == core_window_t::update_state::lazy)
|
||||
wd->other.upd_state = core_window_t::update_state::refresh;
|
||||
wd->other.upd_state = core_window_t::update_state::refreshed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1020,43 +1035,43 @@ namespace detail
|
||||
|
||||
//It's not worthy to redraw if visible is false
|
||||
if (impl_->wd_register.available(wd) && wd->displayed())
|
||||
window_layer::paint(wd, true, true);
|
||||
window_layer::paint(wd, window_layer::paint_operation::try_refresh, true);
|
||||
}
|
||||
|
||||
//do_lazy_refresh
|
||||
//@brief: defined a behavior of flush the screen
|
||||
//@return: it returns true if the wnd is available
|
||||
bool window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen, bool refresh_tree)
|
||||
void window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen, bool refresh_tree)
|
||||
{
|
||||
//Thread-Safe Required!
|
||||
std::lock_guard<mutex_type> lock(mutex_);
|
||||
|
||||
if (false == impl_->wd_register.available(wd))
|
||||
return false;
|
||||
return;
|
||||
|
||||
//It's not worthy to redraw if visible is false
|
||||
if(wd->visible && (!wd->is_draw_through()))
|
||||
{
|
||||
using paint_operation = window_layer::paint_operation;
|
||||
if (wd->visible_parents())
|
||||
{
|
||||
if ((wd->other.upd_state == core_window_t::update_state::refresh) || force_copy_to_screen)
|
||||
if ((wd->other.upd_state == core_window_t::update_state::refreshed) || (wd->other.upd_state == core_window_t::update_state::request_refresh) || force_copy_to_screen)
|
||||
{
|
||||
window_layer::paint(wd, false, refresh_tree);
|
||||
window_layer::paint(wd, (wd->other.upd_state == core_window_t::update_state::request_refresh ? paint_operation::try_refresh : paint_operation::have_refreshed), refresh_tree);
|
||||
this->map(wd, force_copy_to_screen);
|
||||
}
|
||||
else if (effects::edge_nimbus::none != wd->effect.edge_nimbus)
|
||||
{
|
||||
//The window is still mapped because of edge nimbus effect.
|
||||
//Avoid duplicate copy if action state is not changed and the window is not focused.
|
||||
if ((wd->flags.action != wd->flags.action_before) || (bedrock::instance().focus() == wd))
|
||||
if (wd->flags.action != wd->flags.action_before)
|
||||
this->map(wd, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
window_layer::paint(wd, true, refresh_tree); //only refreshing if it has an invisible parent
|
||||
window_layer::paint(wd, paint_operation::try_refresh, refresh_tree); //only refreshing if it has an invisible parent
|
||||
}
|
||||
wd->other.upd_state = core_window_t::update_state::none;
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
//get_graphics
|
||||
|
||||
@@ -992,33 +992,30 @@ namespace API
|
||||
|
||||
void modal_window(window wd)
|
||||
{
|
||||
auto const iwd = reinterpret_cast<basic_window*>(wd);
|
||||
internal_scope_guard isg;
|
||||
|
||||
if (!restrict::wd_manager().available(iwd))
|
||||
return;
|
||||
|
||||
if ((iwd->other.category == category::flags::root) && (iwd->flags.modal == false))
|
||||
{
|
||||
auto const iwd = reinterpret_cast<basic_window*>(wd);
|
||||
internal_scope_guard isg;
|
||||
|
||||
if (!restrict::wd_manager().available(iwd))
|
||||
return;
|
||||
|
||||
if ((iwd->other.category == category::flags::root) && (iwd->flags.modal == false))
|
||||
{
|
||||
iwd->flags.modal = true;
|
||||
iwd->flags.modal = true;
|
||||
#if defined(NANA_X11)
|
||||
interface_type::set_modal(iwd->root);
|
||||
interface_type::set_modal(iwd->root);
|
||||
#endif
|
||||
restrict::wd_manager().show(iwd, true);
|
||||
}
|
||||
else
|
||||
return;
|
||||
restrict::wd_manager().show(iwd, true);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
//modal has to guarantee that does not lock the mutex of window_manager before invokeing the pump_event,
|
||||
//otherwise, the modal will prevent the other thread access the window.
|
||||
restrict::bedrock.pump_event(wd, true);
|
||||
}
|
||||
|
||||
void wait_for(window wd)
|
||||
{
|
||||
if (wd)
|
||||
internal_scope_guard lock;
|
||||
if (restrict::wd_manager().available(reinterpret_cast<basic_window*>(wd)))
|
||||
restrict::bedrock.pump_event(wd, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* A group widget implementation
|
||||
* Nana C++ Library(http://www.nanaro.org)
|
||||
* Copyright(C) 2015 Jinhao(cnjinhao@hotmail.com)
|
||||
* Copyright(C) 2015-2016 Jinhao(cnjinhao@hotmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
@@ -36,6 +36,8 @@ namespace nana{
|
||||
unsigned gap{2};
|
||||
std::string usr_div_str;
|
||||
|
||||
nana::size caption_dimension;
|
||||
|
||||
std::vector<std::unique_ptr<checkbox>> options;
|
||||
radio_group * radio_logic{nullptr};
|
||||
|
||||
@@ -60,19 +62,18 @@ namespace nana{
|
||||
|
||||
void update_div()
|
||||
{
|
||||
::nana::size sz = caption.measure(1000);
|
||||
caption_dimension = caption.measure(1000);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "vert margin=[0," << gap << "," << gap + 5 << "," << gap << "]"
|
||||
<< " <weight=" << sz.height << " <weight=5> <" << field_title << " weight=" << sz.width + 1 << "> >"
|
||||
<< "<<vert margin=5 " << field_options << ">";
|
||||
std::string div = "vert margin=[0," + std::to_string(gap) + "," + std::to_string(gap + 5) + "," + std::to_string(gap) + "]";
|
||||
div += "<weight=" + std::to_string(caption_dimension.height) + " <weight=5><" + field_title + " weight=" + std::to_string(caption_dimension.width + 1) + ">>";
|
||||
div += "<<vert margin=5 " + std::string(field_options) + ">";
|
||||
|
||||
if (!usr_div_str.empty())
|
||||
ss << "<" << usr_div_str << ">>";
|
||||
div += "<" + usr_div_str + ">>";
|
||||
else
|
||||
ss << ">";
|
||||
div += ">";
|
||||
|
||||
place_content.div(ss.str().c_str());
|
||||
place_content.div(div.c_str());
|
||||
|
||||
if (options.empty())
|
||||
place_content.field_display(field_options, false);
|
||||
@@ -202,25 +203,23 @@ namespace nana{
|
||||
outter.collocate();
|
||||
|
||||
color pbg = API::bgcolor(this->parent());
|
||||
impl_->caption.bgcolor(pbg.blend(colors::black, 0.975));
|
||||
color bg = pbg.blend(colors::black, 0.950);
|
||||
|
||||
bgcolor(bg);
|
||||
impl_->caption.bgcolor(pbg.blend(colors::black, 0.975));
|
||||
|
||||
this->bgcolor(pbg.blend(colors::black, 0.950));
|
||||
|
||||
drawing dw(*this);
|
||||
|
||||
::nana::size sz = impl_->caption.measure(1000);
|
||||
|
||||
// This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !!
|
||||
dw.draw([this, sz, bg, pbg](paint::graphics& graph)
|
||||
dw.draw([this](paint::graphics& graph)
|
||||
{
|
||||
auto gap_px = impl_->gap - 1;
|
||||
|
||||
graph.rectangle(true, pbg);
|
||||
graph.round_rectangle(rectangle(point(gap_px, sz.height / 2),
|
||||
nana::size(graph.width() - 2 * gap_px, graph.height() - sz.height / 2 - gap_px)
|
||||
graph.rectangle(true, API::bgcolor(this->parent()));
|
||||
graph.round_rectangle(rectangle(point(gap_px, impl_->caption_dimension.height / 2),
|
||||
nana::size(graph.width() - 2 * gap_px, graph.height() - impl_->caption_dimension.height / 2 - gap_px)
|
||||
),
|
||||
3, 3, colors::gray_border, true, bg);
|
||||
3, 3, colors::gray_border, true, this->bgcolor());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
*/
|
||||
|
||||
#include <nana/gui/widgets/menu.hpp>
|
||||
#include <nana/gui/timer.hpp>
|
||||
|
||||
#include <nana/system/platform.hpp>
|
||||
#include <nana/gui/element.hpp>
|
||||
#include <nana/gui/wvl.hpp>
|
||||
#include <nana/paint/text_renderer.hpp>
|
||||
#include <cctype> //introduces tolower
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace nana
|
||||
{
|
||||
@@ -27,6 +30,20 @@ namespace nana
|
||||
{
|
||||
namespace menu
|
||||
{
|
||||
|
||||
struct menu_type
|
||||
{
|
||||
using item_type = menu_item_type;
|
||||
using item_container = std::vector<std::unique_ptr<item_type>>;
|
||||
using iterator = item_container::iterator;
|
||||
|
||||
std::vector<menu_type*> owner;
|
||||
item_container items;
|
||||
unsigned max_pixels;
|
||||
unsigned item_pixels;
|
||||
nana::point gaps;
|
||||
};
|
||||
|
||||
//A helper function to check the style parameter
|
||||
inline bool good_checks(checks s)
|
||||
{
|
||||
@@ -83,8 +100,8 @@ namespace nana
|
||||
flags.checked = false;
|
||||
}
|
||||
|
||||
menu_item_type::menu_item_type(std::string text, const event_fn_t& f)
|
||||
: text(std::move(text)), functor(f)
|
||||
menu_item_type::menu_item_type(std::string text, const event_fn_t& fn)
|
||||
: text(std::move(text)), event_handler(fn)
|
||||
{
|
||||
flags.enabled = true;
|
||||
flags.splitter = false;
|
||||
@@ -153,10 +170,10 @@ namespace nana
|
||||
|
||||
//Stretchs menu icon only when it doesn't fit, center it otherwise.
|
||||
//Contributed by kmribti(pr#102)
|
||||
nana::point ipos = pos;
|
||||
ipos.x += (image_px - img.size().width ) / 2;
|
||||
ipos.y += (image_px - img.size().height) / 2;
|
||||
img.paste(graph, ipos);
|
||||
img.paste(graph, {
|
||||
pos.x + static_cast<int>(image_px - img.size().width) / 2,
|
||||
pos.y + static_cast<int>(image_px - img.size().height) / 2
|
||||
});
|
||||
}
|
||||
|
||||
void item_text(graph_reference graph, const nana::point& pos, const std::string& text, unsigned text_pixels, const attr& at)
|
||||
@@ -183,11 +200,9 @@ namespace nana
|
||||
: noncopyable
|
||||
{
|
||||
public:
|
||||
typedef menu_item_type item_type;
|
||||
|
||||
typedef menu_type::item_container::value_type::event_fn_t event_fn_t;
|
||||
typedef menu_type::item_container::iterator iterator;
|
||||
typedef menu_type::item_container::const_iterator const_iterator;
|
||||
using item_type = menu_item_type;
|
||||
using event_fn_t = item_type::event_fn_t;
|
||||
using iterator = menu_type::item_container::iterator;
|
||||
|
||||
menu_builder()
|
||||
{
|
||||
@@ -206,40 +221,42 @@ namespace nana
|
||||
if(good_checks(s))
|
||||
{
|
||||
if(root_.items.size() > index)
|
||||
root_.items[index].style = s;
|
||||
root_.items[index]->style = s;
|
||||
}
|
||||
}
|
||||
|
||||
void checked(std::size_t index, bool check)
|
||||
void checked(std::size_t pos, bool check)
|
||||
{
|
||||
if (root_.items.size() <= index)
|
||||
if (root_.items.size() <= pos)
|
||||
return;
|
||||
|
||||
item_type & m = root_.items[index];
|
||||
item_type & m = *(root_.items[pos]);
|
||||
|
||||
if(check && (checks::option == m.style))
|
||||
{
|
||||
if(index)
|
||||
//find a splitter in front of pos
|
||||
if (pos > 0)
|
||||
{
|
||||
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);
|
||||
if (root_.items[--pos]->flags.splitter)
|
||||
{
|
||||
++pos;
|
||||
break;
|
||||
}
|
||||
}while(pos > 0);
|
||||
}
|
||||
|
||||
for(std::size_t i = index + 1; i < root_.items.size(); ++i)
|
||||
while (pos < root_.items.size())
|
||||
{
|
||||
item_type & el = root_.items[i];
|
||||
item_type & el = *(root_.items[pos++]);
|
||||
if(el.flags.splitter) break;
|
||||
|
||||
if(checks::option == el.style)
|
||||
el.flags.checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
m.flags.checked = check;
|
||||
}
|
||||
|
||||
@@ -248,25 +265,12 @@ namespace nana
|
||||
return root_;
|
||||
}
|
||||
|
||||
const menu_type& data() const
|
||||
{
|
||||
return root_;
|
||||
}
|
||||
|
||||
void insert(std::size_t pos, std::string&& text, const event_fn_t& fn)
|
||||
{
|
||||
if(pos < root_.items.size())
|
||||
root_.items.emplace(root_.items.begin() + pos, std::move(text), std::ref(fn));
|
||||
else
|
||||
root_.items.emplace_back(std::move(text), std::ref(fn));
|
||||
}
|
||||
|
||||
bool set_sub_menu(std::size_t pos, menu_type &sub)
|
||||
{
|
||||
if(root_.items.size() > pos)
|
||||
{
|
||||
menu_item_type & item = root_.items[pos];
|
||||
if(item.sub_menu == nullptr)
|
||||
auto & item = *(root_.items[pos]);
|
||||
if(!item.sub_menu)
|
||||
{
|
||||
item.sub_menu = ⊂
|
||||
sub.owner.emplace_back(&root_);
|
||||
@@ -281,17 +285,17 @@ namespace nana
|
||||
for(auto i : root_.owner)
|
||||
for(auto & m : i->items)
|
||||
{
|
||||
if(m.sub_menu == &root_)
|
||||
m.sub_menu = nullptr;
|
||||
if(m->sub_menu == &root_)
|
||||
m->sub_menu = nullptr;
|
||||
}
|
||||
|
||||
for(auto & m : root_.items)
|
||||
{
|
||||
if(m.sub_menu)
|
||||
for(auto i = m.sub_menu->owner.begin(); i != m.sub_menu->owner.end();)
|
||||
if(m->sub_menu)
|
||||
for(auto i = m->sub_menu->owner.begin(); i != m->sub_menu->owner.end();)
|
||||
{
|
||||
if((*i) == &root_)
|
||||
i = m.sub_menu->owner.erase(i);
|
||||
i = m->sub_menu->owner.erase(i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
@@ -318,8 +322,6 @@ namespace nana
|
||||
public:
|
||||
using item_proxy = menu_item_type::item_proxy;
|
||||
|
||||
renderer_interface * renderer;
|
||||
|
||||
menu_drawer()
|
||||
{
|
||||
state_.active = npos;
|
||||
@@ -329,9 +331,16 @@ namespace nana
|
||||
detail_.border.x = detail_.border.y = 2;
|
||||
}
|
||||
|
||||
void close_menu_tree(std::function<void()> && fn)
|
||||
void set_run(menu_builder& mbuilder, menu_type& menu, std::function<void()> && menu_tree_destroyer)
|
||||
{
|
||||
fn_close_tree_ = std::move(fn);
|
||||
mbuilder_ = &mbuilder;
|
||||
menu_ = &menu;
|
||||
fn_close_tree_ = std::move(menu_tree_destroyer);
|
||||
}
|
||||
|
||||
menu_builder& mbuilder()
|
||||
{
|
||||
return *mbuilder_;
|
||||
}
|
||||
|
||||
void attached(widget_reference widget, graph_reference graph)
|
||||
@@ -364,10 +373,12 @@ namespace nana
|
||||
|
||||
void refresh(graph_reference graph)
|
||||
{
|
||||
if (nullptr == menu_) return;
|
||||
if (!(mbuilder_ && menu_))
|
||||
return;
|
||||
|
||||
_m_adjust_window_size();
|
||||
|
||||
auto renderer = mbuilder_->renderer().get();
|
||||
renderer->background(graph, *widget_);
|
||||
|
||||
const unsigned item_h_px = _m_item_height();
|
||||
@@ -376,12 +387,13 @@ namespace nana
|
||||
|
||||
unsigned strpixels = item_r.width - 60;
|
||||
|
||||
int text_top_off = (item_h_px - graph.text_extent_size(L"jh({[").height) / 2;
|
||||
int text_top_off = static_cast<int>(item_h_px - graph.text_extent_size(L"jh({[").height) / 2;
|
||||
|
||||
std::size_t pos = 0;
|
||||
for (auto & m : menu_->items)
|
||||
{
|
||||
if (m.flags.splitter)
|
||||
auto item_ptr = m.get();
|
||||
if (item_ptr->flags.splitter)
|
||||
{
|
||||
graph_->line({ item_r.x + 40, item_r.y }, { static_cast<int>(graph.width()) - 1, item_r.y }, colors::gray_border);
|
||||
item_r.y += 2;
|
||||
@@ -389,24 +401,24 @@ namespace nana
|
||||
continue;
|
||||
}
|
||||
|
||||
renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m);
|
||||
renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, *item_ptr);
|
||||
//Draw item background
|
||||
renderer->item(*graph_, item_r, attr);
|
||||
|
||||
//Draw text, the text is transformed from orignal for hotkey character
|
||||
wchar_t hotkey;
|
||||
std::string::size_type hotkey_pos;
|
||||
auto text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos);
|
||||
auto text = API::transform_shortkey_text(item_ptr->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);
|
||||
if (item_ptr->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, item_ptr->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)
|
||||
item_ptr->hotkey = hotkey;
|
||||
if (item_ptr->flags.enabled)
|
||||
{
|
||||
auto off_px = (hotkey_pos ? graph.text_extent_size(text.c_str(), hotkey_pos).width : 0);
|
||||
auto hotkey_px = graph.text_extent_size(text.c_str() + hotkey_pos, 1).width;
|
||||
@@ -421,7 +433,7 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
if (m.sub_menu)
|
||||
if (item_ptr->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;
|
||||
@@ -438,11 +450,14 @@ namespace nana
|
||||
bool goto_next(bool forword)
|
||||
{
|
||||
state_.nullify_mouse = true;
|
||||
if (menu_->items.empty())
|
||||
|
||||
auto & items = menu_->items;
|
||||
|
||||
if (items.empty())
|
||||
return false;
|
||||
|
||||
auto pos = state_.active;
|
||||
const auto lastpos = menu_->items.size() - 1;
|
||||
const auto lastpos = items.size() - 1;
|
||||
|
||||
bool end = false;
|
||||
while(true)
|
||||
@@ -477,7 +492,8 @@ namespace nana
|
||||
--pos;
|
||||
}
|
||||
|
||||
if(! menu_->items.at(pos).flags.splitter && menu_->items.at(pos).flags.enabled)
|
||||
auto item_ptr = items.at(pos).get();
|
||||
if (!item_ptr->flags.splitter && item_ptr->flags.enabled)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -497,13 +513,14 @@ namespace nana
|
||||
{
|
||||
if (!state_.nullify_mouse)
|
||||
{
|
||||
auto & items = menu_->items;
|
||||
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) && items.at(state_.active)->sub_menu && state_.sub_window)
|
||||
return false;
|
||||
|
||||
state_.active = (index != npos && menu_->items.at(index).flags.splitter) ? npos : index;
|
||||
state_.active = (index != npos && items.at(index)->flags.splitter) ? npos : index;
|
||||
state_.active_timestamp = nana::system::timestamp();
|
||||
return true;
|
||||
}
|
||||
@@ -512,11 +529,6 @@ namespace nana
|
||||
return false;
|
||||
}
|
||||
|
||||
void data(menu_type & menu)
|
||||
{
|
||||
menu_ = & menu;
|
||||
}
|
||||
|
||||
menu_type* data() const
|
||||
{
|
||||
return menu_;
|
||||
@@ -532,19 +544,20 @@ namespace nana
|
||||
if (npos == state_.active)
|
||||
return nullptr;
|
||||
|
||||
auto sub = menu_->items.at(state_.active).sub_menu;
|
||||
auto & items = menu_->items;
|
||||
auto sub = items.at(state_.active)->sub_menu;
|
||||
if (sub)
|
||||
{
|
||||
pos.x = static_cast<int>(graph_->width()) - 2;
|
||||
pos.y = 2;
|
||||
|
||||
auto index = state_.active;
|
||||
for (auto & m : menu_->items)
|
||||
for (auto & m : items)
|
||||
{
|
||||
if (0 == index--)
|
||||
break;
|
||||
|
||||
if (m.flags.splitter)
|
||||
if (m->flags.splitter)
|
||||
{
|
||||
pos.y += 2;
|
||||
continue;
|
||||
@@ -566,15 +579,16 @@ namespace nana
|
||||
std::size_t index = 0;
|
||||
for(auto & m : menu_->items)
|
||||
{
|
||||
if (std::tolower(m.hotkey) != key)
|
||||
auto item_ptr = m.get();
|
||||
if (std::tolower(m->hotkey) != key)
|
||||
{
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!m.flags.splitter)
|
||||
if (!item_ptr->flags.splitter)
|
||||
{
|
||||
if(m.sub_menu)
|
||||
if (item_ptr->sub_menu)
|
||||
{
|
||||
state_.active = index;
|
||||
state_.active_timestamp = nana::system::timestamp();
|
||||
@@ -582,13 +596,13 @@ namespace nana
|
||||
API::refresh_window(*widget_);
|
||||
return 2;
|
||||
}
|
||||
else if(m.flags.enabled)
|
||||
else if (item_ptr->flags.enabled)
|
||||
{
|
||||
fn_close_tree_();
|
||||
if (m.functor)
|
||||
if (item_ptr->event_handler)
|
||||
{
|
||||
item_proxy ip(index, m);
|
||||
m.functor.operator()(ip);
|
||||
item_proxy ip(index, *item_ptr);
|
||||
item_ptr->event_handler.operator()(ip);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -618,9 +632,9 @@ namespace nana
|
||||
|
||||
int pos = detail_.border.y;
|
||||
std::size_t index = 0;
|
||||
for(auto & m : menu_->items)
|
||||
for (auto & m : menu_->items)
|
||||
{
|
||||
unsigned h = (m.flags.splitter ? 1 : _m_item_height());
|
||||
unsigned h = (m->flags.splitter ? 1 : _m_item_height());
|
||||
if(pos <= y && y < static_cast<int>(pos + h))
|
||||
return index;
|
||||
else if(y < pos)
|
||||
@@ -641,13 +655,13 @@ namespace nana
|
||||
{
|
||||
nana::size size;
|
||||
|
||||
if(menu_->items.size())
|
||||
if (menu_->items.size())
|
||||
{
|
||||
for(auto & m : menu_->items)
|
||||
for (auto & m : menu_->items)
|
||||
{
|
||||
if(false == m.flags.splitter)
|
||||
if(false == m->flags.splitter)
|
||||
{
|
||||
nana::size item_size = graph_->text_extent_size(m.text);
|
||||
nana::size item_size = graph_->text_extent_size(m->text);
|
||||
if(size.width < item_size.width)
|
||||
size.width = item_size.width;
|
||||
}
|
||||
@@ -659,7 +673,7 @@ namespace nana
|
||||
size.height = static_cast<unsigned>(menu_->items.size() - size.height) * _m_item_height() + size.height + static_cast<unsigned>(menu_->items.size() - 1);
|
||||
}
|
||||
|
||||
if(size.width > menu_->max_pixels)
|
||||
if (size.width > menu_->max_pixels)
|
||||
size.width = menu_->max_pixels;
|
||||
|
||||
return size;
|
||||
@@ -688,14 +702,15 @@ namespace nana
|
||||
pos.y = scr_area.bottom() - static_cast<int>(size.height);
|
||||
if(pos.y < scr_area.y) pos.y = scr_area.y;
|
||||
|
||||
auto owner = API::get_owner_window(*widget_);
|
||||
API::calc_window_point(owner, pos);
|
||||
API::calc_window_point(API::get_owner_window(*widget_), pos);
|
||||
widget_->move(pos.x, pos.y);
|
||||
}
|
||||
private:
|
||||
widget *widget_{nullptr};
|
||||
paint::graphics *graph_{nullptr};
|
||||
menu_type *menu_{nullptr};
|
||||
|
||||
menu_builder* mbuilder_{ nullptr };
|
||||
menu_type* menu_{ nullptr };
|
||||
|
||||
std::function<void()> fn_close_tree_;
|
||||
|
||||
@@ -723,7 +738,7 @@ namespace nana
|
||||
using item_type = menu_builder::item_type;
|
||||
|
||||
|
||||
menu_window(window wd, bool is_wd_parent_menu, const point& pos, renderer_interface * rdptr)
|
||||
menu_window(window wd, bool is_wd_parent_menu, const point& pos, menu_builder& mbuilder, menu_type& menu_data)
|
||||
//add a is_wd_parent_menu to determine whether the menu wants the focus.
|
||||
//if a submenu gets the focus, the program may cause a crash error when the submenu is being destroyed
|
||||
: base_type(wd, false, rectangle(pos, nana::size(2, 2)), appear::bald<appear::floating>()),
|
||||
@@ -731,8 +746,8 @@ namespace nana
|
||||
event_focus_{ nullptr }
|
||||
{
|
||||
caption("nana menu window");
|
||||
get_drawer_trigger().close_menu_tree([this]{ this->_m_close_all(); });
|
||||
get_drawer_trigger().renderer = rdptr;
|
||||
get_drawer_trigger().set_run(mbuilder, menu_data, [this]{ this->_m_close_all(); });
|
||||
|
||||
state_.owner_menubar = state_.self_submenu = false;
|
||||
state_.auto_popup_submenu = true;
|
||||
|
||||
@@ -740,7 +755,7 @@ namespace nana
|
||||
submenu_.object = nullptr;
|
||||
|
||||
state_.mouse_pos = API::cursor_position();
|
||||
events().mouse_move.connect_unignorable([this]{
|
||||
events().mouse_move.connect_unignorable([this](const arg_mouse&){
|
||||
nana::point pos = API::cursor_position();
|
||||
if (pos != state_.mouse_pos)
|
||||
{
|
||||
@@ -754,10 +769,8 @@ namespace nana
|
||||
});
|
||||
}
|
||||
|
||||
void popup(menu_type& menu, bool owner_menubar)
|
||||
void popup(bool owner_menubar)
|
||||
{
|
||||
get_drawer_trigger().data(menu);
|
||||
|
||||
if (!want_focus_)
|
||||
{
|
||||
API::activate_window(this->parent());
|
||||
@@ -771,7 +784,7 @@ namespace nana
|
||||
}
|
||||
|
||||
auto & events = this->events();
|
||||
events.destroy.connect_unignorable([this]{
|
||||
events.destroy.connect_unignorable([this](const arg_destroy&){
|
||||
_m_destroy();
|
||||
});
|
||||
|
||||
@@ -881,7 +894,7 @@ namespace nana
|
||||
if ((npos == active) || !menu)
|
||||
return;
|
||||
|
||||
menu_item_type & item = menu->items.at(active);
|
||||
menu_item_type & item = *(menu->items.at(active));
|
||||
|
||||
if ((!item.flags.enabled) || item.flags.splitter || item.sub_menu)
|
||||
return;
|
||||
@@ -892,35 +905,18 @@ namespace nana
|
||||
}
|
||||
else if (checks::option == item.style)
|
||||
{
|
||||
//Forward Looks for a splitter
|
||||
auto pos = active;
|
||||
while (pos)
|
||||
{
|
||||
if (menu->items.at(--pos).flags.splitter)
|
||||
break;
|
||||
}
|
||||
|
||||
for (; pos < menu->items.size(); ++pos)
|
||||
{
|
||||
menu_item_type & im = menu->items.at(pos);
|
||||
if (im.flags.splitter) break;
|
||||
|
||||
if ((checks::option == im.style) && im.flags.checked)
|
||||
im.flags.checked = false;
|
||||
}
|
||||
|
||||
item.flags.checked = true;
|
||||
get_drawer_trigger().mbuilder().checked(active, true);
|
||||
}
|
||||
|
||||
this->_m_close_all(); //means deleting this;
|
||||
//The deleting operation has moved here, because item.functor.operator()(ip)
|
||||
//The deleting operation has moved here, because item.event_handler.operator()(ip)
|
||||
//may create a window, which make a killing focus for menu window, if so the close_all
|
||||
//operation preformences after item.functor.operator()(ip), that would be deleting this object twice!
|
||||
//operation preformences after item.event_handler.operator()(ip), that would be deleting this object twice!
|
||||
|
||||
if (item.functor)
|
||||
if (item.event_handler)
|
||||
{
|
||||
item_type::item_proxy ip(active, item);
|
||||
item.functor.operator()(ip);
|
||||
item.event_handler.operator()(ip);
|
||||
}
|
||||
}
|
||||
private:
|
||||
@@ -1035,7 +1031,7 @@ namespace nana
|
||||
menu_ptr->gaps = drawer.data()->gaps;
|
||||
pos += menu_ptr->gaps;
|
||||
|
||||
menu_window & mwnd = form_loader<menu_window, false>()(handle(), true, pos, drawer.renderer);
|
||||
menu_window & mwnd = form_loader<menu_window, false>()(handle(), true, pos, drawer.mbuilder(), *menu_ptr);
|
||||
mwnd.state_.self_submenu = true;
|
||||
submenu_.child = &mwnd;
|
||||
submenu_.child->submenu_.parent = this;
|
||||
@@ -1043,7 +1039,7 @@ namespace nana
|
||||
|
||||
API::set_window_z_order(handle(), mwnd.handle(), z_order_action::none);
|
||||
|
||||
mwnd.popup(*menu_ptr, state_.owner_menubar);
|
||||
mwnd.popup(state_.owner_menubar);
|
||||
drawer.set_sub_window(true);
|
||||
if (forced)
|
||||
mwnd.goto_next(true);
|
||||
@@ -1100,7 +1096,7 @@ namespace nana
|
||||
};
|
||||
|
||||
drawerbase::menu::menu_builder mbuilder;
|
||||
drawerbase::menu::menu_window * uiobj;
|
||||
drawerbase::menu::menu_window * window_ptr;
|
||||
std::function<void()> destroy_answer;
|
||||
std::map<std::size_t, info> sub_container;
|
||||
};
|
||||
@@ -1108,7 +1104,7 @@ namespace nana
|
||||
menu::menu()
|
||||
:impl_(new implement)
|
||||
{
|
||||
impl_->uiobj = nullptr;
|
||||
impl_->window_ptr = nullptr;
|
||||
}
|
||||
|
||||
menu::~menu()
|
||||
@@ -1123,32 +1119,34 @@ namespace nana
|
||||
|
||||
auto menu::append(std::string text_utf8, const menu::event_fn_t& f) -> item_proxy
|
||||
{
|
||||
impl_->mbuilder.data().items.emplace_back(std::move(text_utf8), f);
|
||||
return item_proxy(size() - 1, impl_->mbuilder.data().items.back());
|
||||
impl_->mbuilder.data().items.emplace_back(new item_type(std::move(text_utf8, callback));
|
||||
return item_proxy(size() - 1, *impl_->mbuilder.data().items.back());
|
||||
}
|
||||
|
||||
void menu::append_splitter()
|
||||
{
|
||||
impl_->mbuilder.data().items.emplace_back();
|
||||
impl_->mbuilder.data().items.emplace_back(new item_type);
|
||||
}
|
||||
|
||||
void menu::clear()
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
impl_->mbuilder.data().items.clear();
|
||||
}
|
||||
|
||||
void menu::enabled(std::size_t index, bool enable)
|
||||
{
|
||||
impl_->mbuilder.data().items.at(index).flags.enabled = enable;
|
||||
impl_->mbuilder.data().items.at(index)->flags.enabled = enable;
|
||||
}
|
||||
|
||||
bool menu::enabled(std::size_t index) const
|
||||
{
|
||||
return impl_->mbuilder.data().items.at(index).flags.enabled;
|
||||
return impl_->mbuilder.data().items.at(index)->flags.enabled;
|
||||
}
|
||||
|
||||
void menu::erase(std::size_t index)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & items = impl_->mbuilder.data().items;
|
||||
if(index < items.size())
|
||||
items.erase(items.begin() + index);
|
||||
@@ -1156,7 +1154,7 @@ namespace nana
|
||||
|
||||
void menu::image(std::size_t index, const paint::image& img)
|
||||
{
|
||||
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)
|
||||
@@ -1202,22 +1200,22 @@ namespace nana
|
||||
|
||||
void menu::popup(window wd, int x, int y)
|
||||
{
|
||||
_m_popup(wd, x, y, false);
|
||||
_m_popup(wd, { x, y }, false);
|
||||
}
|
||||
|
||||
void menu::popup_await(window wd, int x, int y)
|
||||
{
|
||||
_m_popup(wd, x, y, false);
|
||||
if (impl_->uiobj)
|
||||
API::wait_for(impl_->uiobj->handle());
|
||||
_m_popup(wd, { x, y }, false);
|
||||
if (impl_->window_ptr)
|
||||
API::wait_for(impl_->window_ptr->handle());
|
||||
}
|
||||
|
||||
void menu::close()
|
||||
{
|
||||
if(impl_->uiobj)
|
||||
if (impl_->window_ptr)
|
||||
{
|
||||
impl_->uiobj->close();
|
||||
impl_->uiobj = nullptr;
|
||||
impl_->window_ptr->close();
|
||||
impl_->window_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,17 +1231,17 @@ namespace nana
|
||||
|
||||
bool menu::checked(std::size_t index) const
|
||||
{
|
||||
return impl_->mbuilder.data().items.at(index).flags.checked;
|
||||
return impl_->mbuilder.data().items.at(index)->flags.checked;
|
||||
}
|
||||
|
||||
void menu::answerer(std::size_t index, const event_fn_t& fn)
|
||||
{
|
||||
impl_->mbuilder.data().items.at(index).functor = fn;
|
||||
impl_->mbuilder.data().items.at(index)->event_handler = fn;
|
||||
}
|
||||
|
||||
void menu::destroy_answer(const std::function<void()>& fn)
|
||||
void menu::destroy_answer(std::function<void()> fn)
|
||||
{
|
||||
impl_->destroy_answer = fn;
|
||||
impl_->destroy_answer = std::move(fn);
|
||||
}
|
||||
|
||||
void menu::gaps(const nana::point& pos)
|
||||
@@ -1253,18 +1251,18 @@ namespace nana
|
||||
|
||||
void menu::goto_next(bool forward)
|
||||
{
|
||||
if(impl_->uiobj)
|
||||
impl_->uiobj->goto_next(forward);
|
||||
if (impl_->window_ptr)
|
||||
impl_->window_ptr->goto_next(forward);
|
||||
}
|
||||
|
||||
bool menu::goto_submen()
|
||||
{
|
||||
return (impl_->uiobj ? impl_->uiobj->submenu(true) : false);
|
||||
return (impl_->window_ptr ? impl_->window_ptr->submenu(true) : false);
|
||||
}
|
||||
|
||||
bool menu::exit_submenu()
|
||||
{
|
||||
return (impl_->uiobj ? impl_->uiobj->submenu(false) : false);
|
||||
return (impl_->window_ptr ? impl_->window_ptr->submenu(false) : false);
|
||||
}
|
||||
|
||||
std::size_t menu::size() const
|
||||
@@ -1274,13 +1272,13 @@ namespace nana
|
||||
|
||||
int menu::send_shortkey(wchar_t key)
|
||||
{
|
||||
return (impl_->uiobj ? impl_->uiobj->send_shortkey(key) : 0);
|
||||
return (impl_->window_ptr ? impl_->window_ptr->send_shortkey(key) : 0);
|
||||
}
|
||||
|
||||
void menu::pick()
|
||||
{
|
||||
if (impl_->uiobj)
|
||||
impl_->uiobj->pick();
|
||||
if (impl_->window_ptr)
|
||||
impl_->window_ptr->pick();
|
||||
}
|
||||
|
||||
menu& menu::max_pixels(unsigned px)
|
||||
@@ -1315,19 +1313,19 @@ namespace nana
|
||||
impl_->mbuilder.renderer(rd);
|
||||
}
|
||||
|
||||
void menu::_m_popup(window wd, int x, int y, bool called_by_menubar)
|
||||
void menu::_m_popup(window wd, const point& pos, bool called_by_menubar)
|
||||
{
|
||||
if (impl_->mbuilder.data().items.size())
|
||||
{
|
||||
close();
|
||||
|
||||
impl_->uiobj = &(form_loader<drawerbase::menu::menu_window, false>()(wd, false, point(x, y), &(*impl_->mbuilder.renderer())));
|
||||
impl_->uiobj->events().destroy.connect_unignorable([this]{
|
||||
impl_->uiobj = nullptr;
|
||||
impl_->window_ptr = &(form_loader<drawerbase::menu::menu_window, false>()(wd, false, pos, impl_->mbuilder, impl_->mbuilder.data()));
|
||||
impl_->window_ptr->events().destroy.connect_unignorable([this](const arg_destroy&){
|
||||
impl_->window_ptr = nullptr;
|
||||
if (impl_->destroy_answer)
|
||||
impl_->destroy_answer();
|
||||
});
|
||||
impl_->uiobj->popup(impl_->mbuilder.data(), called_by_menubar);
|
||||
impl_->window_ptr->popup(called_by_menubar);
|
||||
}
|
||||
}
|
||||
//end class menu
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace nana
|
||||
public:
|
||||
static void popup(menu& m, window wd, int x, int y)
|
||||
{
|
||||
m._m_popup(wd, x, y, true);
|
||||
m._m_popup(wd, { x, y }, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1574,7 +1574,8 @@ namespace nana{ namespace widgets
|
||||
_m_handle_move_key(arg);
|
||||
break;
|
||||
case keyboard::os_del:
|
||||
if (this->attr().editable)
|
||||
// send delete to set_accept function
|
||||
if (this->attr().editable && (!impl_->capacities.pred_acceptive || impl_->capacities.pred_acceptive(key)))
|
||||
del();
|
||||
break;
|
||||
default:
|
||||
@@ -1746,6 +1747,13 @@ namespace nana{ namespace widgets
|
||||
return impl_->undo.max_steps();
|
||||
}
|
||||
|
||||
void text_editor::clear_undo()
|
||||
{
|
||||
auto size = impl_->undo.max_steps();
|
||||
impl_->undo.max_steps(0);
|
||||
impl_->undo.max_steps(size);
|
||||
}
|
||||
|
||||
auto text_editor::customized_renderers() -> renderers&
|
||||
{
|
||||
return impl_->customized_renderers;
|
||||
@@ -1797,7 +1805,7 @@ namespace nana{ namespace widgets
|
||||
if (select_all)
|
||||
{
|
||||
select(true);
|
||||
move_caret_end();
|
||||
move_caret_end(false);
|
||||
renderred = true;
|
||||
|
||||
//If the text widget is focused by clicking mouse button, the selected text will be cancelled
|
||||
@@ -1991,8 +1999,6 @@ namespace nana{ namespace widgets
|
||||
return str;
|
||||
}
|
||||
|
||||
//move_caret
|
||||
//Set caret position through text coordinate
|
||||
bool text_editor::move_caret(const upoint& crtpos, bool reset_caret)
|
||||
{
|
||||
const unsigned line_pixels = line_height();
|
||||
@@ -2036,11 +2042,14 @@ namespace nana{ namespace widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
void text_editor::move_caret_end()
|
||||
void text_editor::move_caret_end(bool update)
|
||||
{
|
||||
points_.caret.y = static_cast<unsigned>(impl_->textbase.lines());
|
||||
if(points_.caret.y) --points_.caret.y;
|
||||
points_.caret.x = static_cast<unsigned>(impl_->textbase.getline(points_.caret.y).size());
|
||||
|
||||
if (update)
|
||||
this->move_caret(points_.caret, false);
|
||||
}
|
||||
|
||||
void text_editor::reset_caret_pixels() const
|
||||
@@ -2064,6 +2073,25 @@ namespace nana{ namespace widgets
|
||||
return (select_.a != select_.b);
|
||||
}
|
||||
|
||||
bool text_editor::get_selected_points(nana::upoint &a, nana::upoint &b) const
|
||||
{
|
||||
if (select_.a == select_.b)
|
||||
return false;
|
||||
|
||||
if (select_.a < select_.b)
|
||||
{
|
||||
a = select_.a;
|
||||
b = select_.b;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = select_.b;
|
||||
b = select_.a;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void text_editor::set_end_caret()
|
||||
{
|
||||
bool new_sel_end = (select_.b != points_.caret);
|
||||
@@ -2096,25 +2124,6 @@ namespace nana{ namespace widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
bool text_editor::get_select_points(nana::upoint& a, nana::upoint& b) const
|
||||
{
|
||||
if (select_.a == select_.b)
|
||||
return false;
|
||||
|
||||
if (select_.a < select_.b)
|
||||
{
|
||||
a = select_.a;
|
||||
b = select_.b;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = select_.b;
|
||||
b = select_.a;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool text_editor::hit_text_area(const point& pos) const
|
||||
{
|
||||
return _m_text_area().is_hit(pos);
|
||||
@@ -2123,7 +2132,7 @@ namespace nana{ namespace widgets
|
||||
bool text_editor::hit_select_area(nana::upoint pos, bool ignore_when_select_all) const
|
||||
{
|
||||
nana::upoint a, b;
|
||||
if(get_select_points(a, b))
|
||||
if (get_selected_points(a, b))
|
||||
{
|
||||
if (ignore_when_select_all)
|
||||
{
|
||||
@@ -3045,7 +3054,7 @@ namespace nana{ namespace widgets
|
||||
nana::upoint text_editor::_m_erase_select()
|
||||
{
|
||||
nana::upoint a, b;
|
||||
if (get_select_points(a, b))
|
||||
if (get_selected_points(a, b))
|
||||
{
|
||||
auto & textbase = this->textbase();
|
||||
if(a.y != b.y)
|
||||
@@ -3076,7 +3085,7 @@ namespace nana{ namespace widgets
|
||||
std::wstring text;
|
||||
|
||||
nana::upoint a, b;
|
||||
if (get_select_points(a, b))
|
||||
if (get_selected_points(a, b))
|
||||
{
|
||||
auto & textbase = this->textbase();
|
||||
if (a.y != b.y)
|
||||
@@ -3167,7 +3176,7 @@ namespace nana{ namespace widgets
|
||||
bool text_editor::_m_cancel_select(int align)
|
||||
{
|
||||
nana::upoint a, b;
|
||||
if (get_select_points(a, b))
|
||||
if (get_selected_points(a, b))
|
||||
{
|
||||
switch(align)
|
||||
{
|
||||
@@ -3262,7 +3271,7 @@ namespace nana{ namespace widgets
|
||||
const bool at_left = (select_.b < select_.a);
|
||||
|
||||
nana::upoint a, b;
|
||||
get_select_points(a, b);
|
||||
get_selected_points(a, b);
|
||||
if (caret.y < a.y || (caret.y == a.y && caret.x < a.x))
|
||||
{//forward
|
||||
undo_ptr->set_caret_pos();
|
||||
@@ -3458,7 +3467,7 @@ namespace nana{ namespace widgets
|
||||
const wchar_t *sbegin = nullptr, *send = nullptr;
|
||||
|
||||
nana::upoint a, b;
|
||||
if (get_select_points(a, b))
|
||||
if (get_selected_points(a, b))
|
||||
{
|
||||
if (a.y < text_coord.y && text_coord.y < b.y)
|
||||
{
|
||||
|
||||
@@ -69,10 +69,10 @@ namespace drawerbase {
|
||||
auto scheme = API::dev::get_scheme(wdg);
|
||||
|
||||
editor_ = new text_editor(wd, graph, dynamic_cast<::nana::widgets::skeletons::text_editor_scheme*>(scheme));
|
||||
editor_->textbase().set_event_agent(evt_agent_.get());
|
||||
editor_->set_event(evt_agent_.get());
|
||||
|
||||
evt_agent_.reset(new event_agent(static_cast<::nana::textbox&>(wdg), editor_->text_position()));
|
||||
editor_->textbase().set_event_agent(evt_agent_.get());
|
||||
editor_->set_event(evt_agent_.get());
|
||||
|
||||
_m_text_area(graph.width(), graph.height());
|
||||
|
||||
@@ -260,8 +260,11 @@ namespace drawerbase {
|
||||
internal_scope_guard lock;
|
||||
auto editor = get_drawer_trigger().editor();
|
||||
if (editor)
|
||||
{
|
||||
editor->text(to_wstring(str), end_caret);
|
||||
{
|
||||
editor->text(to_wstring(str), false);
|
||||
|
||||
if (end_caret)
|
||||
editor->move_caret_end(true);
|
||||
|
||||
editor->textbase().reset();
|
||||
API::update_window(this->handle());
|
||||
@@ -319,6 +322,25 @@ namespace drawerbase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool textbox::getline(std::size_t line_index,std::size_t start_point,std::string& text) const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto editor = get_drawer_trigger().editor();
|
||||
if(editor)
|
||||
{
|
||||
std::wstring line_text;
|
||||
if(editor->getline(line_index,line_text))
|
||||
{
|
||||
if(line_text.length() >= start_point)
|
||||
{
|
||||
text = to_utf8(line_text.substr(start_point));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Gets the caret position
|
||||
bool textbox::caret_pos(point& pos, bool text_coordinate) const
|
||||
{
|
||||
@@ -358,7 +380,7 @@ namespace drawerbase {
|
||||
if(editor)
|
||||
{
|
||||
if(at_caret == false)
|
||||
editor->move_caret_end();
|
||||
editor->move_caret_end(false);
|
||||
|
||||
editor->put(to_wstring(text));
|
||||
API::update_window(this->handle());
|
||||
@@ -461,6 +483,13 @@ namespace drawerbase {
|
||||
return (editor ? editor->selected() : false);
|
||||
}
|
||||
|
||||
bool textbox::get_selected_points(nana::upoint &a, nana::upoint &b) const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto editor = get_drawer_trigger().editor();
|
||||
return (editor ? editor->get_selected_points(a, b) : false);
|
||||
}
|
||||
|
||||
void textbox::select(bool yes)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
@@ -476,7 +505,7 @@ namespace drawerbase {
|
||||
internal_scope_guard lock;
|
||||
auto editor = get_drawer_trigger().editor();
|
||||
if (editor)
|
||||
editor->get_select_points(points.first, points.second);
|
||||
editor->get_selected_points(points.first, points.second);
|
||||
|
||||
return points;
|
||||
}
|
||||
@@ -539,6 +568,11 @@ namespace drawerbase {
|
||||
return *this;
|
||||
}
|
||||
|
||||
void textbox::clear_undo()
|
||||
{
|
||||
get_drawer_trigger().editor()->clear_undo();
|
||||
}
|
||||
|
||||
void textbox::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace nana
|
||||
|
||||
void pump()
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
detail::bedrock::instance().pump_event(nullptr, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,18 @@ namespace paint
|
||||
return (impl_->font_ptr->italic);
|
||||
}
|
||||
|
||||
bool font::underline() const
|
||||
{
|
||||
if(empty()) return false;
|
||||
return (impl_->font_ptr->underline);
|
||||
}
|
||||
|
||||
bool font::strikeout() const
|
||||
{
|
||||
if(empty()) return false;
|
||||
return (impl_->font_ptr->strikeout);
|
||||
}
|
||||
|
||||
native_font_type font::handle() const
|
||||
{
|
||||
if(empty()) return nullptr;
|
||||
|
||||
@@ -356,11 +356,28 @@ namespace threads
|
||||
{
|
||||
}
|
||||
|
||||
pool::pool(pool&& other)
|
||||
: pool()
|
||||
{
|
||||
std::swap(impl_, other.impl_);
|
||||
}
|
||||
|
||||
pool::pool(std::size_t thread_number)
|
||||
: impl_(new impl(thread_number))
|
||||
{
|
||||
}
|
||||
|
||||
pool& pool::operator=(pool&& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
delete impl_;
|
||||
impl_ = other.impl_;
|
||||
other.impl_ = new impl(4);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
pool::~pool()
|
||||
{
|
||||
delete impl_;
|
||||
|
||||
Reference in New Issue
Block a user