Merge branch 'feature-keyboard-accelerator' into develop

This commit is contained in:
Jinhao 2018-03-08 15:35:30 +08:00
commit 90b5f17169
27 changed files with 489 additions and 141 deletions

View File

@ -131,7 +131,7 @@ endif(APPLE)
if(UNIX)
list(APPEND NANA_LINKS -lX11)
find_package(Freetype)
include(FindFreetype)
if (FREETYPE_FOUND)
include_directories( ${FREETYPE_INCLUDE_DIRS})
list(APPEND NANA_LINKS -lXft)

View File

@ -1,7 +1,7 @@
/**
* Predefined Symbols for C++
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2016-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2016-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -171,9 +171,9 @@
#endif
#endif
//Assume the std::thread is not implement on MinGW
//Assume the std::thread is not implemented on MinGW,
//unless it was compiled with POSIX threading support.
//But some toolchains may implement std::thread.
// it seems that MinGW 6.3 and 7.1 have std::thread
#ifdef NANA_MINGW
# ifndef STD_THREAD_NOT_SUPPORTED
# define STD_THREAD_NOT_SUPPORTED
@ -221,8 +221,11 @@
# if __has_include(<filesystem>)
# undef STD_FILESYSTEM_NOT_SUPPORTED
# endif
# if __has_include(<mutex>)
# undef STD_THREAD_NOT_SUPPORTED
# if __has_include(<mutex>)
# if !(defined(NANA_MINGW) && !defined(_GLIBCXX_HAS_GTHREADS))
//See the comment above regarding MinGW's threading support
# undef STD_THREAD_NOT_SUPPORTED
# endif
# endif
#endif

View File

@ -1,7 +1,7 @@
/**
* A ISO C++ filesystem Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -72,13 +72,12 @@ namespace boost
// begin() and end() are only used by a range-based for statement in the context of
// auto - thus the top-level const is stripped - so returning const is harmless and
// emphasizes begin() is just a pass through.
inline
const directory_iterator& begin(const directory_iterator& iter) BOOST_NOEXCEPT
inline const directory_iterator& begin(const directory_iterator& iter) BOOST_NOEXCEPT
{
return iter;
}
inline
directory_iterator end(const directory_iterator&) BOOST_NOEXCEPT
inline directory_iterator end(const directory_iterator&) BOOST_NOEXCEPT
{
return directory_iterator();
}

View File

@ -1,6 +1,6 @@
/**
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -18,7 +18,6 @@
#include <nana/filesystem/filesystem.hpp>
#include <nana/deploy.hpp>
namespace nana
{
namespace filesystem_ext

View File

@ -4,7 +4,7 @@
*
* Basis Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -30,6 +30,15 @@ namespace nana
struct native_drawable_impl{};
}
struct accel_key
{
char key;
bool case_sensitive{ false };
bool alt{ false };
bool ctrl{ false };
bool shift{ false };
};
enum class checkstate
{
unchecked, checked, partial

View File

@ -1,7 +1,7 @@
/**
* A Bedrock Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -27,6 +27,7 @@ namespace detail
struct basic_window;
class window_manager;
struct window_platform_assoc;
/// @brief fundamental core component, it provides an abstraction to the OS platform and some basic functions.
class bedrock
@ -73,6 +74,11 @@ namespace detail
//Closes the windows which are associated with the specified thread. If the given thread_id is 0, it closes all windows
void close_thread_window(unsigned thread_id);
public:
//Platform-dependent functions
static void delete_platform_assoc(window_platform_assoc*);
void keyboard_accelerator(native_window_type, const accel_key&, const std::function<void()>&);
public:
void event_expose(core_window_t *, bool exposed);
void event_move(core_window_t*, int x, int y);

View File

@ -1,3 +1,15 @@
/*
* Effects Renderer
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2018 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/detail/effects_renderer.cpp
*/
#ifndef NANA_GUI_DETAIL_EFFECTS_RENDERER_HPP
#define NANA_GUI_DETAIL_EFFECTS_RENDERER_HPP
#include <nana/gui/effects.hpp>
@ -76,7 +88,7 @@ namespace nana{
nana::rectangle r;
for(auto & action : nimbus)
{
if(_m_edge_nimbus(focused, action.window) && window_layer::read_visual_rectangle(action.window, r))
if(_m_edge_nimbus(action.window, focused) && window_layer::read_visual_rectangle(action.window, r))
{
if (action.window == wd)
{
@ -140,12 +152,17 @@ namespace nana{
}
}
private:
static bool _m_edge_nimbus(core_window_t * focused_wd, core_window_t * wd)
/// Determines whether the effect will be rendered for the given window.
static bool _m_edge_nimbus(core_window_t * const wd, core_window_t * const focused_wd)
{
if((focused_wd == wd) && (static_cast<unsigned>(wd->effect.edge_nimbus) & static_cast<unsigned>(effects::edge_nimbus::active)))
return true;
else if((static_cast<unsigned>(wd->effect.edge_nimbus) & static_cast<unsigned>(effects::edge_nimbus::over)) && (wd->flags.action == mouse_action::hovered))
return true;
// Don't render the effect if the window is disabled.
if (wd->flags.enabled)
{
if ((focused_wd == wd) && (static_cast<unsigned>(wd->effect.edge_nimbus) & static_cast<unsigned>(effects::edge_nimbus::active)))
return true;
else if ((static_cast<unsigned>(wd->effect.edge_nimbus) & static_cast<unsigned>(effects::edge_nimbus::over)) && (wd->flags.action == mouse_action::hovered))
return true;
}
return false;
}

View File

@ -1,7 +1,7 @@
/**
* Definition of General Events
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -460,8 +460,9 @@ namespace nana
::nana::window window_handle; ///< A handle to the event window
mutable wchar_t key; ///< the key corresponding to the key pressed
mutable bool ignore; ///< this member is only available for key_char event, set 'true' to ignore the input.
bool ctrl; ///< keyboard Ctrl is pressed?
bool shift; ///< keyboard Shift is pressed
bool alt; ///< it is set to indicate the modifier key Alt just prior to the event.
bool ctrl; ///< it is set to indicate the modifier key Ctrl just prior to the event.
bool shift; ///< it is set to indicate the modifier key Shift just prior to the event.
};
struct arg_move : public event_arg

View File

@ -301,6 +301,8 @@ namespace API
void window_size(window, const size&);
size window_outline_size(window);
void window_outline_size(window, const size&);
nana::optional<rectangle> window_rectangle(window);
bool get_window_rectangle(window, rectangle&);
bool track_window_size(window, const size&, bool true_for_max); ///< Sets the minimum or maximum tracking size of a window.
void window_enabled(window, bool);

View File

@ -1,7 +1,7 @@
/**
* A Form Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -68,6 +68,8 @@ namespace nana
void modality() const;
void wait_for_this();
void keyboard_accelerator(const accel_key&, const std::function<void()>& fn);
};
class nested_form

View File

@ -1,7 +1,7 @@
/**
* A List Box Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at

View File

@ -1,7 +1,7 @@
/**
* A Scroll Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -147,7 +147,7 @@ namespace nana
void step(size_type s)
{
metrics_.step = s;
metrics_.step = (s ? s : 1);
}
bool make_step(bool forward, unsigned multiple)
@ -398,7 +398,7 @@ namespace nana
/// \brief The construct that creates a widget.
/// @param wd A handle to the parent window of the widget being created.
/// @param visible specify the visibility after creation.
scroll(window wd, bool visible)
scroll(window wd, bool visible = true)
{
this->create(wd, rectangle(), visible); // add a widget scheme? and take some colors from these wd?
}
@ -501,7 +501,8 @@ namespace nana
/// @return true if the vlaue is changed.
bool make_page_scroll(bool forward)
{
return this->make_step(forward, static_cast<unsigned>(range() - 1));
auto const count = range() / step();
return this->make_step(forward, (count > 2 ? count - 1 : 1));
}
};//end class scroll
}//end namespace nana

View File

@ -1,7 +1,7 @@
/**
* A Tabbar implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -42,9 +42,8 @@ namespace nana
: arg_tabbar<T>({wdg, v})
{}
bool remove = true; ///< determines whether to remove the item
bool close_attach_window = true; ///< determines whether to close the attached window. It is ignored if remove is false
mutable bool remove = true; ///< determines whether to remove the item
mutable bool close_attach_window = true; ///< determines whether to close the attached window. It is ignored if remove is false
};
namespace drawerbase
@ -293,7 +292,7 @@ namespace nana
if (pos > length())
throw std::out_of_range("tabbar::insert invalid position");
this->get_drawer_trigger().insert(pos, to_nstring(text), std::move(value));
this->get_drawer_trigger().insert(pos, to_nstring(std::move(text)), std::move(value));
API::update_window(*this);
}

View File

@ -1,7 +1,7 @@
/*
* A Bedrock Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -15,10 +15,10 @@
#include <nana/gui/detail/bedrock_pi_data.hpp>
#include <nana/gui/detail/event_code.hpp>
#include <nana/system/platform.hpp>
#include <nana/gui/detail/inner_fwd_implement.hpp>
#include <nana/gui/detail/native_window_interface.hpp>
#include <nana/gui/layout_utility.hpp>
#include <nana/gui/detail/element_store.hpp>
#include "inner_fwd_implement.hpp"
#include <errno.h>
#include <algorithm>
@ -104,6 +104,42 @@ namespace detail
void window_proc_for_packet(Display *, nana::detail::msg_packet_tag&);
void window_proc_for_xevent(Display*, XEvent&);
class accel_key_comparer
{
public:
bool operator()(const accel_key& a, const accel_key& b) const
{
auto va = a.case_sensitive ? a.key : std::tolower(a.key);
auto vb = b.case_sensitive ? b.key : std::tolower(b.key);
if(va < vb)
return true;
else if(va > vb)
return false;
if (a.case_sensitive != b.case_sensitive)
return b.case_sensitive;
if (a.alt != b.alt)
return b.alt;
if (a.ctrl != b.ctrl)
return b.ctrl;
return ((a.shift != b.shift) && b.shift);
}
};
struct accel_key_value
{
std::function<void()> command;
};
struct window_platform_assoc
{
std::map<accel_key, accel_key_value, accel_key_comparer> accel_commands;
};
//class bedrock defines a static object itself to implement a static singleton
//here is the definition of this object
bedrock bedrock::bedrock_object;
@ -218,10 +254,28 @@ namespace detail
{
XKeyEvent xkey;
nana::detail::platform_spec::instance().read_keystate(xkey);
arg.alt = (xkey.state & Mod1Mask);
arg.ctrl = (xkey.state & ControlMask);
arg.shift = (xkey.state & ShiftMask);
}
void bedrock::delete_platform_assoc(window_platform_assoc* passoc)
{
delete passoc;
}
void bedrock::keyboard_accelerator(native_window_type wd, const accel_key& ackey, const std::function<void()>& fn)
{
auto misc = wd_manager().root_runtime(wd);
if (nullptr == misc)
return;
if (!misc->wpassoc)
misc->wpassoc = new window_platform_assoc;
misc->wpassoc->accel_commands[ackey].command = fn;
}
element_store& bedrock::get_element_store() const
{
return impl_->estore;
@ -490,6 +544,34 @@ namespace detail
return wchar_t(keysym);
}
bool translate_keyboard_accelerator(root_misc* misc, char os_code, const arg_keyboard& modifiers)
{
if(!misc->wpassoc)
return false;
auto lower_oc = std::tolower(os_code);
std::function<void()> command;
for(auto & accel : misc->wpassoc->accel_commands)
{
if(accel.first.key != (accel.first.case_sensitive ? os_code : lower_oc))
continue;
if(accel.first.alt == modifiers.alt && accel.first.ctrl == modifiers.ctrl && accel.first.shift == modifiers.shift)
{
command = accel.second.command;
break;
}
}
if(!command)
return false;
command();
return true;
}
void window_proc_for_xevent(Display* /*display*/, XEvent& xevent)
{
typedef detail::bedrock::core_window_t core_window_t;
@ -849,6 +931,9 @@ namespace detail
if(msgwnd)
{
arg_keyboard modifiers_status;
brock.get_key_state(modifiers_status);
KeySym keysym;
Status status;
char fixbuf[33];
@ -883,16 +968,20 @@ namespace detail
keybuf[len] = 0;
wchar_t os_code = 0;
bool accel_translated = false;
switch(status)
{
case XLookupKeySym:
case XLookupBoth:
os_code = os_code_from_keysym(keysym);
accel_translated = translate_keyboard_accelerator(root_runtime, os_code, modifiers_status);
if(accel_translated)
break;
if(os_code == keyboard::tab && (false == (msgwnd->flags.tab & detail::tab_type::eating))) //Tab
{
arg_keyboard argkey;
brock.get_key_state(argkey);
auto tstop_wd = wd_manager.tabstop(msgwnd, !argkey.shift);
auto tstop_wd = wd_manager.tabstop(msgwnd, !modifiers_status.shift);
if (tstop_wd)
{
root_runtime->condition.ignore_tab = true;
@ -907,9 +996,9 @@ namespace detail
if((nullptr == pressed_wd) && (nullptr == pressed_wd_space))
{
arg_mouse arg;
arg.alt = false;
arg.alt = modifiers_status.alt;
arg.button = ::nana::mouse::left_button;
arg.ctrl = false;
arg.ctrl = modifiers_status.ctrl;
arg.evt_code = event_code::mouse_down;
arg.left_button = true;
arg.mid_button = false;
@ -955,11 +1044,10 @@ namespace detail
if(keyboard::os_ctrl == os_code)
context.is_ctrl_pressed = true;
arg_keyboard arg;
arg_keyboard arg = modifiers_status;
arg.ignore = false;
arg.key = os_code;
arg.evt_code = event_code::key_press;
brock.get_key_state(arg);
arg.window_handle = reinterpret_cast<window>(msgwnd);
brock.emit(event_code::key_press, msgwnd, arg, true, &context);
@ -988,7 +1076,7 @@ namespace detail
for(int i = 0; i < len; ++i)
{
arg_keyboard arg;
arg_keyboard arg = modifiers_status;
arg.ignore = false;
arg.key = charbuf[i];
@ -1011,7 +1099,6 @@ namespace detail
}
arg.evt_code = event_code::key_char;
arg.window_handle = reinterpret_cast<window>(msgwnd);
brock.get_key_state(arg);
msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast<window>(msgwnd));
if(arg.ignore == false && wd_manager.available(msgwnd))
draw_invoker(&drawer::key_char, msgwnd, arg, &context);

View File

@ -1,7 +1,7 @@
/**
* A Bedrock Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -20,11 +20,11 @@
#include <nana/system/platform.hpp>
#include <nana/system/timepiece.hpp>
#include <nana/gui.hpp>
#include <nana/gui/detail/inner_fwd_implement.hpp>
#include <nana/gui/detail/native_window_interface.hpp>
#include <nana/gui/layout_utility.hpp>
#include <nana/gui/detail/element_store.hpp>
#include <nana/gui/detail/color_schemes.hpp>
#include "inner_fwd_implement.hpp"
#include <iostream> //use std::cerr
@ -182,6 +182,12 @@ namespace detail
}cache;
};
struct window_platform_assoc
{
HACCEL accel{ nullptr }; ///< A handle to a Windows keyboard accelerator object.
std::map<int, std::function<void()>> accel_commands;
};
//class bedrock defines a static object itself to implement a static singleton
//here is the definition of this object
bedrock bedrock::bedrock_object;
@ -345,6 +351,25 @@ namespace detail
}
}
void process_msg(bedrock* brock, MSG& msg)
{
if (WM_KEYFIRST <= msg.message && msg.message <= WM_KEYLAST)
{
auto misc = brock->wd_manager().root_runtime(reinterpret_cast<native_window_type>(msg.hwnd));
if (misc && misc->wpassoc && misc->wpassoc->accel)
{
if (::TranslateAccelerator(msg.hwnd, misc->wpassoc->accel, &msg))
return;
}
}
auto menu_wd = brock->get_menu(reinterpret_cast<native_window_type>(msg.hwnd), true);
if (menu_wd) interior_helper_for_menu(msg, menu_wd);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
void bedrock::pump_event(window condition_wd, bool is_modal)
{
const unsigned tid = ::GetCurrentThreadId();
@ -383,11 +408,15 @@ namespace detail
if (msg.message == WM_QUIT) break;
if ((WM_KEYFIRST <= msg.message && msg.message <= WM_KEYLAST) || !::IsDialogMessage(native_handle, &msg))
{
#if 0
auto menu_wd = get_menu(reinterpret_cast<native_window_type>(msg.hwnd), true);
if (menu_wd) interior_helper_for_menu(msg, menu_wd);
::TranslateMessage(&msg);
::TranslateMessage(&msg); //deprecated
::DispatchMessage(&msg);
#else
process_msg(this, msg);
#endif
wd_manager().remove_trash_handle(tid);
}
@ -400,11 +429,15 @@ namespace detail
{
if (-1 != ::GetMessage(&msg, 0, 0, 0))
{
#if 0
auto menu_wd = get_menu(reinterpret_cast<native_window_type>(msg.hwnd), true);
if (menu_wd) interior_helper_for_menu(msg, menu_wd);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
#else
process_msg(this, msg);
#endif
}
wd_manager().call_safe_place(tid);
@ -420,11 +453,15 @@ namespace detail
{
if(-1 != ::GetMessage(&msg, 0, 0, 0))
{
#if 0
auto menu_wd = get_menu(reinterpret_cast<native_window_type>(msg.hwnd), true);
if(menu_wd) interior_helper_for_menu(msg, menu_wd);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
#else
process_msg(this, msg);
#endif
}
wd_manager().call_safe_place(tid);
@ -635,6 +672,7 @@ namespace detail
switch(msg)
{
case WM_COMMAND:
case WM_DESTROY:
case WM_SHOWWINDOW:
case WM_SIZING:
@ -786,6 +824,17 @@ namespace detail
switch (message)
{
case WM_COMMAND:
if ((1 == HIWORD(wParam)) && root_runtime->wpassoc)
{
auto i = root_runtime->wpassoc->accel_commands.find(LOWORD(wParam));
if (i != root_runtime->wpassoc->accel_commands.end())
{
auto fn = i->second;
fn();
}
}
break;
case WM_IME_STARTCOMPOSITION:
if (msgwnd->other.attribute.root->ime_enabled)
{
@ -1578,10 +1627,58 @@ namespace detail
void bedrock::get_key_state(arg_keyboard& kb)
{
kb.alt = (0 != (::GetKeyState(VK_MENU) & 0x80));
kb.ctrl = (0 != (::GetKeyState(VK_CONTROL) & 0x80));
kb.shift = (0 != (::GetKeyState(VK_SHIFT) & 0x80));
}
void bedrock::delete_platform_assoc(window_platform_assoc* passoc)
{
delete passoc;
}
//Generates an identitifer for an accel key.
std::pair<int, WORD> id_accel_key(const accel_key& key)
{
std::pair<int, WORD> ret;
//Use virt-key for non-case sensitive
if (!key.case_sensitive)
ret.second = static_cast<WORD>(std::tolower(key.key) - 'a' + 0x41);
ret.first = ret.second | int(key.case_sensitive ? (1 << 8) : 0) | int(key.alt ? (1 << 9) : 0) | int(key.ctrl ? (1 << 10) : 0) | int(key.shift ? (1 << 11) : 0);
return ret;
}
void bedrock::keyboard_accelerator(native_window_type wd, const accel_key& key, const std::function<void()>& fn)
{
auto misc = wd_manager().root_runtime(wd);
if (nullptr == misc)
return;
if (!misc->wpassoc)
misc->wpassoc = new window_platform_assoc;
auto idkey = id_accel_key(key);
misc->wpassoc->accel_commands[idkey.first] = fn;
auto accel_size = ::CopyAcceleratorTable(misc->wpassoc->accel, nullptr, 0);
std::unique_ptr<ACCEL[]> accels(new ACCEL[accel_size + 1]);
if (accel_size)
::CopyAcceleratorTable(misc->wpassoc->accel, accels.get(), accel_size);
auto p = accels.get() + accel_size;
p->cmd = idkey.first;
p->fVirt = (key.case_sensitive ? 0 : FVIRTKEY) | (key.alt ? FALT : 0) | (key.ctrl ? FCONTROL : 0) | (key.shift ? FSHIFT : 0);
p->key = idkey.second;
::DestroyAcceleratorTable(misc->wpassoc->accel);
misc->wpassoc->accel = ::CreateAcceleratorTable(accels.get(), accel_size + 1);
}
element_store& bedrock::get_element_store() const
{
return impl_->estore;

View File

@ -1,7 +1,7 @@
/*
* Implementations of Inner Forward Declaration
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -15,9 +15,9 @@
#define NANA_GUI_INNER_FWD_IMPLEMENT_HPP
#include <nana/push_ignore_diagnostic>
#include "inner_fwd.hpp"
#include "basic_window.hpp"
#include "../../paint/graphics.hpp"
#include <nana/gui/detail/inner_fwd.hpp>
#include <nana/gui/detail/basic_window.hpp>
#include <nana/paint/graphics.hpp>
#include <map>
@ -54,10 +54,13 @@ namespace nana{
implementation * impl_;
};
struct window_platform_assoc;
struct root_misc
{
basic_window * window;
window_platform_assoc * wpassoc{ nullptr };
nana::paint::graphics root_graph;
shortkey_container shortkeys;
@ -71,6 +74,10 @@ namespace nana{
root_misc(root_misc&&);
root_misc(basic_window * wd, unsigned width, unsigned height);
~root_misc();
private:
root_misc(const root_misc&) = delete;
root_misc& operator=(const root_misc&) = delete;
};//end struct root_misc

View File

@ -1,7 +1,7 @@
/*
* Window Layout Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -106,43 +106,46 @@ namespace nana
visual = rectangle{ wd->pos_root, wd->dimension };
if (wd->root_widget != wd)
if (category::flags::root != wd->other.category)
{
//Test if the root widget is overlapped the specified widget
//the pos of root widget is (0, 0)
if (overlapped(visual, rectangle{ wd->root_widget->pos_owner, wd->root_widget->dimension }) == false)
return false;
}
for (auto parent = wd->parent; parent; parent = parent->parent)
{
if (category::flags::root == parent->other.category)
for (auto parent = wd->parent; parent; parent = parent->parent)
{
//visual rectangle of wd's parent
rectangle vrt_parent{parent->pos_root, parent->dimension};
point pos_root;
while (parent->parent)
if (category::flags::root == parent->other.category)
{
pos_root -= native_interface::window_position(parent->root);
if (!overlap(rectangle{ pos_root, parent->parent->root_widget->dimension }, vrt_parent, vrt_parent))
return false;
parent = parent->parent->root_widget;
wd = parent;
break;
}
if (!overlap(vrt_parent, visual, visual))
if (!overlap(rectangle{ parent->pos_root, parent->dimension }, visual, visual))
return false;
return true;
}
if (!overlap(rectangle{ parent->pos_root, parent->dimension }, visual, visual))
return false;
}
return true;
//Now, wd actually is the root widget of original parameter wd
if (nullptr == wd->parent)
return true;
auto parent_rw = wd->parent->root_widget;
//visual rectangle of wd's parent
rectangle vrt_parent{ parent_rw->pos_root, parent_rw->dimension };
point pos_root;
while (parent_rw->parent)
{
pos_root -= native_interface::window_position(parent_rw->root);
if (!overlap(rectangle{ pos_root, parent_rw->parent->root_widget->dimension }, vrt_parent, vrt_parent))
return false;
parent_rw = parent_rw->parent->root_widget;
}
return overlap(vrt_parent, visual, visual);
}
//read_overlaps
@ -386,6 +389,13 @@ namespace nana
nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension);
for (auto wd : data_sect.effects_bground_windows)
{
//Don't notify the window if both native root windows are not same(e.g. wd and sigwd have
//a some parent). Otherwise, _m_paint_glass_window() recursively paints sigwd to make stack overflow.
//On the other hand, a nested root window is always floating on its parent's child widgets, it's unnecessary to
//notify the wd if they haven't a same native root window.
if (sigwd->root != wd->root)
continue;
if (wd == sigwd || !wd->displayed() ||
(false == overlapped(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd)))
continue;

View File

@ -1,7 +1,7 @@
/*
* Window Manager Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -16,11 +16,11 @@
#include <nana/gui/detail/events_operation.hpp>
#include <nana/gui/detail/window_manager.hpp>
#include <nana/gui/detail/window_layout.hpp>
#include "window_register.hpp"
#include <nana/gui/detail/native_window_interface.hpp>
#include <nana/gui/detail/inner_fwd_implement.hpp>
#include <nana/gui/layout_utility.hpp>
#include <nana/gui/detail/effects_renderer.hpp>
#include "window_register.hpp"
#include "inner_fwd_implement.hpp"
#include <stdexcept>
#include <algorithm>
@ -140,10 +140,12 @@ namespace nana
//struct root_misc
root_misc::root_misc(root_misc&& other):
window(other.window),
wpassoc(other.wpassoc),
root_graph(std::move(other.root_graph)),
shortkeys(std::move(other.shortkeys)),
condition(std::move(other.condition))
{
other.wpassoc = nullptr; //moved-from
}
root_misc::root_misc(basic_window * wd, unsigned width, unsigned height)
@ -155,6 +157,11 @@ namespace nana
condition.pressed_by_space = nullptr;
condition.hovered = nullptr;
}
root_misc::~root_misc()
{
bedrock::delete_platform_assoc(wpassoc);
}
//end struct root_misc
//class root_register

View File

@ -1041,7 +1041,8 @@ namespace nana
bool filebox::show() const
{
#if defined(NANA_WINDOWS)
std::wstring wfile;
auto winitfile = to_wstring(impl_->file);
std::wstring wfile(winitfile);
wfile.resize(520);
OPENFILENAME ofn;

View File

@ -821,6 +821,15 @@ namespace API
}
}
nana::optional<rectangle> window_rectangle(window wd)
{
auto iwd = reinterpret_cast<basic_window*>(wd);
internal_scope_guard lock;
if (restrict::wd_manager().available(iwd))
return rectangle(iwd->pos_owner, iwd->dimension);
return{};
}
bool get_window_rectangle(window wd, rectangle& r)
{
auto iwd = reinterpret_cast<basic_window*>(wd);

View File

@ -379,7 +379,7 @@ namespace nana{ namespace drawerbase
}//end namespace drawerbase
//button
//@brief: Defaine a button widget and it provides the interfaces to be operational
//@brief: Define a button widget and it provides the interfaces to be operational
button::button(){}
button::button(window wd, bool visible)

View File

@ -1,6 +1,6 @@
/*
* A Form Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -10,6 +10,7 @@
*/
#include <nana/gui/widgets/form.hpp>
#include <nana/gui/detail/bedrock.hpp>
namespace nana
{
@ -94,6 +95,11 @@ namespace nana
{
API::wait_for(handle());
}
void form::keyboard_accelerator(const accel_key& key, const std::function<void()>& fn)
{
nana::detail::bedrock::instance().keyboard_accelerator(this->native_handle(), key, fn);
}
//end class form
//class nested_form

View File

@ -208,8 +208,8 @@ namespace nana{
outter[field_title] << impl_->caption;
outter.collocate();
impl_->caption.transparent(true);
color pbg = API::bgcolor(this->parent());
impl_->caption.bgcolor(pbg.blend(colors::black, 0.025));
this->bgcolor(pbg.blend(colors::black, 0.05));
@ -222,10 +222,27 @@ namespace nana{
auto gap_px = impl_->gap - 1;
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)
auto const top_round_line = static_cast<int>(impl_->caption_dimension.height) / 2;
graph.round_rectangle(rectangle(point(gap_px, top_round_line),
nana::size(graph.width() - 2 * gap_px, graph.height() - top_round_line - gap_px)
),
3, 3, colors::gray_border, true, this->bgcolor());
auto opt_r = API::window_rectangle(impl_->caption);
if (opt_r)
{
rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width, static_cast<unsigned>(top_round_line - opt_r->y) } };
grad_r.y += top_round_line*2 / 3;
grad_r.x -= 2;
grad_r.width += 4;
graph.gradual_rectangle(grad_r,
API::bgcolor(this->parent()), this->bgcolor(), true
);
}
});
}

View File

@ -1,7 +1,7 @@
/*
* A List Box Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -1273,6 +1273,7 @@ namespace nana
}
//Backward
n = -n;
dpos = pos;
if (good(dpos.cat))
{
@ -1519,8 +1520,6 @@ namespace nana
template<typename Pred>
std::vector<std::pair<index_pair, bool>> select_display_range_if(const index_pair& fr_abs, index_pair to_dpl, bool deselect_others, Pred pred)
{
const index_pairs already_selected = (deselect_others ? this->pick_items(true) : index_pairs{});
if (to_dpl.empty())
{
if (fr_abs.empty())
@ -1533,6 +1532,11 @@ namespace nana
if (fr_dpl > to_dpl)
std::swap(fr_dpl, to_dpl);
if (to_dpl.is_category() && this->size_item(to_dpl.cat) > 0)
to_dpl.item = this->size_item(to_dpl.cat) - 1;
const index_pairs already_selected = (deselect_others ? this->pick_items(true) : index_pairs{});
const auto begin = fr_dpl;
const auto last = to_dpl;
@ -1586,7 +1590,7 @@ namespace nana
return pairs;
}
bool select_for_all(bool sel, const index_pair& except = index_pair{npos, npos})
bool select_for_all(bool sel, const index_pair& except_abs = index_pair{npos, npos})
{
bool changed = false;
index_pair pos;
@ -1595,7 +1599,7 @@ namespace nana
pos.item = 0;
for(auto & m : cat.items)
{
if (except != pos)
if (except_abs != pos)
{
if (m.flags.selected != sel)
{
@ -1618,7 +1622,7 @@ namespace nana
}
/// return absolute positions, no relative to display
index_pairs pick_items(bool for_selection) const
index_pairs pick_items(bool for_selection, bool find_first = false) const
{
index_pairs results;
index_pair id;
@ -1629,7 +1633,11 @@ namespace nana
for (auto & m : cat.items)
{
if (for_selection ? m.flags.selected : m.flags.checked)
{
results.push_back(id); // absolute positions, no relative to display
if (find_first)
return results;
}
++id.item;
}
++id.cat;
@ -1978,6 +1986,15 @@ namespace nana
};
}
bool cs_status(index_pair abs_pos, bool for_selection) const
{
if (abs_pos.is_category())
return lister.cat_status(abs_pos.cat, for_selection);
auto & flags = lister.get(abs_pos.cat)->items.at(abs_pos.item).flags;
return (for_selection ? flags.selected : flags.checked);
}
void resize_disp_area()
{
auto head_px = this->header_visible_px();
@ -3416,6 +3433,8 @@ namespace nana
rect.y - static_cast<int>(origin.y % item_height_px)
};
essence_->inline_buffered_table.swap(essence_->inline_table);
// The first display is empty when the listbox is empty.
if (!first_disp.empty())
{
@ -3438,8 +3457,6 @@ namespace nana
auto idx = first_disp;
essence_->inline_buffered_table.swap(essence_->inline_table);
for (auto & cat : lister.cat_container())
for (auto & ind : cat.indicators)
{
@ -3509,10 +3526,10 @@ namespace nana
++idx.item;
}
}
essence_->inline_buffered_table.clear();
}
essence_->inline_buffered_table.clear();
if (item_coord.y < rect.bottom())
{
rectangle bground_r{ rect.x, item_coord.y, rect.width, static_cast<unsigned>(rect.bottom() - item_coord.y) };
@ -4117,17 +4134,16 @@ namespace nana
essence_->content_view->sync(false);
}
bool sel = true;
bool new_selected_status = true;
//no single selected
if (!lister.single_status(true))
if (!lister.single_status(true)) //multiply selection enabled
{
if (arg.shift)
{
//Set the first item as the begin of selected item if there
//is not a latest selected item.(#154 reported by RenaudAlpes)
if (lister.latest_selected_abs.empty() || lister.latest_selected_abs.is_category())
lister.latest_selected_abs.set_both(0);
if (lister.latest_selected_abs.empty())
lister.latest_selected_abs = lister.first();
auto before = lister.latest_selected_abs;
@ -4141,7 +4157,7 @@ namespace nana
else if (arg.ctrl)
{
essence_->mouse_selection.reverse_selection = true;
sel = !item_proxy(essence_, abs_item_pos).selected();
new_selected_status = !essence_->cs_status(abs_item_pos, true);
}
else
{
@ -4150,12 +4166,12 @@ namespace nana
//Unselects all selected items if the current item is not selected before selecting.
auto selected = lister.pick_items(true);
if (selected.cend() == std::find(selected.cbegin(), selected.cend(), item_pos))
lister.select_for_all(false, item_pos);
lister.select_for_all(false, abs_item_pos);
}
else
{
//Unselects all selected items except current item if right button clicked.
lister.select_for_all(false, item_pos); //cancel all selections
lister.select_for_all(false, abs_item_pos); //cancel all selections
}
}
}
@ -4164,14 +4180,14 @@ namespace nana
//Clicking on a category is ignored when single selection is enabled.
//Fixed by Greentwip(issue #121)
if (item_ptr)
sel = !item_proxy(essence_, abs_item_pos).selected();
new_selected_status = !item_proxy(essence_, abs_item_pos).selected();
}
if(item_ptr)
{
if (item_ptr->flags.selected != sel)
if (item_ptr->flags.selected != new_selected_status)
{
if (sel)
if (new_selected_status)
{
//Deselects the previously selected item.
lister.cancel_others_if_single_enabled(true, abs_item_pos);
@ -4180,13 +4196,15 @@ namespace nana
else if (essence_->lister.latest_selected_abs == abs_item_pos)
essence_->lister.latest_selected_abs.set_both(npos);
item_ptr->flags.selected = sel;
item_ptr->flags.selected = new_selected_status;
lister.emit_cs(abs_item_pos, true);
}
}
else
{
lister.cat_status(item_pos.cat, true, true);
//A category was clicked. Sets all child items to be selected only if multiply selection is enabled.
if(!lister.single_status(true))
lister.cat_status(item_pos.cat, true, true);
}
}
else
@ -4336,8 +4354,9 @@ namespace nana
void trigger::key_press(graph_reference graph, const arg_keyboard& arg)
{
auto & list = essence_->lister;
// Exit if list is empty
if (essence_->lister.first().empty())
if (list.first().empty())
return;
bool upward = false;
@ -4347,12 +4366,12 @@ namespace nana
case keyboard::os_arrow_up:
upward = true;
case keyboard::os_arrow_down:
essence_->lister.move_select(upward, !arg.shift, true);
list.move_select(upward, !arg.shift, true);
break;
case L' ':
{
index_pairs s;
bool ck = ! essence_->lister.item_selected_all_checked(s);
bool ck = ! list.item_selected_all_checked(s);
for(auto i : s)
item_proxy(essence_, i).check(ck);
}
@ -4361,30 +4380,69 @@ namespace nana
upward = true;
case keyboard::os_pagedown:
{
//Turns page, then returns if no change occurs
if (!essence_->content_view->turn_page(!upward, false))
return;
auto const item_px = essence_->item_height();
auto picked_items = list.pick_items(true, true);
index_pair init_idx = (picked_items.empty() ? list.first() : picked_items[0]);
essence_->lister.select_for_all(false);
auto idx = essence_->first_display();
//Get the pixels between the init item and top edge or bottom edge
auto logic_top = static_cast<int>(list.distance(list.first(), init_idx) * item_px);
if (!upward)
idx = essence_->lister.advance(idx, static_cast<int>(essence_->count_of_exposed(false)) - 1);
auto const screen_top = essence_->content_view->origin().y;
auto const screen_bottom = screen_top + essence_->content_view->view_area().height;
index_pair target_idx;
if (!idx.is_category())
item_proxy::from_display(essence_, idx).select(true);
else if (!essence_->lister.single_status(true)) //not selected
essence_->lister.cat_status(idx.cat, true, true);
//Check if it scrolls in current screen window
//condition: top of target item is not less than top edge of content view and
//the bottom of target item is not greater than bottom edge of content view.
if ((screen_top + item_px <= logic_top) && (logic_top + item_px + item_px <= screen_bottom))
{
int offset = (static_cast<int>(upward ? screen_top : screen_bottom - item_px) - logic_top) / static_cast<int>(item_px);
target_idx = list.advance(init_idx, offset);
}
else
{
//turn page
auto page_item_count = (std::max)(1, static_cast<int>(essence_->count_of_exposed(false)));
break;
auto origin = essence_->content_view->origin();
if (upward)
{
target_idx = list.advance(init_idx, -page_item_count);
if (target_idx.empty())
target_idx = list.first();
origin.y = list.distance(list.first(), target_idx) * item_px;
}
else
{
target_idx = list.advance(init_idx, page_item_count);
if (target_idx.empty())
target_idx = list.last();
origin.y = list.distance(list.first(), target_idx) * item_px + item_px;
if (origin.y >= (screen_bottom - screen_top))
origin.y -= (screen_bottom - screen_top);
else
origin.y = 0;
}
essence_->content_view->move_origin(origin - essence_->content_view->origin());
}
if (!target_idx.is_category())
item_proxy::from_display(essence_, target_idx).select(true);
else if (!list.single_status(true)) //not selected
list.cat_status(target_idx.cat, true, true);
}
break;
case keyboard::os_home:
case keyboard::os_end:
{
essence_->lister.select_for_all(false);
list.select_for_all(false);
auto pos = (keyboard::os_home == arg.key ? essence_->lister.first() : essence_->lister.last());
auto pos = (keyboard::os_home == arg.key ? list.first() : list.last());
if (!pos.empty())
{
//When the pos indicates an empty category, then search forwards/backwards(depending on arg.key whether it is Home or End) for a non empty category.
@ -4393,9 +4451,9 @@ namespace nana
{
if (keyboard::os_home == arg.key)
{
while (0 == essence_->lister.size_item(pos.cat))
while (0 == list.size_item(pos.cat))
{
if (++pos.cat >= essence_->lister.cat_container().size())
if (++pos.cat >= list.cat_container().size())
{
pos = index_pair{ npos, npos };
break;
@ -4404,7 +4462,7 @@ namespace nana
}
else
{
while (0 == essence_->lister.size_item(pos.cat))
while (0 == list.size_item(pos.cat))
{
if (pos.cat-- == 0)
{
@ -4416,7 +4474,7 @@ namespace nana
if (!pos.empty())
{
if (essence_->lister.expand(pos.cat))
if (list.expand(pos.cat))
pos.item = 0;
}
}
@ -4425,8 +4483,8 @@ namespace nana
{
if (pos.is_category())
{
if (!essence_->lister.single_status(true)) //multiple selection is not enabled
essence_->lister.cat_status(pos.cat, true, true);
if (!list.single_status(true)) //multiple selection is not enabled
list.cat_status(pos.cat, true, true);
}
else
item_proxy::from_display(essence_, pos).select(true);
@ -4662,12 +4720,12 @@ namespace nana
bool item_proxy::operator==(const std::string& s) const
{
return (text(pos_.item) == s);
return (text(0) == s);
}
bool item_proxy::operator==(const std::wstring& s) const
{
return (text(pos_.item) == to_utf8(s));
return (text(0) == to_utf8(s));
}
item_proxy & item_proxy::operator=(const item_proxy& rhs)
@ -5158,7 +5216,8 @@ namespace nana
cat_->make_sort_order();
ess_->lister.sort();
ess_->update(true);
//Don't ignore the auto-draw flag for performance enhancement.
ess_->update();
}
}
//end class cat_proxy

View File

@ -1,6 +1,6 @@
/*
* A Progress Indicator Implementation
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -96,7 +96,11 @@ namespace nana
{
if (widget_)
{
auto value_px = (widget_->size().width - border_px * 2) * value_ / max_;
auto value_px = (widget_->size().width - border_px * 2);
//avoid overflow
if (value_ < max_)
value_px = static_cast<unsigned>(value_px * (double(value_) / double(max_)));
if (value_px != value_px_)
{

View File

@ -1,7 +1,7 @@
/*
* A Scroll Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -20,9 +20,15 @@ namespace nana
namespace scroll
{
//struct metrics_type
metrics_type::metrics_type()
:peak(1), range(1), step(1), value(0),
what(buttons::none), pressed(false), scroll_length(0), scroll_pos(0)
metrics_type::metrics_type():
peak(1),
range(1),
step(1),
value(0),
what(buttons::none),
pressed(false),
scroll_length(0),
scroll_pos(0)
{}
//end struct metrics_type

View File

@ -91,7 +91,7 @@ namespace nana
{
::jpeg_create_decompress(&jdstru);
::jpeg_mem_src(&jdstru, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(data)), bytes);
::jpeg_mem_src(&jdstru, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(data)), static_cast<unsigned long>(bytes));
_m_read_jpg(jdstru);
jpeg_finish_decompress(&jdstru);