Merge branch 'hotfix-1.4.1' into develop

This commit is contained in:
Jinhao 2016-10-31 06:21:41 +08:00
commit b1ce0eaff2
26 changed files with 428 additions and 342 deletions

View File

@ -102,7 +102,7 @@
#define _CRT_SECURE_NO_DEPRECATE
#pragma warning(disable : 4996)
#if (_MSC_VER == 1900)
#if (_MSC_VER >= 1900)
// google: break any code that tries to use codecvt<char16_t> or codecvt<char32_t>.
// google: It appears the C++ libs haven't been compiled with native char16_t/char32_t support.
// google: Those definitions are for codecvt<wchar_t>::id, codecvt<unsigned short>::id and codecvt<char>::id respectively.

View File

@ -85,7 +85,7 @@ namespace detail
enum class update_state
{
none, lazy, refresh
none, lazy, refreshed, request_refresh
};
struct edge_nimbus_action

View File

@ -86,7 +86,7 @@ namespace nana{
}
//Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered.
if ((forced && (action.window == wd)) || (focused == action.window) || !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::refreshed))
{
rd_set.emplace_back(r, action.window);
action.rendered = true;

View File

@ -1,6 +1,6 @@
/*
* Window Layout Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -42,10 +42,16 @@ namespace detail
core_window_t * window;
rectangle r;
};
public:
static void paint(core_window_t*, bool is_redraw, bool is_child_refreshed);
static bool maproot(core_window_t*, bool have_refreshed, bool is_child_refreshed);
enum class paint_operation {
none,
have_refreshed,
try_refresh
};
public:
static void paint(core_window_t*, paint_operation, bool request_refresh_children);
static bool maproot(core_window_t*, bool have_refreshed, bool request_refresh_children);
static void paste_children_to_graphics(core_window_t*, nana::paint::graphics& graph);
@ -68,13 +74,12 @@ namespace detail
//_m_paste_children
//@brief:paste children window to the root graphics directly. just paste the visual rectangle
static void _m_paste_children(core_window_t*, bool is_child_refreshed, bool have_refreshed, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos);
static void _m_paste_children(core_window_t*, bool have_refreshed, bool request_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos);
static void _m_paint_glass_window(core_window_t*, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other);
//_m_notify_glasses
//@brief: Notify the glass windows that are overlapped with the specified vis_rect
static void _m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& r_visual);
//Notify the windows which have brground to update their background buffer.
static void _m_notify_glasses(core_window_t* const sigwd);
private:
struct data_section
{

View File

@ -124,7 +124,7 @@ namespace detail
bool update(core_window_t*, bool redraw, bool force, const rectangle* update_area = nullptr);
void refresh_tree(core_window_t*);
bool do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false);
void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false);
bool get_graphics(core_window_t*, nana::paint::graphics&);
bool get_visual_rectangle(core_window_t*, nana::rectangle&);

View File

@ -14,10 +14,7 @@
#ifndef NANA_GUI_WIDGETS_MENU_HPP
#define NANA_GUI_WIDGETS_MENU_HPP
#include "widget.hpp"
#include <vector>
#include <nana/gui/timer.hpp>
#include <nana/pat/cloneable.hpp>
#include <nana/push_ignore_diagnostic>
namespace nana
@ -72,25 +69,12 @@ namespace nana
menu_type *sub_menu{nullptr};
std::string text;
event_fn_t functor;
event_fn_t event_handler;
checks style{checks::none};
paint::image image;
mutable wchar_t hotkey{0};
};
struct menu_type
{
typedef std::vector<menu_item_type> item_container;
typedef item_container::iterator iterator;
typedef item_container::const_iterator const_iterator;
std::vector<menu_type*> owner;
std::vector<menu_item_type> items;
unsigned max_pixels;
unsigned item_pixels;
nana::point gaps;
};
class renderer_interface
{
public:
@ -158,7 +142,7 @@ namespace nana
void popup(window owner, int x, int y); ///< Popup the menu at the owner window.
void popup_await(window owner, int x, int y);
void answerer(std::size_t index, const event_fn_t&); ///< Modify answerer of the specified item.
void destroy_answer(const std::function<void()>&); ///< Sets an answerer for the callback while the menu window is closing.
void destroy_answer(std::function<void()>); ///< Sets an answerer for the callback while the menu window is closing.
void gaps(const nana::point&); ///< Sets the gap between a menu and its sub menus.(\See Note4)
void goto_next(bool forward); ///< Moves the focus to the next or previous item.
bool goto_submen();///< Popup the submenu of the current item if it has a sub menu. Returns true if succeeds.
@ -177,7 +161,7 @@ namespace nana
const pat::cloneable<renderer_interface>& renderer() const;
private:
void _m_popup(window, int x, int y, bool called_by_menubar);
void _m_popup(window, const point& position, bool called_by_menubar);
private:
implement * impl_;
};

View File

@ -134,6 +134,7 @@ namespace nana{ namespace widgets
std::size_t undo_max_steps() const;
renderers& customized_renderers();
void clear_undo(); ///< same with undo_max_steps(0)
unsigned line_height() const;
unsigned screen_lines() const;
@ -148,14 +149,15 @@ namespace nana{ namespace widgets
* @param reset indicates whether to reset the text position by the pos. If this parameter is true, the text position is set by pos. If the parameter is false, it only moves the UI caret to the specified position.
*/
bool move_caret(const upoint& pos, bool reset = false);
void move_caret_end();
void move_caret_end(bool update);
void reset_caret_pixels() const;
void reset_caret();
void show_caret(bool isshow);
bool selected() const;
bool get_selected_points(nana::upoint&, nana::upoint&) const;
bool select(bool);
bool get_select_points(nana::upoint&, nana::upoint&) const;
/// Sets the end position of a selected string.
void set_end_caret();

View File

@ -162,6 +162,9 @@ namespace nana
/// Read the text from a specified line. It returns true for success.
bool getline(std::size_t pos, std::string&) const;
/// Read the text from a specified line with a set offset. It returns true for success.
bool getline(std::size_t line_index,std::size_t offset,std::string& text) const;
/// Gets the caret position
/// Returns true if the caret is in the area of display, false otherwise.
bool caret_pos(point& pos, bool text_coordinate) const;
@ -196,6 +199,7 @@ namespace nana
/// Returns true if some text is selected.
bool selected() const;
bool get_selected_points(nana::upoint &a, nana::upoint &b) const;
/// Selects/unselects all text.
void select(bool);
@ -215,6 +219,8 @@ namespace nana
textbox& from(int);
textbox& from(double);
void clear_undo();
void set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor);
void erase_highlight(const std::string& name);
void set_keywords(const std::string& name, bool case_sensitive, bool whole_word_match, std::initializer_list<std::wstring> kw_list);

View File

@ -51,6 +51,8 @@ namespace nana
bool italic() const;
native_font_type handle() const;
void release();
bool strikeout() const;
bool underline() const;
font& operator=(const font&);
bool operator==(const font&) const;

View File

@ -54,11 +54,17 @@ namespace threads
struct task_signal;
class impl;
pool(const pool&) = delete;
pool& operator=(const pool&) = delete;
public:
pool(); ///< Creates a group of threads.
pool(pool&&);
pool(std::size_t thread_number); ///< Creates a number of threads specifed by thread_number.
~pool(); ///< waits for the all running tasks till they are finished and skips all the queued tasks.
pool& operator=(pool&&);
template<typename Function>
void push(const Function& f)
{

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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());
});
}

View File

@ -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;
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

View File

@ -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);
}
};

View File

@ -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)
{

View File

@ -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;

View File

@ -46,6 +46,7 @@ namespace nana
void pump()
{
internal_scope_guard lock;
detail::bedrock::instance().pump_event(nullptr, false);
}

View File

@ -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;

View File

@ -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_;