diff --git a/build/codeblocks/nana.cbp b/build/codeblocks/nana.cbp
index 973b633f..83985a24 100644
--- a/build/codeblocks/nana.cbp
+++ b/build/codeblocks/nana.cbp
@@ -91,6 +91,7 @@
+
diff --git a/build/codeblocks/nana.layout b/build/codeblocks/nana.layout
index 8af72c72..57a19817 100644
--- a/build/codeblocks/nana.layout
+++ b/build/codeblocks/nana.layout
@@ -1,11 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -21,21 +51,26 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -46,29 +81,59 @@
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -81,74 +146,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj
index 89c4ae25..d6b6e976 100644
--- a/build/vc2013/nana.vcxproj
+++ b/build/vc2013/nana.vcxproj
@@ -139,6 +139,7 @@
+
diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters
index c4738ee4..f0f82981 100644
--- a/build/vc2013/nana.vcxproj.filters
+++ b/build/vc2013/nana.vcxproj.filters
@@ -297,5 +297,8 @@
Source Files\nana\gui
+
+ Source Files\nana\gui\widgets
+
\ No newline at end of file
diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp
index 0524db4b..759f8096 100644
--- a/include/nana/gui/detail/basic_window.hpp
+++ b/include/nana/gui/detail/basic_window.hpp
@@ -155,7 +155,7 @@ namespace detail
{
bool enabled :1;
bool dbl_click :1;
- bool capture :1; //if mouse button is down, it always receive mouse move even the mouse is out of its rectangle
+ bool captured :1; //if mouse button is down, it always receive mouse move even the mouse is out of its rectangle
bool modal :1;
bool take_active:1; //If take_active is false, other.active_window still keeps the focus.
bool refreshing :1;
diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp
index b91c728f..2f62fee1 100644
--- a/include/nana/gui/detail/bedrock.hpp
+++ b/include/nana/gui/detail/bedrock.hpp
@@ -42,7 +42,7 @@ namespace detail
~bedrock();
void pump_event(window, bool is_modal);
- void map_thread_root_buffer(core_window_t* );
+ void map_thread_root_buffer(core_window_t*, bool forced);
static int inc_window(unsigned tid = 0);
thread_context* open_thread_context(unsigned tid = 0);
thread_context* get_thread_context(unsigned tid = 0);
diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp
index 7b11fc37..dd0c4192 100644
--- a/include/nana/gui/detail/drawer.hpp
+++ b/include/nana/gui/detail/drawer.hpp
@@ -110,7 +110,7 @@ namespace nana
void key_char(const arg_keyboard&);
void key_release(const arg_keyboard&);
void shortkey(const arg_keyboard&);
- void map(window); //Copy the root buffer to screen
+ void map(window, bool forced); //Copy the root buffer to screen
void refresh();
drawer_trigger* realizer() const;
void attached(widget&, drawer_trigger&);
diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp
index 27ea4ab6..3489dbaf 100644
--- a/include/nana/gui/detail/effects_renderer.hpp
+++ b/include/nana/gui/detail/effects_renderer.hpp
@@ -28,7 +28,7 @@ namespace nana{
return 2;
}
- bool render(core_window_t * wd)
+ bool render(core_window_t * wd, bool forced)
{
bool rendered = false;
core_window_t * root_wd = wd->root_widget;
@@ -53,7 +53,7 @@ namespace nana{
rendered = true;
//Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered.
- if (!action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh))
+ if ((forced && (action.window == wd)) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh))
{
rd_set.emplace_back(r, action.window);
action.rendered = true;
diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp
index baab1232..2e07ac31 100644
--- a/include/nana/gui/detail/window_manager.hpp
+++ b/include/nana/gui/detail/window_manager.hpp
@@ -150,7 +150,7 @@ namespace detail
core_window_t* root(native_window_type) const;
//Copy the root buffer that wnd specified into DeviceContext
- void map(core_window_t*);
+ void map(core_window_t*, bool forced);
bool update(core_window_t*, bool redraw, bool force);
void refresh_tree(core_window_t*);
diff --git a/include/nana/gui/element.hpp b/include/nana/gui/element.hpp
index b0cdc9e1..ee63ecf0 100644
--- a/include/nana/gui/element.hpp
+++ b/include/nana/gui/element.hpp
@@ -106,6 +106,9 @@ namespace nana
void add_arrow(const std::string&, const pat::cloneable>&);
arrow_interface* const * keeper_arrow(const std::string&);
+
+ void add_button(const std::string&, const pat::cloneable>&);
+ element_interface* const* keeper_button(const std::string&);
};
class crook;
@@ -131,6 +134,14 @@ namespace nana
using factory_t = provider::factory;
provider().add_arrow(name, pat::cloneable(factory_t()));
}
+
+ class button;
+ template
+ void add_button(const std::string& name)
+ {
+ using factory_t = provider::factory;
+ provider().add_button(name, pat::cloneable(factory_t()));
+ }
}//end namespace element
template class facade;
@@ -200,6 +211,20 @@ namespace nana
::nana::direction dir_{::nana::direction::north};
};//end class facade
+ template<>
+ class facade
+ : public element::element_interface
+ {
+ public:
+ facade(const char* name = nullptr);
+ void switch_to(const char*);
+ public:
+ //Implement element_interface
+ bool draw(graph_reference, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle&, element_state) override;
+ private:
+ element::element_interface* const * keeper_;
+ };//end class facade
+
namespace element
{
diff --git a/include/nana/gui/widgets/date_chooser.hpp b/include/nana/gui/widgets/date_chooser.hpp
index d6aa3fb8..5c9e3507 100644
--- a/include/nana/gui/widgets/date_chooser.hpp
+++ b/include/nana/gui/widgets/date_chooser.hpp
@@ -45,7 +45,7 @@ namespace nana
void week_name(unsigned index, const nana::string&);
void month_name(unsigned index, const nana::string&);
private:
- where _m_pos_where(graph_reference, int x, int y);
+ where _m_pos_where(graph_reference, const ::nana::point& pos);
void _m_draw(graph_reference);
void _m_draw_topbar(graph_reference);
void _m_make_drawing_basis(drawing_basis&, graph_reference, const nana::point& refpos);
diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp
new file mode 100644
index 00000000..dcf0b982
--- /dev/null
+++ b/include/nana/gui/widgets/spinbox.hpp
@@ -0,0 +1,78 @@
+/*
+ * A Spin box widget
+ * Nana C++ Library(http://www.nanapro.org)
+ * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
+ *
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ *
+ * @file: nana/gui/widgets/spanbox.hpp
+ */
+
+#ifndef NANA_GUI_WIDGET_SPINBOX_HPP
+#define NANA_GUI_WIDGET_SPINBOX_HPP
+#include "widget.hpp"
+#include "skeletons/text_editor_scheme.hpp"
+
+namespace nana
+{
+ namespace drawerbase
+ {
+ namespace spinbox
+ {
+ /// Declaration of internal spinbox implementation
+ class implementation;
+
+ /// Drawer of spinbox
+ class drawer
+ : public ::nana::drawer_trigger
+ {
+ drawer(const drawer&) = delete;
+ drawer(drawer&&) = delete;
+ drawer& operator=(const drawer&) = delete;
+ drawer& operator=(drawer&&) = delete;
+ public:
+ drawer();
+ ~drawer();
+ implementation * impl() const;
+ private:
+ //Overrides drawer_trigger
+ void attached(widget_reference, graph_reference) override;
+ void refresh(graph_reference) override;
+
+ void focus(graph_reference, const arg_focus&) override;
+ void mouse_wheel(graph_reference, const arg_wheel&) override;
+ void mouse_down(graph_reference, const arg_mouse&) override;
+ void mouse_move(graph_reference, const arg_mouse&) override;
+ void mouse_up(graph_reference, const arg_mouse& arg) override;
+ void mouse_leave(graph_reference, const arg_mouse&) override;
+ private:
+ implementation * const impl_;
+ };
+ };
+ }//end namespace drawerbase
+
+ /// Spinbox Widget
+ class spinbox
+ : public widget_object
+ {
+ public:
+ spinbox();
+ spinbox(window, bool visible);
+ spinbox(window, const nana::rectangle& = {}, bool visible = true);
+
+ void range(int begin, int last, int step);
+ void range(double begin, double last, double step);
+ void range(std::initializer_list steps_utf8);
+ void range(std::initializer_list steps);
+
+ void qualify(std::wstring prefix, std::wstring suffix);
+ void qualify(const std::string & prefix_utf8, const std::string& suffix_utf8);
+ private:
+ ::nana::string _m_caption() const;
+ void _m_caption(::nana::string&&);
+ };
+}//end namespace nana
+
+#endif //NANA_GUI_WIDGET_SPINBOX_HPP
\ No newline at end of file
diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp
index a4f78b11..16545934 100644
--- a/source/gui/detail/basic_window.cpp
+++ b/source/gui/detail/basic_window.cpp
@@ -327,7 +327,7 @@ namespace nana
}
predef_cursor = cursor::arrow;
- flags.capture = false;
+ flags.captured = false;
flags.dbl_click = true;
flags.enabled = true;
flags.modal = false;
diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp
index 7fddd857..d3722ce2 100644
--- a/source/gui/detail/bedrock_pi.cpp
+++ b/source/gui/detail/bedrock_pi.cpp
@@ -63,7 +63,7 @@ namespace nana
}
wd_manager.refresh_tree(wd);
- wd_manager.map(wd);
+ wd_manager.map(wd, false);
}
}
diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp
index 6e5c4673..7270aa7a 100644
--- a/source/gui/detail/drawer.cpp
+++ b/source/gui/detail/drawer.cpp
@@ -242,7 +242,7 @@ namespace nana
_m_emit(event_code::shortkey, arg, &drawer_trigger::shortkey);
}
- void drawer::map(window wd) //Copy the root buffer to screen
+ void drawer::map(window wd, bool forced) //Copy the root buffer to screen
{
if(wd)
{
@@ -264,7 +264,7 @@ namespace nana
#endif
}
- if(false == edge_nimbus_renderer_t::instance().render(iwd))
+ if (false == edge_nimbus_renderer_t::instance().render(iwd, forced))
{
nana::rectangle vr;
if(bedrock_type::window_manager_t::wndlayout_type::read_visual_rectangle(iwd, vr))
diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp
index c9e1ae78..8b55a9b7 100644
--- a/source/gui/detail/linux_X11/bedrock.cpp
+++ b/source/gui/detail/linux_X11/bedrock.cpp
@@ -176,9 +176,9 @@ namespace detail
delete impl_;
}
- void bedrock::map_thread_root_buffer(bedrock::core_window_t* wnd)
+ void bedrock::map_thread_root_buffer(core_window_t*, bool forced)
{
- //GUI in X11 is not thread-dependent, so no implementation.
+ //GUI in X11 is thread-independent, so no implementation.
}
//inc_window
@@ -619,7 +619,8 @@ namespace detail
msgwnd = brock.wd_manager.find_window(native_window, xevent.xcrossing.x, xevent.xcrossing.y);
if(msgwnd)
{
- msgwnd->flags.action = mouse_action::over;
+ if (mouse_action::pressed != msgwnd->flags.action)
+ msgwnd->flags.action = mouse_action::over;
hovered_wd = msgwnd;
arg_mouse arg;
@@ -870,7 +871,8 @@ namespace detail
else
{
evt_code = event_code::mouse_enter;
- msgwnd->flags.action = mouse_action::over;
+ if (mouse_action::pressed != msgwnd->flags.action)
+ msgwnd->flags.action = mouse_action::over;
}
arg_mouse arg;
assign_arg(arg, msgwnd, message, xevent);
@@ -883,7 +885,10 @@ namespace detail
{
arg_mouse arg;
assign_arg(arg, msgwnd, message, xevent);
- msgwnd->flags.action = mouse_action::over;
+
+ if (mouse_action::pressed != msgwnd->flags.action)
+ msgwnd->flags.action = mouse_action::over;
+
if (hovered_wd != msgwnd)
{
hovered_wd = msgwnd;
diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp
index 74b8cb30..fd1eb903 100644
--- a/source/gui/detail/win32/bedrock.cpp
+++ b/source/gui/detail/win32/bedrock.cpp
@@ -342,9 +342,9 @@ namespace detail
return bedrock_object;
}
- void bedrock::map_thread_root_buffer(core_window_t* wd)
+ void bedrock::map_thread_root_buffer(core_window_t* wd, bool forced)
{
- ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), 0);
+ ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), static_cast(forced ? TRUE : FALSE));
}
void interior_helper_for_menu(MSG& msg, native_window_type menu_window)
@@ -596,7 +596,7 @@ namespace detail
}
return true;
case nana::detail::messages::map_thread_root_buffer:
- bedrock.wd_manager.map(reinterpret_cast(wParam));
+ bedrock.wd_manager.map(reinterpret_cast(wParam), (TRUE == lParam));
::UpdateWindow(wd);
return true;
case nana::detail::messages::remote_thread_move_window:
@@ -1060,7 +1060,10 @@ namespace detail
else
{
evt_code = event_code::mouse_enter;
- msgwnd->flags.action = mouse_action::over;
+ if (pressed_wd == msgwnd)
+ msgwnd->flags.action = mouse_action::pressed;
+ else if (mouse_action::pressed != msgwnd->flags.action)
+ msgwnd->flags.action = mouse_action::over;
}
arg_mouse arg;
assign_arg(arg, msgwnd, message, pmdec);
@@ -1073,9 +1076,14 @@ namespace detail
{
arg_mouse arg;
assign_arg(arg, msgwnd, message, pmdec);
- msgwnd->flags.action = mouse_action::over;
+
if (hovered_wd != msgwnd)
{
+ if (pressed_wd == msgwnd)
+ msgwnd->flags.action = mouse_action::pressed;
+ else if (mouse_action::pressed != msgwnd->flags.action)
+ msgwnd->flags.action = mouse_action::over;
+
hovered_wd = msgwnd;
arg.evt_code = event_code::mouse_enter;
brock.emit(event_code::mouse_enter, msgwnd, arg, true, &context);
@@ -1105,21 +1113,34 @@ namespace detail
auto scrolled_wd = brock.wd_manager.find_window(reinterpret_cast(pointer_wd), scr_pos.x, scr_pos.y);
def_window_proc = true;
- while (scrolled_wd)
+ auto evt_wd = scrolled_wd;
+ while (evt_wd)
{
- if (scrolled_wd->together.attached_events->mouse_wheel.length() != 0)
+ if (evt_wd->together.attached_events->mouse_wheel.length() != 0)
{
def_window_proc = false;
nana::point mspos{ scr_pos.x, scr_pos.y };
- brock.wd_manager.calc_window_point(scrolled_wd, mspos);
+ brock.wd_manager.calc_window_point(evt_wd, mspos);
arg_wheel arg;
arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical);
- assign_arg(arg, scrolled_wd, pmdec);
- brock.emit(event_code::mouse_wheel, scrolled_wd, arg, true, &context);
+ assign_arg(arg, evt_wd, pmdec);
+ brock.emit(event_code::mouse_wheel, evt_wd, arg, true, &context);
break;
}
- scrolled_wd = scrolled_wd->parent;
+ evt_wd = evt_wd->parent;
+ }
+
+ if (scrolled_wd && (nullptr == evt_wd))
+ {
+ nana::point mspos{ scr_pos.x, scr_pos.y };
+ brock.wd_manager.calc_window_point(scrolled_wd, mspos);
+
+ arg_wheel arg;
+ arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical);
+ assign_arg(arg, scrolled_wd, pmdec);
+ brock.emit_drawer(event_code::mouse_wheel, scrolled_wd, arg, &context);
+ brock.wd_manager.do_lazy_refresh(scrolled_wd, false);
}
}
else
diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp
index 990f1090..008c0dc6 100644
--- a/source/gui/detail/window_manager.cpp
+++ b/source/gui/detail/window_manager.cpp
@@ -671,7 +671,7 @@ namespace detail
}
//Copy the root buffer that wnd specified into DeviceContext
- void window_manager::map(core_window_t* wd)
+ void window_manager::map(core_window_t* wd, bool forced)
{
//Thread-Safe Required!
std::lock_guard lock(mutex_);
@@ -682,9 +682,9 @@ namespace detail
wd->drawer.map(reinterpret_cast(wd));
#elif defined(NANA_WINDOWS)
if(nana::system::this_thread_id() == wd->thread_id)
- wd->drawer.map(reinterpret_cast(wd));
+ wd->drawer.map(reinterpret_cast(wd), forced);
else
- bedrock::instance().map_thread_root_buffer(wd);
+ bedrock::instance().map_thread_root_buffer(wd, forced);
#endif
}
}
@@ -693,7 +693,7 @@ namespace detail
//@brief: update is used for displaying the screen-off buffer.
// Because of a good efficiency, if it is called in an event procedure and the event procedure window is the
// same as update's, update would not map the screen-off buffer and just set the window for lazy refresh
- bool window_manager::update(core_window_t* wd, bool redraw, bool force)
+ bool window_manager::update(core_window_t* wd, bool redraw, bool forced)
{
//Thread-Safe Required!
std::lock_guard lock(mutex_);
@@ -701,10 +701,10 @@ namespace detail
if (wd->visible && wd->visible_parents())
{
- if(force || (false == wd->belong_to_lazy()))
+ if(forced || (false == wd->belong_to_lazy()))
{
wndlayout_type::paint(wd, redraw, false);
- this->map(wd);
+ this->map(wd, forced);
}
else
{
@@ -746,13 +746,13 @@ namespace detail
if ((wd->other.upd_state == core_window_t::update_state::refresh) || force_copy_to_screen)
{
wndlayout_type::paint(wd, false, false);
- this->map(wd);
+ this->map(wd, force_copy_to_screen);
}
else if (effects::edge_nimbus::none != wd->effect.edge_nimbus)
{
//Update the nimbus effect
using nimbus_renderer = detail::edge_nimbus_renderer;
- nimbus_renderer::instance().render(wd);
+ nimbus_renderer::instance().render(wd, force_copy_to_screen);
}
}
else
@@ -935,7 +935,7 @@ namespace detail
if (impl_->wd_register.available(wd))
{
- wd->flags.capture = true;
+ wd->flags.captured = true;
native_interface::capture_window(wd->root, value);
auto prev = attr_.capture.window;
if(prev && (prev != wd))
@@ -953,6 +953,7 @@ namespace detail
else if(wd == attr_.capture.window)
{
attr_.capture.window = nullptr;
+ wd->flags.captured = false;
if(attr_cap.size())
{
std::pair last = attr_cap.back();
@@ -964,6 +965,7 @@ namespace detail
attr_.capture.ignore_children = last.second;
native_interface::capture_window(last.first->root, true);
native_interface::calc_window_point(last.first->root, pos);
+ last.first->flags.captured = true;
attr_.capture.inside = _m_effective(last.first, pos);
}
}
diff --git a/source/gui/element.cpp b/source/gui/element.cpp
index 6535f01d..5b6cf192 100644
--- a/source/gui/element.cpp
+++ b/source/gui/element.cpp
@@ -476,7 +476,57 @@ namespace nana
graph.set_pixel(x, y + 5);
}
}
- };
+ };//end class arrow_double
+
+ class annex_button
+ : public element_interface
+ {
+ bool draw(graph_reference graph, const ::nana::color& arg_bgcolor, const ::nana::color& fgcolor, const rectangle& r, element_state estate) override
+ {
+ auto bgcolor = arg_bgcolor;
+
+ switch (estate)
+ {
+ case element_state::hovered:
+ case element_state::focus_hovered:
+ bgcolor = arg_bgcolor.blend(colors::white, 0.8);
+ break;
+ case element_state::pressed:
+ bgcolor = arg_bgcolor.blend(colors::black, 0.8);
+ break;
+ default:
+ break;
+ }
+
+ auto part_px = (r.height - 3) * 5 / 13;
+ graph.rectangle(r, false, bgcolor.blend(colors::black, 0.6));
+
+ ::nana::point left_top{ r.x + 1, r.y + 1 }, right_top{r.right() - 2, r.y + 1};
+ ::nana::point left_mid{ r.x + 1, r.y + 1 + static_cast(part_px) }, right_mid{ right_top.x, left_mid.y };
+ ::nana::point left_bottom{ r.x + 1, r.bottom() - 2 }, right_bottom{ r.right() - 2, r.bottom() - 2 };
+
+ graph.set_color(bgcolor.blend(colors::white, 0.9));
+ graph.line(left_top, left_mid);
+ graph.line(right_top, right_mid);
+
+ graph.set_color(bgcolor.blend(colors::white, 0.5));
+ graph.line(left_top, right_top);
+
+ left_mid.y++;
+ right_mid.y++;
+ graph.set_color(bgcolor.blend(colors::black, 0.8));
+ graph.line(left_mid, left_bottom);
+ graph.line(right_mid, right_bottom);
+
+ ::nana::rectangle part_r{ r.x + 2, r.y + 2, r.width - 4, part_px };
+ graph.rectangle(part_r, true, bgcolor.blend(colors::white, 0.8));
+
+ part_r.y += static_cast(part_r.height);
+ part_r.height = (r.height - 3 - part_r.height);
+ graph.rectangle(part_r, true, bgcolor);
+ return true;
+ }
+ };//end class annex_button
}//end namespace element
template
@@ -558,10 +608,12 @@ namespace nana
element::add_border("");
- element::add_arrow("");
+ element::add_arrow(""); //"arrowhead" in default
element::add_arrow("double");
element::add_arrow("solid_triangle");
element::add_arrow("hollow_triangle");
+
+ element::add_button(""); //"annex" in default
}
return obj;
}
@@ -595,6 +647,16 @@ namespace nana
{
return _m_get((name.empty() ? "arrowhead" : name), arrow_).keeper();
}
+
+ void button(const std::string& name, const pat::cloneable>& factory)
+ {
+ _m_add((name.empty() ? "annex" : name), button_, factory);
+ }
+
+ element::element_interface * const * button(const std::string& name) const
+ {
+ return _m_get((name.empty() ? "annex" : name), button_).keeper();
+ }
private:
using lock_guard = std::lock_guard;
@@ -630,6 +692,7 @@ namespace nana
item crook_;
item border_;
item arrow_;
+ item button_;
};
namespace element
@@ -664,6 +727,16 @@ namespace nana
{
return element_manager::instance().arrow(name);
}
+
+ void provider::add_button(const std::string& name, const pat::cloneable>& factory)
+ {
+ element_manager::instance().button(name, factory);
+ }
+
+ element_interface* const* provider::keeper_button(const std::string& name)
+ {
+ return element_manager::instance().button(name);
+ }
}//end namespace element
//facades
@@ -705,7 +778,7 @@ namespace nana
void facade::switch_to(const char* name)
{
- keeper_ = element::provider().keeper_crook(name);
+ keeper_ = element::provider().keeper_crook(name ? name : "");
}
bool facade::draw(graph_reference graph, const ::nana::color& bgcol, const ::nana::color& fgcol, const nana::rectangle& r, element_state es)
@@ -721,7 +794,7 @@ namespace nana
void facade::switch_to(const char* name)
{
- keeper_ = element::provider().keeper_border(name);
+ keeper_ = element::provider().keeper_border(name ? name : "");
}
bool facade::draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state es)
@@ -754,6 +827,23 @@ namespace nana
}
//end class facade
+ //class facade::
+ facade::facade(const char* name)
+ : keeper_(element::provider().keeper_button(name ? name : ""))
+ {}
+
+ void facade::switch_to(const char* name)
+ {
+ keeper_ = element::provider().keeper_button(name ? name : "");
+ }
+
+ //Implement element_interface
+ bool facade::draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate)
+ {
+ return (*keeper_)->draw(graph, bgcolor, fgcolor, r, estate);
+ }
+ //end class facade
+
namespace element
{
void set_bground(const char* name, const pat::cloneable& obj)
diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp
index 6dbd0101..5ab4e8d6 100644
--- a/source/gui/widgets/combox.cpp
+++ b/source/gui/widgets/combox.cpp
@@ -58,16 +58,15 @@ namespace nana
class drawer_impl
{
public:
- typedef nana::paint::graphics & graph_reference;
- typedef widget & widget_reference;
+ using graph_reference = paint::graphics&;
+ using widget_reference = widget&;
enum class where_t{unknown, text, push_button};
- enum class state_t{none, mouse_over, pressed};
drawer_impl()
{
state_.focused = false;
- state_.state = state_t::none;
+ state_.button_state = element_state::normal;
state_.pointer_where = where_t::unknown;
state_.lister = nullptr;
}
@@ -195,13 +194,13 @@ namespace nana
void set_mouse_over(bool mo)
{
- state_.state = mo ? state_t::mouse_over : state_t::none;
+ state_.button_state = (mo ? element_state::hovered : element_state::normal);
state_.pointer_where = where_t::unknown;
}
void set_mouse_press(bool mp)
{
- state_.state = (mp ? state_t::pressed : state_t::mouse_over);
+ state_.button_state = (mp ? element_state::pressed : element_state::hovered);
}
void set_focused(bool f)
@@ -310,8 +309,8 @@ namespace nana
{
auto pos = API::cursor_position();
API::calc_window_point(widget_->handle(), pos);
- if(calc_where(*graph_, pos.x, pos.y))
- state_.state = state_t::none;
+ if (calc_where(*graph_, pos.x, pos.y))
+ state_.button_state = element_state::normal;
editor_->text(items_[index]->item_text);
_m_draw_push_button(widget_->enabled());
@@ -437,7 +436,8 @@ namespace nana
::nana::rectangle r(graph.size());
auto clr_from = colors::button_face_shadow_start;
auto clr_to = colors::button_face_shadow_end;
- if(state_.state == state_t::pressed)
+
+ if (element_state::pressed == state_.button_state)
{
r.pare_off(2);
std::swap(clr_from, clr_to);
@@ -450,54 +450,30 @@ namespace nana
void _m_draw_push_button(bool enabled)
{
- using namespace nana::paint;
+ ::nana::rectangle r{graph_->size()};
+ r.x = r.right() - 16;
+ r.y = 1;
+ r.width = 16;
+ r.height -= 2;
- if (nullptr == graph_) return;
-
- int left = graph_->width() - 17;
- int right = left + 16;
- int top = 1;
- int bottom = graph_->height() - 2;
- int mid = top + (bottom - top) * 5 / 18;
-
- ::nana::color topcol, topcol_ln, botcol, botcol_ln;
- ::nana::color arrow_color{ colors::white };
- if (enabled && items_.size())
+ auto estate = state_.button_state;
+ if (enabled && !items_.empty())
{
- double percent = 1;
- if (has_lister() || (state_.state == state_t::pressed && state_.pointer_where == where_t::push_button))
- percent = 0.8;
- else if (state_.state == state_t::mouse_over)
- percent = 0.9;
-
- topcol_ln = color{ 0x3F, 0x47, 0x6C }.blend(arrow_color, percent);
- botcol_ln = color{ 0x03, 0x31, 0x114 }.blend(arrow_color, percent);
- topcol = color{ 0x3F, 83, 84 }.blend(arrow_color, percent);
- botcol = color{ 0x0c, 0x4a, 0x9a }.blend(arrow_color, percent);
+ if (has_lister() || (element_state::pressed == estate && state_.pointer_where == where_t::push_button))
+ estate = element_state::pressed;
}
else
- {
- topcol_ln = { 0x7F, 0x7F, 0x7F };
- botcol_ln = { 0x50, 0x50, 0x50 };
- topcol = { 0xC3, 0xC3, 0xC3 };
- botcol = { 0xA0, 0xA0, 0xA0 };
- }
+ estate = element_state::disabled;
- graph_->set_color(topcol_ln);
- graph_->line({ left, top }, { left, mid });
- graph_->line({ right - 1, top }, { right - 1, mid });
-
- graph_->set_color(botcol_ln);
- graph_->line({ left, mid + 1 }, { left, bottom });
- graph_->line({ right - 1, mid + 1 }, { right - 1, bottom });
-
-
- graph_->rectangle({ left + 1, top, static_cast(right - left - 2), static_cast(mid - top + 1) }, true, topcol);
- graph_->rectangle({ left + 1, mid + 1, static_cast(right - left - 2), static_cast(bottom - mid) }, true, botcol);
+ facade button;
+ button.draw(*graph_, ::nana::color{ 3, 65, 140 }, colors::white, r, estate);
facade arrow("solid_triangle");
arrow.direction(::nana::direction::south);
- arrow.draw(*graph_, {}, arrow_color, { left, top + (bottom - top) / 2 - 7, 16, 16 }, element_state::normal);
+
+ r.y += (r.height / 2) - 7;
+ r.width = r.height = 16;
+ arrow.draw(*graph_, {}, colors::white, r, element_state::normal);
}
void _m_draw_image()
@@ -558,7 +534,7 @@ namespace nana
struct state_type
{
bool focused;
- state_t state;
+ element_state button_state;
where_t pointer_where;
nana::float_listbox * lister;
diff --git a/source/gui/widgets/date_chooser.cpp b/source/gui/widgets/date_chooser.cpp
index 031ec41b..0d25bccc 100644
--- a/source/gui/widgets/date_chooser.cpp
+++ b/source/gui/widgets/date_chooser.cpp
@@ -66,25 +66,24 @@ namespace nana
this->monthstr_[index] = str;
}
- trigger::where trigger::_m_pos_where(graph_reference graph, int x, int y)
+ trigger::where trigger::_m_pos_where(graph_reference graph, const ::nana::point& pos)
{
int xend = static_cast(graph.width()) - 1;
int yend = static_cast(graph.height()) - 1;
- if(0 < y && y < static_cast(topbar_height))
+ if(0 < pos.y && pos.y < static_cast(topbar_height))
{
- if(static_cast(border_size) < x && x < xend)
+ if(static_cast(border_size) < pos.x && pos.x < xend)
{
- if(x < border_size + 16)
+ if(pos.x < border_size + 16)
return where::left_button;
- else if(xend - border_size - 16 < x)
+ else if(xend - border_size - 16 < pos.x)
return where::right_button;
return where::topbar;
}
}
- else if(topbar_height < y && y < yend)
+ else if(topbar_height < pos.y && pos.y < yend)
{
- trace_pos_.x = x;
- trace_pos_.y = y;
+ trace_pos_ = pos;
return where::textarea;
}
return where::none;
@@ -476,7 +475,7 @@ namespace nana
void trigger::mouse_move(graph_reference graph, const arg_mouse& arg)
{
- where pos = _m_pos_where(graph, arg.pos.x, arg.pos.y);
+ where pos = _m_pos_where(graph, arg.pos);
if(pos == pos_ && pos_ != where::textarea) return;
pos_ = pos;
_m_draw(graph);
@@ -494,7 +493,7 @@ namespace nana
void trigger::mouse_up(graph_reference graph, const arg_mouse& arg)
{
bool redraw = true;
- where pos = _m_pos_where(graph, arg.pos.x, arg.pos.y);
+ where pos = _m_pos_where(graph, arg.pos);
transform_action tfid = transform_action::none;
if(pos == where::topbar)
diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp
new file mode 100644
index 00000000..c34e88d6
--- /dev/null
+++ b/source/gui/widgets/spinbox.cpp
@@ -0,0 +1,483 @@
+/*
+ * A Spin box widget
+ * Nana C++ Library(http://www.nanapro.org)
+ * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
+ *
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ *
+ * @file: nana/gui/widgets/spanbox.cpp
+ */
+
+#include
+#include
+#include
+#include
+
+namespace nana
+{
+ namespace drawerbase
+ {
+ namespace spinbox
+ {
+ enum class buttons
+ {
+ none, increase, decrease
+ };
+
+ class range_interface
+ {
+ public:
+ virtual ~range_interface() = default;
+
+ virtual std::wstring value() const = 0;
+ virtual void spin(bool increase) = 0;
+ };
+
+ template
+ class range_numeric
+ : public range_interface
+ {
+ public:
+ range_numeric(T vbegin, T vlast, T step)
+ : begin_{ vbegin }, last_{ vlast }, step_{ step }, value_{ vbegin }
+ {}
+
+ std::wstring value() const override
+ {
+ std::wstringstream ss;
+ ss << value_;
+ return ss.str();
+ }
+
+ void spin(bool increase) override
+ {
+ if (increase)
+ {
+ value_ += step_;
+ if (value_ > last_)
+ value_ = last_;
+ }
+ else
+ {
+ value_ -= step_;
+ if (value_ < begin_)
+ value_ = begin_;
+ }
+ }
+ private:
+ T begin_;
+ T last_;
+ T step_;
+ T value_;
+ };
+
+ class range_text
+ : public range_interface
+ {
+ public:
+ range_text(std::initializer_list & initlist)
+ {
+ for (auto & s : initlist)
+ {
+ texts_.emplace_back(::nana::charset(s, ::nana::unicode::utf8));
+ }
+ }
+
+ range_text(std::initializer_list& initlist)
+ : texts_(initlist)
+ {}
+
+ std::wstring value() const override
+ {
+ if (texts_.empty())
+ return{};
+
+ return texts_[pos_];
+ }
+
+ void spin(bool increase) override
+ {
+ if (texts_.empty())
+ return;
+
+ if (increase)
+ {
+ ++pos_;
+ if (texts_.size() <= pos_)
+ pos_ = texts_.size() - 1;
+ }
+ else
+ {
+ --pos_;
+ if (texts_.size() <= pos_)
+ pos_ = 0;
+ }
+ }
+ private:
+ std::vector texts_;
+ std::size_t pos_{0};
+ };
+
+ class implementation
+ {
+ public:
+ implementation()
+ {
+ //Sets a timer for continous spin when mouse button is pressed.
+ timer_.elapse([this]
+ {
+ range_->spin(buttons::increase == spin_stated_);
+ _m_text();
+ API::update_window(editor_->window_handle());
+
+ auto intv = timer_.interval();
+ if (intv > 50)
+ timer_.interval(intv / 2);
+ });
+
+ timer_.interval(1000);
+ }
+
+ void attach(::nana::widget& wdg, ::nana::paint::graphics& graph)
+ {
+ auto wd = wdg.handle();
+ graph_ = &graph;
+ auto scheme = static_cast<::nana::widgets::skeletons::text_editor_scheme*>(API::dev::get_scheme(wd));
+ editor_ = new ::nana::widgets::skeletons::text_editor(wd, graph, scheme);
+ editor_->multi_lines(false);
+
+ if (!range_)
+ range_.reset(new range_numeric(0, 100, 1));
+
+ _m_text();
+
+ API::tabstop(wd);
+ API::eat_tabstop(wd, true);
+ API::effects_edge_nimbus(wd, effects::edge_nimbus::active);
+ API::effects_edge_nimbus(wd, effects::edge_nimbus::over);
+ _m_reset_text_area();
+ }
+
+ void detach()
+ {
+ delete editor_;
+ editor_ = nullptr;
+ }
+
+ void set_range(std::unique_ptr ptr)
+ {
+ range_.swap(ptr);
+
+ _m_text();
+ }
+
+ void qualify(std::wstring&& prefix, std::wstring&& suffix)
+ {
+ surround_.prefix = std::move(prefix);
+ surround_.suffix = std::move(suffix);
+
+ if (editor_)
+ {
+ _m_text();
+ API::update_window(editor_->window_handle());
+ }
+ }
+
+ void render()
+ {
+ editor_->render(API::is_focus_window(editor_->window_handle()));
+ _m_draw_spins(spin_stated_);
+ }
+
+ ::nana::widgets::skeletons::text_editor* editor() const
+ {
+ return editor_;
+ }
+
+ void mouse_wheel(bool upwards)
+ {
+ range_->spin(!upwards);
+ _m_text();
+ }
+
+ bool mouse_button(const ::nana::arg_mouse& arg, bool pressed)
+ {
+ if (!pressed)
+ {
+ API::capture_window(editor_->window_handle(), false);
+ timer_.stop();
+ timer_.interval(1000);
+ }
+
+ if (buttons::none != spin_stated_)
+ {
+ //Spins the value when mouse button is released
+ if (pressed)
+ {
+ API::capture_window(editor_->window_handle(), true);
+ range_->spin(buttons::increase == spin_stated_);
+ _m_text();
+ timer_.start();
+ }
+ _m_draw_spins(spin_stated_);
+ return true;
+ }
+
+
+ bool refreshed = false;
+ if (pressed)
+ refreshed = editor_->mouse_down(arg.left_button, arg.pos);
+ else
+ refreshed = editor_->mouse_up(arg.left_button, arg.pos);
+
+ if (refreshed)
+ _m_draw_spins(buttons::none);
+
+ return refreshed;
+ }
+
+ bool mouse_move(bool left_button, const ::nana::point& pos)
+ {
+ if (editor_->mouse_move(left_button, pos))
+ {
+ editor_->reset_caret();
+ render();
+ return true;
+ }
+
+ auto btn = _m_where(pos);
+ if (buttons::none != btn)
+ {
+ spin_stated_ = btn;
+ _m_draw_spins(btn);
+ return true;
+ }
+ else if (buttons::none != spin_stated_)
+ {
+ spin_stated_ = buttons::none;
+ _m_draw_spins(buttons::none);
+ return true;
+ }
+
+ return false;
+ }
+ private:
+ void _m_text()
+ {
+ if (editor_)
+ {
+ std::wstring text = surround_.prefix + range_->value() + surround_.suffix;
+ editor_->text(std::move(text));
+ _m_draw_spins(spin_stated_);
+ }
+ }
+
+ void _m_reset_text_area()
+ {
+ auto spins_r = _m_spins_area();
+ if (spins_r.x == 0)
+ editor_->text_area({});
+ else
+ editor_->text_area({ 2, 2, graph_->width() - spins_r.width - 2, spins_r.height - 2 });
+ }
+
+ ::nana::rectangle _m_spins_area() const
+ {
+ auto size = API::window_size(editor_->window_handle());
+ if (size.width > 18)
+ return{ static_cast(size.width - 16), 0, 16, size.height };
+
+ return{ 0, 0, size.width, size.height };
+ }
+
+ buttons _m_where(const ::nana::point& pos) const
+ {
+ auto spins_r = _m_spins_area();
+ if (spins_r.is_hit(pos))
+ {
+ if (pos.y < spins_r.y + static_cast(spins_r.height / 2))
+ return buttons::increase;
+
+ return buttons::decrease;
+ }
+ return buttons::none;
+ }
+
+ void _m_draw_spins(buttons spins)
+ {
+ auto estate = API::element_state(editor_->window_handle());
+
+ auto spin_r0 = _m_spins_area();
+ spin_r0.height /= 2;
+
+ auto spin_r1 = spin_r0;
+ spin_r1.y += static_cast(spin_r0.height);
+ spin_r1.height = _m_spins_area().height - spin_r0.height;
+
+ ::nana::color bgcolor{ 3, 65, 140 };
+ facade arrow;
+ facade button;
+
+ auto spin_state = (buttons::increase == spins ? estate : element_state::normal);
+ button.draw(*graph_, bgcolor, colors::white, spin_r0, spin_state);
+ spin_r0.x += 5;
+ arrow.draw(*graph_, bgcolor, colors::white, spin_r0, spin_state);
+
+ spin_state = (buttons::decrease == spins ? estate : element_state::normal);
+ button.draw(*graph_, bgcolor, colors::white, spin_r1, spin_state);
+ spin_r1.x += 5;
+ arrow.direction(direction::south);
+ arrow.draw(*graph_, bgcolor, colors::white, spin_r1, spin_state);
+ }
+ private:
+ ::nana::paint::graphics * graph_{nullptr};
+ ::nana::widgets::skeletons::text_editor * editor_{nullptr};
+ buttons spin_stated_{ buttons::none };
+ std::unique_ptr range_;
+ ::nana::timer timer_;
+
+ struct surround_data
+ {
+ std::wstring prefix;
+ std::wstring suffix;
+ }surround_;
+ };
+
+ //class drawer
+ drawer::drawer()
+ : impl_(new implementation)
+ {}
+
+ drawer::~drawer()
+ {
+ delete impl_;
+ }
+
+ implementation* drawer::impl() const
+ {
+ return impl_;
+ }
+
+ //Overrides drawer_trigger
+ void drawer::attached(widget_reference wdg, graph_reference graph)
+ {
+ impl_->attach(wdg, graph);
+ }
+
+ void drawer::refresh(graph_reference)
+ {
+ impl_->render();
+ }
+
+ void drawer::focus(graph_reference, const arg_focus&)
+ {
+ impl_->render();
+ impl_->editor()->reset_caret();
+ API::lazy_refresh();
+ }
+
+ void drawer::mouse_wheel(graph_reference, const arg_wheel& arg)
+ {
+ impl_->mouse_wheel(arg.upwards);
+ impl_->editor()->reset_caret();
+ API::lazy_refresh();
+ }
+
+ void drawer::mouse_down(graph_reference, const arg_mouse& arg)
+ {
+ if (impl_->mouse_button(arg, true))
+ API::lazy_refresh();
+ }
+
+ void drawer::mouse_up(graph_reference, const arg_mouse& arg)
+ {
+ if (impl_->mouse_button(arg, false))
+ API::lazy_refresh();
+ }
+
+ void drawer::mouse_move(graph_reference, const arg_mouse& arg)
+ {
+ if (impl_->mouse_move(arg.left_button, arg.pos))
+ API::lazy_refresh();
+ }
+
+ void drawer::mouse_leave(graph_reference, const arg_mouse&)
+ {
+ impl_->render();
+ API::lazy_refresh();
+ }
+
+ }
+ }//end namespace drawerbase
+
+ spinbox::spinbox()
+ {}
+
+ spinbox::spinbox(window wd, bool visible)
+ {
+ this->create(wd, visible);
+ }
+
+ spinbox::spinbox(window wd, const nana::rectangle& r, bool visible)
+ {
+ this->create(wd, r, visible);
+ }
+
+ void spinbox::range(int begin, int last, int step)
+ {
+ using namespace drawerbase::spinbox;
+ get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_numeric(begin, last, step)));
+ API::refresh_window(handle());
+ }
+
+ void spinbox::range(double begin, double last, double step)
+ {
+ using namespace drawerbase::spinbox;
+ get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_numeric(begin, last, step)));
+ API::refresh_window(handle());
+ }
+
+ void spinbox::range(std::initializer_list steps_utf8)
+ {
+ using namespace drawerbase::spinbox;
+ get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_text(steps_utf8)));
+ API::refresh_window(handle());
+ }
+
+ void spinbox::range(std::initializer_list steps)
+ {
+ using namespace drawerbase::spinbox;
+ get_drawer_trigger().impl()->set_range(std::unique_ptr(new range_text(steps)));
+ API::refresh_window(handle());
+ }
+
+ void spinbox::qualify(std::wstring prefix, std::wstring suffix)
+ {
+ get_drawer_trigger().impl()->qualify(std::move(prefix), std::move(suffix));
+ }
+
+ void spinbox::qualify(const std::string & prefix_utf8, const std::string& suffix_utf8)
+ {
+ qualify(static_cast(::nana::charset(prefix_utf8, ::nana::unicode::utf8)), static_cast(::nana::charset(suffix_utf8, ::nana::unicode::utf8)));
+ }
+
+ ::nana::string spinbox::_m_caption() const
+ {
+ internal_scope_guard lock;
+ auto editor = get_drawer_trigger().impl()->editor();
+ return (editor ? editor->text() : nana::string());
+ }
+
+ void spinbox::_m_caption(::nana::string&& text)
+ {
+ internal_scope_guard lock;
+ auto editor = get_drawer_trigger().impl()->editor();
+ if (editor)
+ {
+ editor->text(std::move(text));
+ API::refresh_window(*this);
+ }
+ }
+}//end namespace nana
\ No newline at end of file