Merge branch 'develop'
This commit is contained in:
commit
dbd8a4a691
@ -5,7 +5,7 @@
|
||||
# Robert Hauck - Enable support for PNG/Freetype
|
||||
# Qiangqiang Wu - Add biicode support
|
||||
# Ariel Vina-Rodriguez (qPCR4vir)
|
||||
# Pavel O. - fix compilation with boost::filesystem (#281)
|
||||
# Pavel O. - fix compilation with boost::filesystem (#281)
|
||||
# Frostbane - Add option for compiling a shared library (#263,#265)
|
||||
#
|
||||
# Nana uses some build systems: MS-VS solution, MAKE, bakefile, codeblock, etc. manually optimized.
|
||||
@ -156,25 +156,25 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
if(NANA_CMAKE_SHARED_LIB)
|
||||
list(APPEND NANA_LINKS -lgcc -lstdc++ -pthread)
|
||||
else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread")
|
||||
# message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}")
|
||||
if(MINGW)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static -pthread")
|
||||
else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread")
|
||||
endif()
|
||||
endif(NANA_CMAKE_SHARED_LIB)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
# GCC 4.9
|
||||
# GCC 4.9
|
||||
list(APPEND NANA_LINKS "-lboost_system -lboost_thread")
|
||||
|
||||
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3)
|
||||
# IS_GNUCXX < 5.3
|
||||
# IS_GNUCXX < 5.3
|
||||
else()
|
||||
list(APPEND NANA_LINKS -lstdc++fs)
|
||||
endif()
|
||||
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang
|
||||
# set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libstdc++")
|
||||
list(APPEND NANA_LINKS -stdlib=libstdc++)
|
||||
endif ()
|
||||
|
||||
|
||||
@ -23,6 +23,8 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace nana{ namespace audio
|
||||
{
|
||||
namespace detail
|
||||
@ -52,8 +54,8 @@ namespace nana{ namespace audio
|
||||
private:
|
||||
void _m_prepare_routine();
|
||||
private:
|
||||
volatile bool running_;
|
||||
volatile bool wait_for_buffer_;
|
||||
std::atomic<bool> running_;
|
||||
std::atomic<bool> wait_for_buffer_;
|
||||
std::thread thr_;
|
||||
mutable std::mutex token_buffer_, token_prepared_;
|
||||
mutable std::condition_variable cond_buffer_, cond_prepared_;
|
||||
|
||||
@ -220,12 +220,14 @@
|
||||
#endif
|
||||
|
||||
#undef _nana_std_has_string_view
|
||||
#undef _nana_std_has_emplace_return_type
|
||||
#if ((defined(_MSC_VER) && (_MSC_VER >= 1912) && defined(_MSVC_LANG) && _MSVC_LANG >= 201703)) || \
|
||||
((__cplusplus >= 201703L) && \
|
||||
(defined(__clang__) && (__clang_major__ * 100 + __clang_minor__ >= 400) || \
|
||||
(!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 701))) \
|
||||
)
|
||||
# define _nana_std_has_string_view
|
||||
# define _nana_std_has_emplace_return_type
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -65,11 +65,12 @@ namespace nana
|
||||
bool review_utf8(std::string& text);
|
||||
|
||||
const std::string& to_utf8(const std::string&);
|
||||
std::string to_utf8(const std::wstring&);
|
||||
|
||||
#ifdef _nana_std_has_string_view
|
||||
std::string to_utf8(std::wstring_view sv);
|
||||
std::wstring to_wstring(std::string_view utf8_str);
|
||||
#else
|
||||
std::string to_utf8(const std::wstring&);
|
||||
std::wstring to_wstring(const std::string& utf8_str);
|
||||
#endif
|
||||
|
||||
|
||||
@ -51,6 +51,13 @@ namespace nana
|
||||
top_left, top_right, bottom_left, bottom_right
|
||||
};
|
||||
|
||||
enum class window_relationship
|
||||
{
|
||||
owner, ///< Owner window.
|
||||
parent, ///< Parent window.
|
||||
either_po ///< One between owner and parent.
|
||||
};
|
||||
|
||||
enum class bground_mode
|
||||
{
|
||||
none,
|
||||
|
||||
@ -172,7 +172,7 @@ namespace nana
|
||||
}
|
||||
else if constexpr(std::is_invocable_v<Function>)
|
||||
{
|
||||
return _m_emplace(new docker{ this, [fn](arg_reference){
|
||||
return _m_emplace(new docker{ this, [fn](arg_reference) mutable{
|
||||
fn();
|
||||
}, false }, false);
|
||||
}
|
||||
@ -200,7 +200,7 @@ namespace nana
|
||||
}
|
||||
else if constexpr(std::is_invocable_v<Function>)
|
||||
{
|
||||
return _m_emplace(new docker{ this, [fn](arg_reference) {
|
||||
return _m_emplace(new docker{ this, [fn](arg_reference) mutable{
|
||||
fn();
|
||||
}, true }, in_front);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Platform 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
|
||||
@ -36,6 +36,14 @@ namespace detail
|
||||
unsigned extra_height; //extra border size, it is useful in Windows, ignore in X11 always 0
|
||||
};
|
||||
|
||||
struct frame_extents
|
||||
{
|
||||
int left;
|
||||
int right;
|
||||
int top;
|
||||
int bottom;
|
||||
};
|
||||
|
||||
using native_string_type = ::nana::detail::native_string_type;
|
||||
|
||||
//Execute a function in a thread which is associated with the specified native window.
|
||||
@ -70,14 +78,15 @@ namespace detail
|
||||
static void bring_top(native_window_type, bool activated);
|
||||
static void set_window_z_order(native_window_type, native_window_type wd_after, z_order_action action_if_no_wd_after);
|
||||
|
||||
static frame_extents window_frame_extents(native_window_type);
|
||||
static bool window_size(native_window_type, const size&);
|
||||
static void get_window_rect(native_window_type, rectangle&);
|
||||
static void window_caption(native_window_type, const native_string_type&);
|
||||
static native_string_type window_caption(native_window_type);
|
||||
static void capture_window(native_window_type, bool);
|
||||
static nana::point cursor_position();
|
||||
static native_window_type get_owner_window(native_window_type);
|
||||
static native_window_type parent_window(native_window_type);
|
||||
|
||||
static native_window_type get_window(native_window_type wd, window_relationship);
|
||||
static native_window_type parent_window(native_window_type child, native_window_type new_parent, bool returns_previous);
|
||||
//For Caret
|
||||
static void caret_create(native_window_type, const ::nana::size&);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A Message Box Class
|
||||
* 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
|
||||
@ -255,7 +255,11 @@ namespace nana
|
||||
bool show_modal(Args&& ... args)
|
||||
{
|
||||
std::vector<abstract_content*> contents;
|
||||
#ifdef __cpp_fold_expressions
|
||||
(contents.emplace_back(&args), ...);
|
||||
#else
|
||||
_m_fetch_args(contents, std::forward<Args>(args)...);
|
||||
#endif
|
||||
|
||||
if (contents.empty())
|
||||
return false;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* A Tooltip Implementation
|
||||
* 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
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* A Combox 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
|
||||
@ -77,6 +77,7 @@ namespace nana
|
||||
public:
|
||||
item_proxy(drawer_impl*, std::size_t pos);
|
||||
item_proxy& text(const ::std::string&);
|
||||
|
||||
::std::string text() const;
|
||||
item_proxy& select();
|
||||
bool selected() const;
|
||||
@ -103,22 +104,19 @@ namespace nana
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
item_proxy& value(const T& t)
|
||||
item_proxy& value(T&& val)
|
||||
{
|
||||
*_m_anyobj(true) = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
item_proxy& value(T&& t)
|
||||
{
|
||||
*_m_anyobj(true) = ::std::move(t);
|
||||
*_m_anyobj(true) = ::std::forward<T>(val);
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
/// Behavior of Iterator's value_type
|
||||
#ifdef _nana_std_has_string_view
|
||||
bool operator==(::std::string_view) const;
|
||||
#else
|
||||
bool operator==(const ::std::string&) const;
|
||||
bool operator==(const char*) const;
|
||||
#endif
|
||||
|
||||
/// Behavior of Iterator
|
||||
item_proxy & operator=(const item_proxy&);
|
||||
@ -192,19 +190,11 @@ namespace nana
|
||||
return _m_at_key(std::move(p));
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
void erase_key(const Key& kv)
|
||||
{
|
||||
typedef typename nana::detail::type_escape<Key>::type key_t;
|
||||
std::unique_ptr<nana::detail::key_interface> p(new nana::key<key_t, std::less<key_t> >(kv));
|
||||
_m_erase(p.get());
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
void erase_key(Key&& kv)
|
||||
{
|
||||
typedef typename nana::detail::type_escape<Key>::type key_t;
|
||||
std::unique_ptr<nana::detail::key_interface> p(new nana::key<key_t, std::less<key_t> >(std::move(kv)));
|
||||
std::unique_ptr<nana::detail::key_interface> p(new nana::key<key_t, std::less<key_t> >(std::forward<Key>(kv)));
|
||||
_m_erase(p.get());
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* A Widget Iterator Template
|
||||
* Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com)
|
||||
* Copyright(C) 2017-2018 Jinhao(cnjinhao@hotmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
@ -23,6 +23,7 @@ namespace nana {
|
||||
template<typename Category, typename T>
|
||||
class widget_iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = Category;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
@ -809,7 +809,6 @@ namespace nana
|
||||
/// operate with absolute positions and contain only the position but montain pointers to parts of the real items
|
||||
/// item_proxy self, it references and iterators are not invalidated by sort()
|
||||
class item_proxy
|
||||
//: public std::iterator<std::input_iterator_tag, item_proxy> //deprecated
|
||||
: public ::nana::widgets::detail::widget_iterator<std::input_iterator_tag, item_proxy>
|
||||
{
|
||||
public:
|
||||
@ -933,15 +932,20 @@ namespace nana
|
||||
template<typename T>
|
||||
item_proxy & value(T&& t)
|
||||
{
|
||||
*_m_value(true) = std::forward<T>(t);
|
||||
*_m_value(true) = ::std::forward<T>(t);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Behavior of Iterator's value_type
|
||||
#ifdef _nana_std_has_string_view
|
||||
bool operator==(::std::string_view sv) const;
|
||||
bool operator==(::std::wstring_view sv) const;
|
||||
#else
|
||||
bool operator==(const char * s) const;
|
||||
bool operator==(const wchar_t * s) const;
|
||||
bool operator==(const ::std::string& s) const;
|
||||
bool operator==(const ::std::wstring& s) const;
|
||||
#endif
|
||||
|
||||
/// Behavior of Iterator
|
||||
item_proxy & operator=(const item_proxy&);
|
||||
@ -984,7 +988,6 @@ namespace nana
|
||||
};
|
||||
|
||||
class cat_proxy
|
||||
//: public std::iterator<std::input_iterator_tag, cat_proxy> //deprecated
|
||||
: public ::nana::widgets::detail::widget_iterator<std::input_iterator_tag, cat_proxy>
|
||||
{
|
||||
public:
|
||||
@ -1028,6 +1031,7 @@ namespace nana
|
||||
template<typename T>
|
||||
void append_model(const T& t)
|
||||
{
|
||||
nana::internal_scope_guard lock;
|
||||
_m_try_append_model(const_virtual_pointer{ &t });
|
||||
_m_update();
|
||||
}
|
||||
|
||||
@ -191,14 +191,14 @@ namespace nana{ namespace widgets
|
||||
void draw_corner();
|
||||
void render(bool focused);
|
||||
public:
|
||||
void put(std::wstring);
|
||||
void put(std::wstring, bool perform_event);
|
||||
void put(wchar_t);
|
||||
void copy() const;
|
||||
void cut();
|
||||
void paste();
|
||||
void enter(bool record_undo = true);
|
||||
void enter(bool record_undo, bool perform_event);
|
||||
void del();
|
||||
void backspace(bool record_undo = true);
|
||||
void backspace(bool record_undo, bool perform_event);
|
||||
void undo(bool reverse);
|
||||
void set_undo_queue_length(std::size_t len);
|
||||
void move_ns(bool to_north); //Moves up and down
|
||||
@ -243,9 +243,9 @@ namespace nana{ namespace widgets
|
||||
void _m_reset();
|
||||
|
||||
//Inserts text at position where the caret is
|
||||
::nana::upoint _m_put(::std::wstring);
|
||||
::nana::upoint _m_put(::std::wstring, bool perform_event);
|
||||
|
||||
::nana::upoint _m_erase_select();
|
||||
::nana::upoint _m_erase_select(bool perform_event);
|
||||
|
||||
::std::wstring _m_make_select_string() const;
|
||||
static bool _m_resolve_text(const ::std::wstring&, std::vector<std::pair<std::size_t, std::size_t>> & lines);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Text Token Stream
|
||||
* 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
|
||||
@ -22,6 +22,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <nana/push_ignore_diagnostic>
|
||||
#include <nana/unicode_bidi.hpp>
|
||||
|
||||
namespace nana{ namespace widgets{ namespace skeletons
|
||||
{
|
||||
@ -95,9 +96,11 @@ namespace nana{ namespace widgets{ namespace skeletons
|
||||
return std::stoi(idstr_, nullptr, 0);
|
||||
}
|
||||
private:
|
||||
static bool _m_unicode_word_breakable(wchar_t ch)
|
||||
static bool _m_unicode_word_breakable(const wchar_t* ch) noexcept
|
||||
{
|
||||
return ((0x4E00 <= ch) && (ch <= 0x9FFF));
|
||||
if (*ch)
|
||||
return unicode_wordbreak(*ch, ch[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Read the data token
|
||||
@ -112,14 +115,14 @@ namespace nana{ namespace widgets{ namespace skeletons
|
||||
idstr_.clear();
|
||||
idstr_.append(1, ch);
|
||||
|
||||
if(_m_unicode_word_breakable(ch))
|
||||
if (_m_unicode_word_breakable(iptr_))
|
||||
{
|
||||
++iptr_;
|
||||
return token::data;
|
||||
}
|
||||
|
||||
ch = *++iptr_;
|
||||
while((iptr_ != endptr_) && (ch > 0xFF) && (false == _m_unicode_word_breakable(ch)))
|
||||
while((iptr_ != endptr_) && (ch > 0xFF) && (false == _m_unicode_word_breakable(iptr_)))
|
||||
{
|
||||
idstr_.append(1, ch);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A textbase class 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
|
||||
@ -301,6 +301,29 @@ namespace skeletons
|
||||
}
|
||||
}
|
||||
|
||||
//Triggers the text_changed event.
|
||||
//It is exposed for outter classes. For a outter class(eg. text_editor), a changing text content operation
|
||||
//may contains multiple textbase operations, therefore, the outter class determines when an event should be triggered.
|
||||
//
|
||||
//Addtional, using text_changed() method, it is possible to allow a outter class performing some updating operations
|
||||
//before triggering text_changed event.
|
||||
void text_changed()
|
||||
{
|
||||
if (!changed_)
|
||||
{
|
||||
_m_first_change();
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
if (edited_)
|
||||
{
|
||||
if (evt_agent_)
|
||||
evt_agent_->text_changed();
|
||||
|
||||
edited_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
size_type lines() const
|
||||
{
|
||||
return text_cont_.size();
|
||||
@ -330,7 +353,7 @@ namespace skeletons
|
||||
_m_at(pos).swap(text);
|
||||
|
||||
_m_make_max(pos);
|
||||
_m_edited();
|
||||
edited_ = true;
|
||||
}
|
||||
|
||||
void insert(upoint pos, string_type && str)
|
||||
@ -351,7 +374,7 @@ namespace skeletons
|
||||
}
|
||||
|
||||
_m_make_max(pos.y);
|
||||
_m_edited();
|
||||
edited_ = true;
|
||||
}
|
||||
|
||||
void insertln(size_type pos, string_type&& str)
|
||||
@ -362,7 +385,7 @@ namespace skeletons
|
||||
text_cont_.emplace_back(new string_type(std::move(str)));
|
||||
|
||||
_m_make_max(pos);
|
||||
_m_edited();
|
||||
edited_ = true;
|
||||
}
|
||||
|
||||
void erase(size_type line, size_type pos, size_type count)
|
||||
@ -378,7 +401,7 @@ namespace skeletons
|
||||
if (attr_max_.line == line)
|
||||
_m_scan_for_max();
|
||||
|
||||
_m_edited();
|
||||
edited_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,7 +421,7 @@ namespace skeletons
|
||||
else if (pos < attr_max_.line)
|
||||
attr_max_.line -= n;
|
||||
|
||||
_m_edited();
|
||||
edited_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -426,7 +449,7 @@ namespace skeletons
|
||||
if(pos < attr_max_.line)
|
||||
--attr_max_.line;
|
||||
|
||||
_m_edited();
|
||||
edited_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,23 +537,12 @@ namespace skeletons
|
||||
|
||||
changed_ = false;
|
||||
}
|
||||
|
||||
void _m_edited()
|
||||
{
|
||||
if(!changed_)
|
||||
{
|
||||
_m_first_change();
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
if (evt_agent_)
|
||||
evt_agent_->text_changed();
|
||||
}
|
||||
private:
|
||||
std::deque<std::unique_ptr<string_type>> text_cont_;
|
||||
textbase_event_agent_interface* evt_agent_{ nullptr };
|
||||
|
||||
mutable bool changed_{ false };
|
||||
mutable bool changed_{ false };
|
||||
mutable bool edited_{ false };
|
||||
mutable std::string filename_; //A string for the saved filename.
|
||||
const string_type nullstr_;
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
#include "skeletons/textbase_export_interface.hpp"
|
||||
#include "skeletons/text_editor_part.hpp"
|
||||
|
||||
#include <nana/optional.hpp>
|
||||
|
||||
namespace nana
|
||||
{
|
||||
class textbox;
|
||||
@ -173,6 +175,13 @@ namespace nana
|
||||
/// 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;
|
||||
|
||||
/// Read the text from a specified line; returns an empty optional on failure
|
||||
std::optional<std::string> getline(std::size_t pos) const;
|
||||
|
||||
///Read the text from a specified line with a set offset. Returns an empty optional for
|
||||
/// failure.
|
||||
std::optional<std::string> getline(std::size_t line_index, std::size_t offset) 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;
|
||||
|
||||
@ -170,7 +170,8 @@ namespace nana
|
||||
}
|
||||
|
||||
/// Base class of all the classes defined as a widget window. Defaultly a widget_tag
|
||||
template<typename Category, typename DrawerTrigger, typename Events = ::nana::general_events, typename Scheme = ::nana::widget_geometrics>
|
||||
template<typename Category, typename DrawerTrigger, typename Events = ::nana::general_events, typename Scheme = ::nana::widget_geometrics,
|
||||
typename = typename std::enable_if<std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value>::type> //type DrawerTrigger must be derived from nana::drawer_trigger
|
||||
class widget_object: public detail::widget_base
|
||||
{
|
||||
protected:
|
||||
@ -182,7 +183,9 @@ namespace nana
|
||||
widget_object()
|
||||
: events_{ std::make_shared<Events>() },
|
||||
scheme_{ API::dev::make_scheme<Scheme>() }
|
||||
{}
|
||||
{
|
||||
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
|
||||
}
|
||||
|
||||
~widget_object()
|
||||
{
|
||||
@ -291,7 +294,7 @@ namespace nana
|
||||
};//end class widget_object
|
||||
|
||||
/// Base class of all the classes defined as a non-graphics-buffer widget window. The second template parameter DrawerTrigger is always ignored.\see nana::panel
|
||||
template<typename DrawerTrigger, typename Events, typename Scheme>
|
||||
template<typename DrawerTrigger, typename Events, typename Scheme> //type DrawerTrigger must be derived from nana::drawer_trigger
|
||||
class widget_object<category::lite_widget_tag, DrawerTrigger, Events, Scheme>: public detail::widget_base
|
||||
{
|
||||
protected:
|
||||
@ -302,7 +305,9 @@ namespace nana
|
||||
|
||||
widget_object()
|
||||
: events_{ std::make_shared<Events>() }, scheme_{ API::dev::make_scheme<scheme_type>() }
|
||||
{}
|
||||
{
|
||||
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
|
||||
}
|
||||
|
||||
~widget_object()
|
||||
{
|
||||
@ -355,7 +360,7 @@ namespace nana
|
||||
|
||||
|
||||
/// Base class of all the classes defined as a root window. \see nana::form
|
||||
template<typename DrawerTrigger, typename Events, typename Scheme>
|
||||
template<typename DrawerTrigger, typename Events, typename Scheme> //type DrawerTrigger must be derived from nana::drawer_trigger
|
||||
class widget_object<category::root_tag, DrawerTrigger, Events, Scheme>: public detail::widget_base
|
||||
{
|
||||
protected:
|
||||
@ -367,10 +372,12 @@ namespace nana
|
||||
widget_object()
|
||||
: widget_object(nullptr, false, API::make_center(300, 150), appearance(), this)
|
||||
{
|
||||
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
|
||||
}
|
||||
|
||||
widget_object(window owner, bool nested, const rectangle& r = {}, const appearance& apr = {})
|
||||
{
|
||||
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
|
||||
handle_ = API::dev::create_window(owner, nested, r, apr, this);
|
||||
_m_bind_and_attach();
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* An Implementation of i18n
|
||||
* 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
|
||||
|
||||
@ -193,7 +193,7 @@ namespace nana{ namespace pat{
|
||||
cwrapper_.reset();
|
||||
}
|
||||
|
||||
operator operator_bool_t() const volatile noexcept
|
||||
operator operator_bool_t() const noexcept
|
||||
{
|
||||
return (fast_ptr_ ? &inner_bool::true_stand : nullptr);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6)
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6)
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Weffc++"
|
||||
#endif
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Timepiece 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
|
||||
@ -13,6 +13,8 @@
|
||||
#ifndef NANA_SYSTEM_TIMEPIECE_HPP
|
||||
#define NANA_SYSTEM_TIMEPIECE_HPP
|
||||
|
||||
#include "../c++defines.hpp"
|
||||
|
||||
namespace nana
|
||||
{
|
||||
namespace system
|
||||
@ -21,11 +23,11 @@ namespace system
|
||||
{
|
||||
public:
|
||||
timepiece();
|
||||
timepiece(const volatile timepiece&);
|
||||
timepiece(const timepiece&);
|
||||
~timepiece();
|
||||
timepiece & operator=(const volatile timepiece &);
|
||||
void start() volatile; ///< Set the begin time.
|
||||
double calc() const volatile; ///< Get the intervals from the begin time.
|
||||
timepiece & operator=(const timepiece &);
|
||||
void start() noexcept; ///< Set the begin time.
|
||||
double calc() const noexcept; ///< Get the intervals from the begin time.
|
||||
private:
|
||||
struct impl_t;
|
||||
impl_t * impl_;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* A Thread Pool Implementation
|
||||
* 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
|
||||
@ -13,10 +13,14 @@
|
||||
#ifndef NANA_THREADS_POOL_HPP
|
||||
#define NANA_THREADS_POOL_HPP
|
||||
|
||||
#include <nana/c++defines.hpp>
|
||||
#include <nana/traits.hpp>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
|
||||
#ifndef STD_THREAD_NOT_SUPPORTED
|
||||
# include <thread>
|
||||
#endif
|
||||
|
||||
namespace nana{
|
||||
/// Some mutex classes for synchronizing.
|
||||
@ -58,9 +62,12 @@ namespace threads
|
||||
pool(const pool&) = delete;
|
||||
pool& operator=(const pool&) = delete;
|
||||
public:
|
||||
pool(); ///< Creates a group of threads.
|
||||
#ifndef STD_THREAD_NOT_SUPPORTED
|
||||
pool(unsigned thread_number = std::thread::hardware_concurrency()); ///< Creates a group of threads.
|
||||
#else
|
||||
pool(unsigned thread_number = 0);
|
||||
#endif
|
||||
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&&);
|
||||
|
||||
@ -71,6 +71,8 @@ namespace nana
|
||||
|
||||
std::vector<unicode_bidi::entity> unicode_reorder(const wchar_t* text, std::size_t length);
|
||||
|
||||
bool unicode_wordbreak(wchar_t left, wchar_t right);
|
||||
|
||||
}
|
||||
#include <nana/pop_ignore_diagnostic>
|
||||
|
||||
|
||||
@ -186,10 +186,12 @@ namespace nana
|
||||
throw std::invalid_argument(excpt_what);
|
||||
|
||||
std::vector<std::string> rgb;
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto const is_real = (rgb.emplace_back(i->str()).back() == '%');
|
||||
#else
|
||||
rgb.emplace_back(i->str());
|
||||
|
||||
const bool is_real = (rgb.back().back() == '%');
|
||||
#endif
|
||||
pat.assign(is_real ? "(\\d*\\.)?\\d+\\%" : "\\d+");
|
||||
|
||||
for (++i; i != end; ++i)
|
||||
@ -275,9 +277,13 @@ namespace nana
|
||||
{
|
||||
std::vector<std::string> rgb;
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto const is_real = (rgb.emplace_back(std::move(str)).back() == '%');
|
||||
#else
|
||||
rgb.emplace_back(std::move(str));
|
||||
|
||||
const bool is_real = (rgb.back().back() == '%');
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
|
||||
@ -175,12 +175,12 @@ namespace nana
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string to_utf8(const std::wstring& text)
|
||||
#ifdef _nana_std_has_string_view
|
||||
std::string to_utf8(std::wstring_view text)
|
||||
{
|
||||
return ::nana::charset(text).to_bytes(::nana::unicode::utf8);
|
||||
return ::nana::charset(std::wstring{text}).to_bytes(::nana::unicode::utf8);
|
||||
}
|
||||
|
||||
#ifdef _nana_std_has_string_view
|
||||
std::wstring to_wstring(std::string_view utf8_str)
|
||||
{
|
||||
if (utf8_str.empty())
|
||||
@ -189,6 +189,11 @@ namespace nana
|
||||
return ::nana::charset(std::string{ utf8_str.data(), utf8_str.size() }, unicode::utf8);
|
||||
}
|
||||
#else
|
||||
std::string to_utf8(const std::wstring& text)
|
||||
{
|
||||
return ::nana::charset(text).to_bytes(::nana::unicode::utf8);
|
||||
}
|
||||
|
||||
std::wstring to_wstring(const std::string& utf8_str)
|
||||
{
|
||||
return ::nana::charset(utf8_str, ::nana::unicode::utf8);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Platform Specification 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
|
||||
|
||||
@ -486,6 +486,7 @@ namespace detail
|
||||
atombase_.wm_protocols = ::XInternAtom(display_, "WM_PROTOCOLS", False);
|
||||
atombase_.wm_change_state = ::XInternAtom(display_, "WM_CHANGE_STATE", False);
|
||||
atombase_.wm_delete_window = ::XInternAtom(display_, "WM_DELETE_WINDOW", False);
|
||||
atombase_.net_frame_extents = ::XInternAtom(display_, "_NET_FRAME_EXTENTS", False);
|
||||
atombase_.net_wm_state = ::XInternAtom(display_, "_NET_WM_STATE", False);
|
||||
atombase_.net_wm_state_skip_taskbar = ::XInternAtom(display_, "_NET_WM_STATE_SKIP_TASKBAR", False);
|
||||
atombase_.net_wm_state_fullscreen = ::XInternAtom(display_, "_NET_WM_STATE_FULLSCREEN", False);
|
||||
@ -588,16 +589,59 @@ namespace detail
|
||||
}
|
||||
|
||||
//There are three members make_owner(), get_owner() and remove(),
|
||||
//they are maintain a table to discribe the owner of windows because the feature in X11, the
|
||||
//they are maintain a table to discribe the owner of windows because of the feature in X11, the
|
||||
//owner of top level window must be RootWindow.
|
||||
void platform_spec::make_owner(native_window_type owner, native_window_type wd)
|
||||
{
|
||||
platform_scope_guard psg;
|
||||
platform_scope_guard lock;
|
||||
wincontext_[wd].owner = owner;
|
||||
window_context_t & context = wincontext_[owner];
|
||||
if(context.owned == 0)
|
||||
context.owned = new std::vector<native_window_type>;
|
||||
context.owned->push_back(wd);
|
||||
|
||||
auto& owner_ctx = wincontext_[owner];
|
||||
if(!owner_ctx.owned)
|
||||
owner_ctx.owned = new std::vector<native_window_type>;
|
||||
owner_ctx.owned->push_back(wd);
|
||||
}
|
||||
|
||||
bool platform_spec::umake_owner(native_window_type child)
|
||||
{
|
||||
platform_scope_guard lock;
|
||||
|
||||
auto i = wincontext_.find(child);
|
||||
if(i == wincontext_.end())
|
||||
return false;
|
||||
|
||||
if(i->second.owner)
|
||||
{
|
||||
auto u = wincontext_.find(i->second.owner);
|
||||
if(u != wincontext_.end())
|
||||
{
|
||||
auto * owned = u->second.owned;
|
||||
if(owned)
|
||||
{
|
||||
auto j = std::find(owned->begin(), owned->end(), child);
|
||||
if(j != owned->end())
|
||||
owned->erase(j);
|
||||
|
||||
if(owned->empty())
|
||||
{
|
||||
delete owned;
|
||||
u->second.owned = nullptr;
|
||||
//The owner owns no child. If it is not a child of other owners,
|
||||
//remove it.
|
||||
if(nullptr == u->second.owner)
|
||||
wincontext_.erase(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i->second.owner = nullptr;
|
||||
}
|
||||
|
||||
//Don't remove the ownerships which the child is a owner window.
|
||||
if(nullptr == i->second.owned)
|
||||
wincontext_.erase(i);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
native_window_type platform_spec::get_owner(native_window_type wd) const
|
||||
@ -610,37 +654,28 @@ namespace detail
|
||||
void platform_spec::remove(native_window_type wd)
|
||||
{
|
||||
msg_dispatcher_->erase(reinterpret_cast<Window>(wd));
|
||||
platform_scope_guard psg;
|
||||
auto i = wincontext_.find(wd);
|
||||
if(i == wincontext_.end()) return;
|
||||
|
||||
if(i->second.owner)
|
||||
platform_scope_guard lock;
|
||||
if(umake_owner(wd))
|
||||
{
|
||||
auto u = wincontext_.find(i->second.owner);
|
||||
if(u != wincontext_.end())
|
||||
auto i = wincontext_.find(wd);
|
||||
if(i != wincontext_.end())
|
||||
{
|
||||
auto * vec = u->second.owned;
|
||||
if(vec)
|
||||
if(i->second.owned)
|
||||
{
|
||||
auto j = std::find(vec->begin(), vec->end(), i->first);
|
||||
if(j != vec->end())
|
||||
vec->erase(j);
|
||||
set_error_handler();
|
||||
auto & wd_manager = detail::bedrock::instance().wd_manager();
|
||||
for(auto u = i->second.owned->rbegin(); u != i->second.owned->rend(); ++u)
|
||||
wd_manager.close(wd_manager.root(*u));
|
||||
|
||||
rev_error_handler();
|
||||
|
||||
delete i->second.owned;
|
||||
}
|
||||
|
||||
wincontext_.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
auto * vec = i->second.owned;
|
||||
if(vec)
|
||||
{
|
||||
set_error_handler();
|
||||
auto & wd_manager = detail::bedrock::instance().wd_manager();
|
||||
for(auto u = vec->rbegin(); u != vec->rend(); ++u)
|
||||
wd_manager.close(wd_manager.root(*u));
|
||||
|
||||
rev_error_handler();
|
||||
}
|
||||
delete vec;
|
||||
wincontext_.erase(i);
|
||||
iconbase_.erase(wd);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Platform Specification 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
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Message Dispatcher 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
|
||||
@ -26,6 +26,7 @@
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
namespace nana
|
||||
{
|
||||
@ -221,6 +222,8 @@ namespace detail
|
||||
case 0:
|
||||
msg_pack.kind = msg_pack.kind_xevent;
|
||||
msg_pack.u.xevent = event;
|
||||
_m_msg_dispatch(msg_pack);
|
||||
break;
|
||||
case 1:
|
||||
_m_msg_dispatch(msg_pack);
|
||||
}
|
||||
@ -338,7 +341,7 @@ namespace detail
|
||||
|
||||
private:
|
||||
Display * display_;
|
||||
volatile bool is_work_{ false };
|
||||
std::atomic<bool> is_work_{ false };
|
||||
std::unique_ptr<std::thread> thrd_;
|
||||
|
||||
struct table_tag
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Platform Specification 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,6 +20,7 @@
|
||||
|
||||
#include <nana/push_ignore_diagnostic>
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
@ -132,6 +133,7 @@ namespace detail
|
||||
Atom wm_change_state;
|
||||
Atom wm_delete_window;
|
||||
//ext
|
||||
Atom net_frame_extents;
|
||||
Atom net_wm_state;
|
||||
Atom net_wm_state_skip_taskbar;
|
||||
Atom net_wm_state_fullscreen;
|
||||
@ -213,6 +215,9 @@ namespace detail
|
||||
const atombase_tag & atombase() const;
|
||||
|
||||
void make_owner(native_window_type owner, native_window_type wd);
|
||||
|
||||
// Cancel the ownership
|
||||
bool umake_owner(native_window_type child);
|
||||
native_window_type get_owner(native_window_type) const;
|
||||
void remove(native_window_type);
|
||||
|
||||
@ -264,7 +269,7 @@ namespace detail
|
||||
std::recursive_mutex xlib_locker_;
|
||||
struct caret_holder_tag
|
||||
{
|
||||
volatile bool exit_thread;
|
||||
std::atomic<bool> exit_thread;
|
||||
std::unique_ptr<std::thread> thr;
|
||||
std::map<native_window_type, caret_rep*> carets;
|
||||
}caret_holder_;
|
||||
|
||||
@ -308,11 +308,11 @@ namespace nana
|
||||
{
|
||||
if (pi_data_->menu.window && (pi_data_->menu.window != wd))
|
||||
{
|
||||
wd = native_interface::get_owner_window(wd);
|
||||
wd = native_interface::get_window(wd, window_relationship::owner);
|
||||
while (wd)
|
||||
{
|
||||
if (wd != pi_data_->menu.window)
|
||||
wd = native_interface::get_owner_window(wd);
|
||||
wd = native_interface::get_window(wd, window_relationship::owner);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@ -329,7 +329,7 @@ namespace nana
|
||||
erase_menu(true);
|
||||
|
||||
pi_data_->menu.window = menu_wd;
|
||||
pi_data_->menu.owner = native_interface::get_owner_window(menu_wd);
|
||||
pi_data_->menu.owner = native_interface::get_window(menu_wd, window_relationship::owner);
|
||||
pi_data_->menu.has_keyboard = has_keyboard;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,9 @@ namespace nana
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
//Declarations of helper functions defined in native_window_interface.cpp
|
||||
void x11_apply_exposed_position(native_window_type wd);
|
||||
|
||||
#pragma pack(1)
|
||||
union event_mask
|
||||
{
|
||||
@ -572,6 +575,62 @@ namespace detail
|
||||
return true;
|
||||
}
|
||||
|
||||
void x_lookup_chars(const root_misc* rruntime, basic_window * msgwd, char* keybuf, std::size_t keybuf_len, const arg_keyboard& modifiers_status)
|
||||
{
|
||||
if (!msgwd->flags.enabled)
|
||||
return;
|
||||
|
||||
static auto& brock = detail::bedrock::instance();
|
||||
auto & wd_manager = brock.wd_manager();
|
||||
|
||||
auto& context = *brock.get_thread_context(msgwd->thread_id);
|
||||
|
||||
auto const native_window = rruntime->window->root;
|
||||
|
||||
|
||||
nana::detail::charset_conv charset(NANA_UNICODE, "UTF-8");
|
||||
const std::string& str = charset.charset(std::string(keybuf, keybuf + keybuf_len));
|
||||
auto const charbuf = reinterpret_cast<const wchar_t*>(str.c_str());
|
||||
auto const len = str.size() / sizeof(wchar_t);
|
||||
|
||||
for(std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
arg_keyboard arg = modifiers_status;
|
||||
arg.ignore = false;
|
||||
arg.key = charbuf[i];
|
||||
|
||||
// ignore Unicode BOM (it may or may not appear)
|
||||
if (arg.key == 0xFEFF) continue;
|
||||
|
||||
//Only accept tab when it is not ignored.
|
||||
if ((keyboard::tab == arg.key) && rruntime->condition.ignore_tab)
|
||||
continue;
|
||||
|
||||
if(context.is_alt_pressed)
|
||||
{
|
||||
arg.ctrl = arg.shift = false;
|
||||
arg.evt_code = event_code::shortkey;
|
||||
brock.shortkey_occurred(true);
|
||||
auto shr_wd = wd_manager.find_shortkey(native_window, arg.key);
|
||||
if(shr_wd)
|
||||
{
|
||||
arg.window_handle = reinterpret_cast<window>(shr_wd);
|
||||
brock.emit(event_code::shortkey, shr_wd, arg, true, &context);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
arg.evt_code = event_code::key_char;
|
||||
arg.window_handle = reinterpret_cast<window>(msgwd);
|
||||
msgwd->annex.events_ptr->key_char.emit(arg, reinterpret_cast<window>(msgwd));
|
||||
if(arg.ignore == false && wd_manager.available(msgwd))
|
||||
draw_invoker(&drawer::key_char, msgwd, arg, &context);
|
||||
}
|
||||
|
||||
if(brock.shortkey_occurred(false))
|
||||
context.is_alt_pressed = false;
|
||||
}
|
||||
|
||||
void window_proc_for_xevent(Display* /*display*/, XEvent& xevent)
|
||||
{
|
||||
typedef detail::bedrock::core_window_t core_window_t;
|
||||
@ -905,6 +964,9 @@ namespace detail
|
||||
break;
|
||||
case MapNotify:
|
||||
case UnmapNotify:
|
||||
if(xevent.type == MapNotify)
|
||||
x11_apply_exposed_position(native_window);
|
||||
|
||||
brock.event_expose(msgwnd, (xevent.type == MapNotify));
|
||||
context.platform.motion_window = nullptr;
|
||||
break;
|
||||
@ -1071,52 +1133,10 @@ namespace detail
|
||||
wd_manager.do_lazy_refresh(msgwnd, false);
|
||||
break;
|
||||
}
|
||||
x_lookup_chars(root_runtime, msgwnd, keybuf, len, modifiers_status);
|
||||
break;
|
||||
case XLookupChars:
|
||||
if (msgwnd->flags.enabled)
|
||||
{
|
||||
const wchar_t* charbuf;
|
||||
|
||||
nana::detail::charset_conv charset(NANA_UNICODE, "UTF-8");
|
||||
const std::string& str = charset.charset(std::string(keybuf, keybuf + len));
|
||||
charbuf = reinterpret_cast<const wchar_t*>(str.c_str());
|
||||
len = str.size() / sizeof(wchar_t);
|
||||
|
||||
for(int i = 0; i < len; ++i)
|
||||
{
|
||||
arg_keyboard arg = modifiers_status;
|
||||
arg.ignore = false;
|
||||
arg.key = charbuf[i];
|
||||
|
||||
// ignore Unicode BOM (it may or may not appear)
|
||||
if (arg.key == 0xFEFF) continue;
|
||||
|
||||
//Only accept tab when it is not ignored.
|
||||
if ((keyboard::tab == arg.key) && root_runtime->condition.ignore_tab)
|
||||
continue;
|
||||
|
||||
if(context.is_alt_pressed)
|
||||
{
|
||||
arg.ctrl = arg.shift = false;
|
||||
arg.evt_code = event_code::shortkey;
|
||||
brock.shortkey_occurred(true);
|
||||
auto shr_wd = wd_manager.find_shortkey(native_window, arg.key);
|
||||
if(shr_wd)
|
||||
{
|
||||
arg.window_handle = reinterpret_cast<window>(shr_wd);
|
||||
brock.emit(event_code::shortkey, shr_wd, arg, true, &context);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
arg.evt_code = event_code::key_char;
|
||||
arg.window_handle = reinterpret_cast<window>(msgwnd);
|
||||
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);
|
||||
}
|
||||
|
||||
if(brock.shortkey_occurred(false))
|
||||
context.is_alt_pressed = false;
|
||||
}
|
||||
x_lookup_chars(root_runtime, msgwnd, keybuf, len, modifiers_status);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1281,7 +1301,7 @@ namespace detail
|
||||
if(condition_wd && is_modal)
|
||||
{
|
||||
native_window_type modal = reinterpret_cast<core_window_t*>(condition_wd)->root;
|
||||
owner_native = native_interface::get_owner_window(modal);
|
||||
owner_native = native_interface::get_window(modal, window_relationship::owner);
|
||||
if(owner_native)
|
||||
{
|
||||
native_interface::enable_window(owner_native, false);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Platform 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
|
||||
@ -129,6 +129,96 @@ namespace nana{
|
||||
{
|
||||
nana::detail::platform_spec & spec = nana::detail::platform_spec::instance();
|
||||
}
|
||||
|
||||
|
||||
//The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is
|
||||
//unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that
|
||||
//set to a unmapped windows should be kept and use the position when the window is mapped.
|
||||
std::map<Window, ::nana::point> exposed_positions; //locked by platform_scope_guard
|
||||
|
||||
//Returns the parent window.
|
||||
//It may return a decoration frame window if the requested window is a top level and WM is a
|
||||
//reparenting window manager.
|
||||
native_window_type x11_parent_window(native_window_type wd)
|
||||
{
|
||||
Window root;
|
||||
Window parent;
|
||||
Window * children;
|
||||
unsigned size;
|
||||
|
||||
platform_scope_guard lock;
|
||||
|
||||
if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast<Window>(wd),
|
||||
&root, &parent, &children, &size))
|
||||
{
|
||||
::XFree(children);
|
||||
return reinterpret_cast<native_window_type>(parent);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
native_window_type x11_decoration_frame(native_window_type wd)
|
||||
{
|
||||
auto const owner = restrict::spec.get_owner(wd);
|
||||
auto const root_wd = restrict::spec.root_window();
|
||||
|
||||
if(owner)
|
||||
{
|
||||
auto test_wd = wd;
|
||||
while(true)
|
||||
{
|
||||
auto upper = x11_parent_window(test_wd);
|
||||
if((reinterpret_cast<Window>(upper) != root_wd) && (upper != owner))
|
||||
{
|
||||
test_wd = upper;
|
||||
}
|
||||
else if(wd != test_wd)
|
||||
return test_wd;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void x11_apply_exposed_position(native_window_type wd)
|
||||
{
|
||||
nana::detail::platform_scope_guard lock;
|
||||
|
||||
auto i = exposed_positions.find(reinterpret_cast<Window>(wd));
|
||||
if(i == exposed_positions.cend())
|
||||
return;
|
||||
|
||||
native_interface::move_window(wd, i->second.x, i->second.y);
|
||||
|
||||
exposed_positions.erase(i);
|
||||
}
|
||||
|
||||
namespace x11_wait
|
||||
{
|
||||
static Bool configure(Display *disp, XEvent *evt, char *arg)
|
||||
{
|
||||
return disp && evt && arg && (evt->type == ConfigureNotify) && (evt->xconfigure.window == *reinterpret_cast<Window*>(arg));
|
||||
}
|
||||
|
||||
static Bool map(Display *disp, XEvent *evt, char *arg)
|
||||
{
|
||||
return disp && evt && arg && (evt->type == MapNotify) && (evt->xmap.window == *reinterpret_cast<Window*>(arg));
|
||||
}
|
||||
|
||||
static Bool unmap(Display *disp, XEvent *evt, char *arg)
|
||||
{
|
||||
return disp && evt && arg && (evt->type == MapNotify) && (evt->xunmap.window == *reinterpret_cast<Window*>(arg));
|
||||
}
|
||||
}
|
||||
|
||||
static void x11_wait_for(Window wd, Bool(*pred_fn)(Display*, XEvent*, char*))
|
||||
{
|
||||
XEvent dummy;
|
||||
::XPeekIfEvent(restrict::spec.open_display(), &dummy, pred_fn, reinterpret_cast<XPointer>(&wd));
|
||||
}
|
||||
#endif
|
||||
|
||||
//struct native_interface
|
||||
@ -196,13 +286,6 @@ namespace nana{
|
||||
return rectangle{ primary_monitor_size() };
|
||||
}
|
||||
|
||||
#ifdef NANA_X11
|
||||
//The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is
|
||||
//unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that
|
||||
//set to a unmapped windows should be kept and use the position when the window is mapped.
|
||||
std::map<Window, ::nana::point> exposed_positions; //locked by platform_scope_guard
|
||||
#endif
|
||||
|
||||
//platform-dependent
|
||||
native_interface::window_result native_interface::create_window(native_window_type owner, bool nested, const rectangle& r, const appearance& app)
|
||||
{
|
||||
@ -267,7 +350,7 @@ namespace nana{
|
||||
|
||||
XSetWindowAttributes win_attr;
|
||||
unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
|
||||
CWWinGravity | CWBitGravity | CWColormap | CWEventMask;
|
||||
CWColormap | CWEventMask;
|
||||
|
||||
Display * disp = restrict::spec.open_display();
|
||||
win_attr.colormap = restrict::spec.colormap();
|
||||
@ -276,8 +359,6 @@ namespace nana{
|
||||
win_attr.background_pixel = 0xFFFFFF;
|
||||
win_attr.border_pixmap = None;
|
||||
win_attr.border_pixel = 0x0;
|
||||
win_attr.bit_gravity = 0;
|
||||
win_attr.win_gravity = NorthWestGravity;
|
||||
win_attr.backing_store = 0;
|
||||
win_attr.backing_planes = 0;
|
||||
win_attr.backing_pixel = 0;
|
||||
@ -296,6 +377,8 @@ namespace nana{
|
||||
win_attr.save_under = True;
|
||||
attr_mask |= CWSaveUnder;
|
||||
|
||||
///The parameter of XCreateWindow to create a top level window must be root.
|
||||
///But after creation, the real parent is the reparenting frame window.
|
||||
parent = restrict::spec.root_window();
|
||||
calc_screen_point(owner, pos);
|
||||
}
|
||||
@ -309,9 +392,10 @@ namespace nana{
|
||||
if(handle)
|
||||
{
|
||||
//make owner if it is a popup window
|
||||
if((!nested) && owner)
|
||||
if(!nested)
|
||||
{
|
||||
restrict::spec.make_owner(owner, reinterpret_cast<native_window_type>(handle));
|
||||
auto origin_owner = (owner ? owner : reinterpret_cast<native_window_type>(restrict::spec.root_window()));
|
||||
restrict::spec.make_owner(origin_owner, reinterpret_cast<native_window_type>(handle));
|
||||
exposed_positions[handle] = pos;
|
||||
}
|
||||
|
||||
@ -421,7 +505,7 @@ namespace nana{
|
||||
|
||||
XSetWindowAttributes win_attr;
|
||||
unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
|
||||
CWWinGravity | CWBitGravity | CWColormap | CWEventMask;
|
||||
CWColormap | CWEventMask | CWOverrideRedirect;
|
||||
|
||||
Display * disp = restrict::spec.open_display();
|
||||
win_attr.colormap = restrict::spec.colormap();
|
||||
@ -430,15 +514,12 @@ namespace nana{
|
||||
win_attr.background_pixel = 0xFFFFFF;
|
||||
win_attr.border_pixmap = None;
|
||||
win_attr.border_pixel = 0x0;
|
||||
win_attr.bit_gravity = 0;
|
||||
win_attr.win_gravity = NorthWestGravity;
|
||||
win_attr.backing_store = 0;
|
||||
win_attr.backing_planes = 0;
|
||||
win_attr.backing_pixel = 0;
|
||||
win_attr.colormap = restrict::spec.colormap();
|
||||
|
||||
win_attr.override_redirect = True;
|
||||
attr_mask |= CWOverrideRedirect;
|
||||
|
||||
nana::point pos(r.x, r.y);
|
||||
win_attr.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ExposureMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask;
|
||||
@ -664,25 +745,33 @@ namespace nana{
|
||||
{
|
||||
nana::detail::platform_scope_guard psg;
|
||||
Display* disp = restrict::spec.open_display();
|
||||
|
||||
//Returns if the requested visibility is same with the current status.
|
||||
//In some X-Server versions/implementations, XMapWindow() doesn't generate
|
||||
//a ConfigureNotify if the requested visibility is same with the current status.
|
||||
//It causes that x11_wait_for always waiting for the ConfigureNotify.
|
||||
if(show == is_window_visible(wd))
|
||||
return;
|
||||
|
||||
if(show)
|
||||
{
|
||||
::XMapWindow(disp, reinterpret_cast<Window>(wd));
|
||||
|
||||
auto i = exposed_positions.find(reinterpret_cast<Window>(wd));
|
||||
if(i != exposed_positions.end())
|
||||
{
|
||||
::XMoveWindow(disp, reinterpret_cast<Window>(wd), i->second.x, i->second.y);
|
||||
exposed_positions.erase(i);
|
||||
}
|
||||
//Wait for the mapping notify to update the local attribute of visibility so that
|
||||
//the followed window_visible() call can return the updated visibility value.
|
||||
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::map);
|
||||
|
||||
Window grab = restrict::spec.grab(0);
|
||||
if(grab == reinterpret_cast<Window>(wd))
|
||||
capture_window(wd, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
::XUnmapWindow(disp, reinterpret_cast<Window>(wd));
|
||||
|
||||
::XFlush(disp);
|
||||
//Wait for the mapping notify to update the local attribute of visibility so that
|
||||
//the followed window_visible() call can return the updated visibility value.
|
||||
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::unmap);
|
||||
}
|
||||
}
|
||||
static_cast<void>(active); //eliminate unused parameter compiler warning.
|
||||
#endif
|
||||
@ -844,19 +933,33 @@ namespace nana{
|
||||
}
|
||||
return nana::point(r.left, r.top);
|
||||
#elif defined(NANA_X11)
|
||||
int x, y;
|
||||
nana::detail::platform_scope_guard psg;
|
||||
Window coord_wd = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
|
||||
if(!coord_wd)
|
||||
point scr_pos;
|
||||
nana::detail::platform_scope_guard lock;
|
||||
|
||||
|
||||
point origin{};
|
||||
|
||||
auto coord_wd = restrict::spec.get_owner(wd);
|
||||
if(coord_wd)
|
||||
{
|
||||
coord_wd = reinterpret_cast<Window>(parent_window(wd));
|
||||
if(!coord_wd)
|
||||
coord_wd = restrict::spec.root_window();
|
||||
auto fm_extents = window_frame_extents(wd);
|
||||
origin.x = -fm_extents.left;
|
||||
origin.y = -fm_extents.top;
|
||||
|
||||
if(reinterpret_cast<Window>(coord_wd) != restrict::spec.root_window())
|
||||
{
|
||||
fm_extents = window_frame_extents(coord_wd);
|
||||
origin.x += fm_extents.left;
|
||||
origin.y += fm_extents.top;
|
||||
}
|
||||
}
|
||||
else
|
||||
coord_wd = get_window(wd, window_relationship::parent);
|
||||
|
||||
Window child;
|
||||
if(True == ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast<Window>(wd), coord_wd, 0, 0, &x, &y, &child))
|
||||
return nana::point(x, y);
|
||||
return nana::point(0, 0);
|
||||
::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast<Window>(wd), reinterpret_cast<Window>(coord_wd), origin.x, origin.y, &scr_pos.x, &scr_pos.y, &child);
|
||||
|
||||
return scr_pos;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -887,13 +990,15 @@ namespace nana{
|
||||
#elif defined(NANA_X11)
|
||||
Display * disp = restrict::spec.open_display();
|
||||
|
||||
nana::detail::platform_scope_guard psg;
|
||||
Window owner = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
|
||||
if(owner)
|
||||
nana::detail::platform_scope_guard lock;
|
||||
|
||||
if(point{x, y} == window_position(wd))
|
||||
{
|
||||
Window child;
|
||||
::XTranslateCoordinates(disp, owner, restrict::spec.root_window(),
|
||||
x, y, &x, &y, &child);
|
||||
//Returns if the requested position is same with the current position.
|
||||
//In some X-Server versions/implementations, XMoveWindow() doesn't generate
|
||||
//a ConfigureNotify if the requested position is same with the current position.
|
||||
//It causes that x11_wait_for always waiting for the ConfigureNotify.
|
||||
return;
|
||||
}
|
||||
|
||||
XWindowAttributes attr;
|
||||
@ -901,7 +1006,19 @@ namespace nana{
|
||||
if(attr.map_state == IsUnmapped)
|
||||
exposed_positions[reinterpret_cast<Window>(wd)] = ::nana::point{x, y};
|
||||
|
||||
auto const owner = restrict::spec.get_owner(wd);
|
||||
if(owner && (owner != reinterpret_cast<native_window_type>(restrict::spec.root_window())))
|
||||
{
|
||||
auto origin = window_position(owner);
|
||||
x += origin.x;
|
||||
y += origin.y;
|
||||
}
|
||||
|
||||
::XMoveWindow(disp, reinterpret_cast<Window>(wd), x, y);
|
||||
|
||||
//Wait for the configuration notify to update the local attribute of position so that
|
||||
//the followed window_position() call can return the updated position value.
|
||||
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::configure);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -941,6 +1058,16 @@ namespace nana{
|
||||
XSizeHints hints;
|
||||
nana::detail::platform_scope_guard psg;
|
||||
|
||||
|
||||
//Returns if the requested rectangle is same with the current rectangle.
|
||||
//In some X-Server versions/implementations, XMapWindow() doesn't generate
|
||||
//a ConfigureNotify if the requested rectangle is same with the current rectangle.
|
||||
//It causes that x11_wait_for always waiting for the ConfigureNotify.
|
||||
rectangle current_r;
|
||||
get_window_rect(wd, current_r);
|
||||
if(r == current_r)
|
||||
return true;
|
||||
|
||||
::XGetWMNormalHints(disp, reinterpret_cast<Window>(wd), &hints, &supplied);
|
||||
if((hints.flags & (PMinSize | PMaxSize)) && (hints.min_width == hints.max_width) && (hints.min_height == hints.max_height))
|
||||
{
|
||||
@ -951,16 +1078,6 @@ namespace nana{
|
||||
else
|
||||
hints.flags = 0;
|
||||
|
||||
Window owner = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
|
||||
int x = r.x;
|
||||
int y = r.y;
|
||||
if(owner)
|
||||
{
|
||||
Window child;
|
||||
::XTranslateCoordinates(disp, owner, restrict::spec.root_window(),
|
||||
r.x, r.y, &x, &y, &child);
|
||||
}
|
||||
|
||||
XWindowAttributes attr;
|
||||
::XGetWindowAttributes(disp, reinterpret_cast<Window>(wd), &attr);
|
||||
if(attr.map_state == IsUnmapped)
|
||||
@ -969,13 +1086,32 @@ namespace nana{
|
||||
hints.width = r.width;
|
||||
hints.height = r.height;
|
||||
|
||||
exposed_positions[reinterpret_cast<Window>(wd)] = point{x, y};
|
||||
exposed_positions[reinterpret_cast<Window>(wd)] = r.position();
|
||||
}
|
||||
|
||||
if(hints.flags)
|
||||
::XSetWMNormalHints(disp, reinterpret_cast<Window>(wd), &hints);
|
||||
|
||||
int x = r.x;
|
||||
int y = r.y;
|
||||
|
||||
auto const owner = restrict::spec.get_owner(wd);
|
||||
if(owner && (owner != reinterpret_cast<native_window_type>(restrict::spec.root_window())))
|
||||
{
|
||||
auto origin = window_position(owner);
|
||||
x += origin.x;
|
||||
y += origin.y;
|
||||
}
|
||||
|
||||
::XMoveResizeWindow(disp, reinterpret_cast<Window>(wd), x, y, r.width, r.height);
|
||||
|
||||
//Wait for the configuration notify to update the local attribute of position so that
|
||||
//the followed window_position() call can return the updated position value.
|
||||
|
||||
//It seems that XMoveResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called
|
||||
//to make sure the local attribute is updated.
|
||||
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::configure);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@ -1055,6 +1191,46 @@ namespace nana{
|
||||
#endif
|
||||
}
|
||||
|
||||
native_interface::frame_extents native_interface::window_frame_extents(native_window_type wd)
|
||||
{
|
||||
frame_extents fm_extents{0, 0, 0, 0};
|
||||
|
||||
#if defined(NANA_WINDOWS)
|
||||
::RECT client;
|
||||
::GetClientRect(reinterpret_cast<HWND>(wd), &client); //The right and bottom of client by GetClientRect indicate the width and height of the area
|
||||
::RECT wd_area;
|
||||
::GetWindowRect(reinterpret_cast<HWND>(wd), &wd_area);
|
||||
|
||||
fm_extents.left = client.left - wd_area.left;
|
||||
fm_extents.right = wd_area.right - client.right;
|
||||
fm_extents.top = client.top - wd_area.top;
|
||||
fm_extents.bottom = wd_area.bottom - client.bottom;
|
||||
#elif defined(NANA_X11)
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long len, bytes_left = 0;
|
||||
unsigned char *data;
|
||||
|
||||
nana::detail::platform_scope_guard lock;
|
||||
if(Success == ::XGetWindowProperty(restrict::spec.open_display(), reinterpret_cast<Window>(wd),
|
||||
restrict::spec.atombase().net_frame_extents, 0, 16, 0,
|
||||
XA_CARDINAL, &type, &format,
|
||||
&len, &bytes_left, &data))
|
||||
{
|
||||
if(type != None && len == 4)
|
||||
{
|
||||
fm_extents.left = ((long*)data)[0];
|
||||
fm_extents.right = ((long*)data)[1];
|
||||
fm_extents.top = ((long*)data)[2];
|
||||
fm_extents.bottom = ((long*)data)[3];
|
||||
}
|
||||
::XFree(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
return fm_extents;
|
||||
}
|
||||
|
||||
bool native_interface::window_size(native_window_type wd, const size& sz)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
@ -1081,6 +1257,15 @@ namespace nana{
|
||||
auto disp = restrict::spec.open_display();
|
||||
nana::detail::platform_scope_guard psg;
|
||||
|
||||
//Returns if the requested size is same with the current size.
|
||||
//In some X-Server versions/implementations, XMapWindow() doesn't generate
|
||||
//a ConfigureNotify if the requested size is same with the current size.
|
||||
//It causes that x11_wait_for always waiting for the ConfigureNotify.
|
||||
rectangle current_r;
|
||||
get_window_rect(wd, current_r);
|
||||
if(current_r.dimension() == sz)
|
||||
return true;
|
||||
|
||||
//Check the XSizeHints for testing whether the window is sizable.
|
||||
XSizeHints hints;
|
||||
long supplied;
|
||||
@ -1093,6 +1278,10 @@ namespace nana{
|
||||
::XSetWMNormalHints(disp, reinterpret_cast<Window>(wd), &hints);
|
||||
}
|
||||
::XResizeWindow(disp, reinterpret_cast<Window>(wd), sz.width, sz.height);
|
||||
|
||||
//It seems that XResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called
|
||||
//to make sure the local attribute is updated.
|
||||
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::configure);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@ -1112,6 +1301,9 @@ namespace nana{
|
||||
unsigned border, depth;
|
||||
nana::detail::platform_scope_guard psg;
|
||||
::XGetGeometry(restrict::spec.open_display(), reinterpret_cast<Window>(wd), &root, &x, &y, &r.width, &r.height, &border, &depth);
|
||||
|
||||
auto pos = window_position(wd);
|
||||
r.position(pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1222,41 +1414,37 @@ namespace nana{
|
||||
Window drop_wd;
|
||||
int x, y;
|
||||
unsigned mask;
|
||||
nana::detail::platform_scope_guard psg;
|
||||
nana::detail::platform_scope_guard lock;
|
||||
::XQueryPointer(restrict::spec.open_display(), restrict::spec.root_window(), &drop_wd, &drop_wd, &pos.x, &pos.y, &x, &y, &mask);
|
||||
return pos;
|
||||
#endif
|
||||
}
|
||||
|
||||
native_window_type native_interface::get_owner_window(native_window_type wd)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
return reinterpret_cast<native_window_type>(::GetWindow(reinterpret_cast<HWND>(wd), GW_OWNER));
|
||||
#elif defined(NANA_X11)
|
||||
return restrict::spec.get_owner(wd);
|
||||
#endif
|
||||
}
|
||||
|
||||
native_window_type native_interface::parent_window(native_window_type wd)
|
||||
native_window_type native_interface::get_window(native_window_type wd, window_relationship rsp)
|
||||
{
|
||||
#ifdef NANA_WINDOWS
|
||||
return reinterpret_cast<native_window_type>(::GetParent(reinterpret_cast<HWND>(wd)));
|
||||
if(window_relationship::either_po == rsp)
|
||||
return reinterpret_cast<native_window_type>(::GetParent(reinterpret_cast<HWND>(wd)));
|
||||
else if(window_relationship::parent == rsp)
|
||||
return reinterpret_cast<native_window_type>(::GetAncestor(reinterpret_cast<HWND>(wd), GA_PARENT));
|
||||
else if(window_relationship::owner == rsp)
|
||||
return reinterpret_cast<native_window_type>(::GetWindow(reinterpret_cast<HWND>(wd), GW_OWNER));
|
||||
#elif defined(NANA_X11)
|
||||
Window root;
|
||||
Window parent;
|
||||
Window * children;
|
||||
unsigned size;
|
||||
|
||||
platform_scope_guard lock;
|
||||
|
||||
if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast<Window>(wd),
|
||||
&root, &parent, &children, &size))
|
||||
auto owner = restrict::spec.get_owner(wd);
|
||||
|
||||
if(window_relationship::either_po == rsp)
|
||||
{
|
||||
::XFree(children);
|
||||
return reinterpret_cast<native_window_type>(parent);
|
||||
if(owner)
|
||||
return owner;
|
||||
}
|
||||
return nullptr;
|
||||
else if(window_relationship::owner == rsp)
|
||||
return owner;
|
||||
else if(window_relationship::parent == rsp)
|
||||
return x11_parent_window(wd);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
native_window_type native_interface::parent_window(native_window_type child, native_window_type new_parent, bool returns_previous)
|
||||
@ -1276,11 +1464,21 @@ namespace nana{
|
||||
platform_scope_guard lock;
|
||||
|
||||
if(returns_previous)
|
||||
prev = parent_window(child);
|
||||
prev = get_window(child, window_relationship::either_po);
|
||||
|
||||
if(native_window_type{} == new_parent)
|
||||
new_parent = reinterpret_cast<native_window_type>(restrict::spec.root_window());
|
||||
|
||||
::XReparentWindow(restrict::spec.open_display(),
|
||||
reinterpret_cast<Window>(child), reinterpret_cast<Window>(new_parent),
|
||||
0, 0);
|
||||
|
||||
|
||||
// If umake_owner returns true, it indicates the child windows is a popup window.
|
||||
// So make the ownership of new_parent and child.
|
||||
if(restrict::spec.umake_owner(child))
|
||||
restrict::spec.make_owner(new_parent, child);
|
||||
|
||||
return prev;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -166,10 +166,18 @@ namespace nana
|
||||
if (!cover->visible)
|
||||
continue;
|
||||
|
||||
if (is_wd_root ?
|
||||
(category::flags::root == cover->other.category)
|
||||
:
|
||||
((category::flags::root != cover->other.category) && (nullptr == cover->effect.bground)))
|
||||
if (is_wd_root)
|
||||
{
|
||||
if(category::flags::root == cover->other.category)
|
||||
{
|
||||
if (overlap(vis_rect, rectangle{ native_interface::window_position(cover->root), cover->dimension }, block.r))
|
||||
{
|
||||
block.window = cover;
|
||||
blocks.push_back(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if((category::flags::root != cover->other.category) && (nullptr == cover->effect.bground))
|
||||
{
|
||||
if (overlap(vis_rect, rectangle{ cover->pos_root, cover->dimension }, block.r))
|
||||
{
|
||||
|
||||
@ -85,8 +85,12 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & rep = impl_->base.emplace_back();
|
||||
#else
|
||||
impl_->base.emplace_back();
|
||||
auto & rep = impl_->base.back();
|
||||
#endif
|
||||
rep.handle = wd;
|
||||
rep.keys.emplace_back(key);
|
||||
|
||||
@ -242,8 +246,12 @@ namespace detail
|
||||
return kv.second;
|
||||
}
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
return table_.emplace_back(key).second;
|
||||
#else
|
||||
table_.emplace_back(key);
|
||||
return table_.back().second;
|
||||
#endif
|
||||
}
|
||||
|
||||
iterator find(const Key& key)
|
||||
@ -950,6 +958,19 @@ namespace detail
|
||||
if (wd->dimension == sz)
|
||||
return false;
|
||||
|
||||
std::vector<core_window_t*> presence;
|
||||
|
||||
if (wd->dimension.width < sz.width || wd->dimension.height < sz.height)
|
||||
{
|
||||
auto wd_r = rectangle{ wd->dimension };
|
||||
for (auto child : wd->children)
|
||||
{
|
||||
auto child_r = rectangle{ child->pos_owner, child->dimension };
|
||||
if (!overlapped(wd_r, child_r))
|
||||
presence.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
//Before resiz the window, creates the new graphics
|
||||
paint::graphics graph;
|
||||
paint::graphics root_graph;
|
||||
@ -1010,6 +1031,11 @@ namespace detail
|
||||
}
|
||||
}
|
||||
|
||||
for (auto child : presence)
|
||||
{
|
||||
refresh_tree(child);
|
||||
}
|
||||
|
||||
arg_resized arg;
|
||||
arg.window_handle = reinterpret_cast<window>(wd);
|
||||
arg.width = sz.width;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A Message Box Class
|
||||
* 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
|
||||
@ -1294,7 +1294,7 @@ namespace nana
|
||||
min_width_entry_field_pixels_ = pixels;
|
||||
}
|
||||
|
||||
#ifndef _nana_cxx_folding_expression
|
||||
#ifndef __cpp_fold_expressions
|
||||
void inputbox::_m_fetch_args(std::vector<abstract_content*>&)
|
||||
{}
|
||||
#endif
|
||||
|
||||
@ -742,8 +742,14 @@ namespace nana
|
||||
|
||||
void _m_add_agent(const detail::place_agent& ag) override
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
this->operator<<(
|
||||
widgets_.emplace_back(ag.create(place_ptr_->window_handle()))->handle()
|
||||
);
|
||||
#else
|
||||
widgets_.emplace_back(ag.create(place_ptr_->window_handle()));
|
||||
this->operator<<(widgets_.back()->handle());
|
||||
#endif
|
||||
}
|
||||
public:
|
||||
division* attached{ nullptr };
|
||||
@ -798,17 +804,20 @@ namespace nana
|
||||
case number_t::kind::real:
|
||||
return static_cast<unsigned>(number.real());
|
||||
case number_t::kind::percent:
|
||||
adjustable_px = area_px * number.real();
|
||||
case number_t::kind::none:
|
||||
{
|
||||
auto fpx = adjustable_px + precise_px;
|
||||
auto px = static_cast<unsigned>(fpx);
|
||||
precise_px = fpx - px;
|
||||
return px;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0; //Useless
|
||||
}
|
||||
return 0; //Useless
|
||||
|
||||
if(number_t::kind::percent == number.kind_of())
|
||||
adjustable_px = area_px * number.real() + precise_px;
|
||||
else
|
||||
adjustable_px += precise_px;
|
||||
|
||||
auto const px = static_cast<unsigned>(adjustable_px);
|
||||
precise_px = adjustable_px - px;
|
||||
return px;
|
||||
}
|
||||
|
||||
std::pair<double, double> calc_weight_floor()
|
||||
@ -2879,25 +2888,26 @@ namespace nana
|
||||
}
|
||||
|
||||
field_gather * attached_field = nullptr;
|
||||
if (name.size())
|
||||
|
||||
//find the field with specified name.
|
||||
//the field may not be created.
|
||||
auto i = fields.find(name);
|
||||
if (fields.end() != i)
|
||||
{
|
||||
//find the field with specified name.
|
||||
//the field may not be created.
|
||||
auto i = fields.find(name);
|
||||
if (fields.end() != i)
|
||||
{
|
||||
attached_field = i->second;
|
||||
//the field is attached to a division, it means there is another division with same name.
|
||||
if (attached_field->attached)
|
||||
throw std::runtime_error("place, the name '" + name + "' is redefined.");
|
||||
}
|
||||
attached_field = i->second;
|
||||
//the field is attached to a division, it means there is another division with same name.
|
||||
if (attached_field->attached)
|
||||
throw std::runtime_error("place, the name '" + name + "' is redefined.");
|
||||
}
|
||||
|
||||
token unmatch = token::width;
|
||||
switch (div_type)
|
||||
{
|
||||
case token::eof: unmatch = token::height; // "horitontal" div
|
||||
case token::vert: // "vertical" div
|
||||
case token::eof: // "horitontal" div
|
||||
case token::vert: // "vertical" div
|
||||
if(token::eof == div_type)
|
||||
unmatch = token::height;
|
||||
|
||||
for (auto& ch : children)
|
||||
if (ch->weigth_type == unmatch)
|
||||
throw std::invalid_argument("nana.place: unmatch vertical-heigth/horizontal-width betwen division '"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Parts of Class Place
|
||||
* 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
|
||||
@ -436,10 +436,13 @@ namespace nana
|
||||
caption_.caption(wdg->caption());
|
||||
}
|
||||
|
||||
panels_.emplace_back();
|
||||
auto wdg_ptr = wdg.get();
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
panels_.emplace_back().widget_ptr = std::move(wdg);
|
||||
#else
|
||||
panels_.emplace_back();
|
||||
panels_.back().widget_ptr.swap(wdg);
|
||||
|
||||
#endif
|
||||
for (auto & pn : panels_)
|
||||
{
|
||||
if (pn.widget_ptr)
|
||||
@ -607,12 +610,16 @@ namespace nana
|
||||
it = ib = 0;
|
||||
il = ir = 1;
|
||||
break;
|
||||
default:
|
||||
il = 3; //left
|
||||
case 3: //top, right, bottom
|
||||
it = 0;
|
||||
ir = 1;
|
||||
ib = 2;
|
||||
break;
|
||||
default: //left, top, right, bottom, left
|
||||
it = 0;
|
||||
ir = 1;
|
||||
ib = 2;
|
||||
il = 3;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
@ -661,12 +668,16 @@ namespace nana
|
||||
it = ib = 0;
|
||||
il = ir = 1;
|
||||
break;
|
||||
default:
|
||||
il = 3; //left
|
||||
case 3: //top, right, bottom
|
||||
it = 0;
|
||||
ir = 1;
|
||||
ib = 2;
|
||||
break;
|
||||
default: //left, top, right, bottom, left
|
||||
it = 0;
|
||||
ir = 1;
|
||||
ib = 2;
|
||||
il = 3;
|
||||
}
|
||||
|
||||
using px_type = decltype(r.height);
|
||||
|
||||
@ -669,7 +669,7 @@ namespace API
|
||||
internal_scope_guard lock;
|
||||
if(restrict::wd_manager().available(iwd) && (iwd->other.category == category::flags::root))
|
||||
{
|
||||
auto owner = interface_type::get_owner_window(iwd->root);
|
||||
auto owner = interface_type::get_window(iwd->root, window_relationship::owner);
|
||||
if(owner)
|
||||
return reinterpret_cast<window>(restrict::wd_manager().root(owner));
|
||||
}
|
||||
@ -812,8 +812,14 @@ namespace API
|
||||
return{};
|
||||
|
||||
auto sz = window_size(wd);
|
||||
sz.width += iwd->extra_width;
|
||||
sz.height += iwd->extra_height;
|
||||
|
||||
if(category::flags::root == iwd->other.category)
|
||||
{
|
||||
auto fm_extents = interface_type::window_frame_extents(iwd->root);
|
||||
sz.width += fm_extents.left + fm_extents.right;
|
||||
sz.height += fm_extents.top + fm_extents.bottom;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
@ -825,16 +831,19 @@ namespace API
|
||||
{
|
||||
if (category::flags::root == iwd->other.category)
|
||||
{
|
||||
auto fm_extents = interface_type::window_frame_extents(iwd->root);
|
||||
|
||||
size inner_size = sz;
|
||||
if (inner_size.width < iwd->extra_width)
|
||||
|
||||
if (inner_size.width < static_cast<unsigned>(fm_extents.left + fm_extents.right))
|
||||
inner_size.width = 0;
|
||||
else
|
||||
inner_size.width -= iwd->extra_width;
|
||||
inner_size.width -= static_cast<unsigned>(fm_extents.left + fm_extents.right);
|
||||
|
||||
if (inner_size.height < iwd->extra_height)
|
||||
if (inner_size.height < static_cast<unsigned>(fm_extents.top + fm_extents.bottom))
|
||||
inner_size.height = 0;
|
||||
else
|
||||
inner_size.height -= iwd->extra_height;
|
||||
inner_size.height -= static_cast<unsigned>(fm_extents.top + fm_extents.bottom);
|
||||
|
||||
window_size(wd, inner_size);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A Tooltip 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,6 +15,7 @@
|
||||
#include <nana/gui/timer.hpp>
|
||||
#include <nana/gui/screen.hpp>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace nana
|
||||
{
|
||||
@ -157,7 +158,14 @@ namespace nana
|
||||
|
||||
class controller
|
||||
{
|
||||
typedef std::pair<window, std::string> pair_t;
|
||||
struct tip_value
|
||||
{
|
||||
std::string text;
|
||||
event_handle evt_msenter;
|
||||
event_handle evt_msleave;
|
||||
event_handle evt_msdown;
|
||||
event_handle evt_destroy;
|
||||
};
|
||||
|
||||
typedef std::function<void(tooltip_interface*)> deleter_type;
|
||||
|
||||
@ -207,7 +215,7 @@ namespace nana
|
||||
if (str.empty())
|
||||
_m_untip(wd);
|
||||
else
|
||||
_m_get(wd).second = str;
|
||||
_m_get(wd).text = str;
|
||||
}
|
||||
|
||||
void show(const std::string& text, const point* pos, std::size_t duration)
|
||||
@ -236,35 +244,35 @@ namespace nana
|
||||
window_.reset();
|
||||
|
||||
//Destroy the tooltip controller when there are not tooltips.
|
||||
if (cont_.empty())
|
||||
if (table_.empty())
|
||||
instance(true);
|
||||
}
|
||||
private:
|
||||
void _m_untip(window wd)
|
||||
{
|
||||
for (auto i = cont_.begin(); i != cont_.end(); ++i)
|
||||
auto i = table_.find(wd);
|
||||
if(i != table_.end())
|
||||
{
|
||||
if (i->first == wd)
|
||||
{
|
||||
cont_.erase(i);
|
||||
API::umake_event(i->second.evt_msdown);
|
||||
API::umake_event(i->second.evt_msenter);
|
||||
API::umake_event(i->second.evt_msleave);
|
||||
API::umake_event(i->second.evt_destroy);
|
||||
|
||||
if (cont_.empty())
|
||||
{
|
||||
window_.reset();
|
||||
instance(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
table_.erase(i);
|
||||
}
|
||||
|
||||
if (table_.empty())
|
||||
{
|
||||
window_.reset();
|
||||
instance(true);
|
||||
}
|
||||
}
|
||||
private:
|
||||
pair_t& _m_get(window wd)
|
||||
tip_value& _m_get(window wd)
|
||||
{
|
||||
for (auto & pr : cont_)
|
||||
{
|
||||
if (pr.first == wd)
|
||||
return pr;
|
||||
}
|
||||
auto i = table_.find(wd);
|
||||
if (i != table_.end())
|
||||
return i->second;
|
||||
|
||||
auto & events = API::events(wd);
|
||||
|
||||
@ -272,28 +280,29 @@ namespace nana
|
||||
{
|
||||
if (event_code::mouse_enter == arg.evt_code)
|
||||
{
|
||||
auto & pr = _m_get(arg.window_handle);
|
||||
if (pr.second.size())
|
||||
this->show(pr.second, nullptr, 0);
|
||||
auto & value = _m_get(arg.window_handle);
|
||||
if (value.text.size())
|
||||
this->show(value.text, nullptr, 0);
|
||||
}
|
||||
else
|
||||
this->close();
|
||||
};
|
||||
|
||||
events.mouse_enter.connect(mouse_fn);
|
||||
events.mouse_leave.connect(mouse_fn);
|
||||
events.mouse_down.connect(mouse_fn);
|
||||
auto & value = table_[wd];
|
||||
|
||||
events.destroy.connect([this](const arg_destroy& arg){
|
||||
value.evt_msenter = events.mouse_enter.connect(mouse_fn);
|
||||
value.evt_msleave = events.mouse_leave.connect(mouse_fn);
|
||||
value.evt_msdown = events.mouse_down.connect(mouse_fn);
|
||||
|
||||
value.evt_destroy = events.destroy.connect([this](const arg_destroy& arg){
|
||||
_m_untip(arg.window_handle);
|
||||
});
|
||||
|
||||
cont_.emplace_back(wd, std::string());
|
||||
return cont_.back();
|
||||
return value;
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<tooltip_interface, deleter_type> window_;
|
||||
std::vector<pair_t> cont_;
|
||||
std::map<window, tip_value> table_;
|
||||
};
|
||||
}//namespace tooltip
|
||||
}//namespace drawerbase
|
||||
|
||||
@ -150,11 +150,12 @@ namespace nana{ namespace drawerbase
|
||||
|
||||
if (false == cite_.draw(graph, attr_.bgcolor, attr_.fgcolor, ::nana::rectangle{ graph.size() }, e_state))
|
||||
{
|
||||
if (!API::is_transparent_background(*wdg_))
|
||||
{
|
||||
if (API::is_transparent_background(*wdg_))
|
||||
API::dev::copy_transparent_background(*wdg_, graph);
|
||||
else
|
||||
_m_draw_background(graph);
|
||||
_m_draw_border(graph);
|
||||
}
|
||||
|
||||
_m_draw_border(graph);
|
||||
}
|
||||
_m_draw_title(graph, eb);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A Combox 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
|
||||
@ -732,7 +732,6 @@ namespace nana
|
||||
bool call_other_keys = false;
|
||||
if(drawer_->editable())
|
||||
{
|
||||
bool is_move_up = false;
|
||||
switch(arg.key)
|
||||
{
|
||||
case keyboard::os_arrow_left:
|
||||
@ -741,9 +740,8 @@ namespace nana
|
||||
drawer_->editor()->reset_caret();
|
||||
break;
|
||||
case keyboard::os_arrow_up:
|
||||
is_move_up = true;
|
||||
case keyboard::os_arrow_down:
|
||||
drawer_->move_items(is_move_up, true);
|
||||
drawer_->move_items((keyboard::os_arrow_up == arg.key), true);
|
||||
break;
|
||||
default:
|
||||
call_other_keys = true;
|
||||
@ -751,15 +749,15 @@ namespace nana
|
||||
}
|
||||
else
|
||||
{
|
||||
bool is_move_up = false;
|
||||
switch(arg.key)
|
||||
{
|
||||
case keyboard::os_arrow_left:
|
||||
case keyboard::os_arrow_up:
|
||||
is_move_up = true;
|
||||
drawer_->move_items(true, true);
|
||||
break;
|
||||
case keyboard::os_arrow_right:
|
||||
case keyboard::os_arrow_down:
|
||||
drawer_->move_items(is_move_up, true);
|
||||
drawer_->move_items(false, true);
|
||||
break;
|
||||
default:
|
||||
call_other_keys = true;
|
||||
@ -823,6 +821,14 @@ namespace nana
|
||||
}
|
||||
|
||||
/// Behavior of Iterator's value_type
|
||||
#ifdef _nana_std_has_string_view
|
||||
bool item_proxy::operator == (::std::string_view s) const
|
||||
{
|
||||
if (pos_ == nana::npos)
|
||||
return false;
|
||||
return (impl_->at(pos_).item_text == s);
|
||||
}
|
||||
#else
|
||||
bool item_proxy::operator == (const ::std::string& s) const
|
||||
{
|
||||
if (pos_ == nana::npos)
|
||||
@ -836,6 +842,7 @@ namespace nana
|
||||
return false;
|
||||
return (impl_->at(pos_).item_text == s);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/// Behavior of Iterator
|
||||
|
||||
@ -136,8 +136,12 @@ namespace nana{
|
||||
{
|
||||
_THROW_IF_EMPTY()
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & opt = impl_->options.emplace_back(new checkbox{ handle() });
|
||||
#else
|
||||
impl_->options.emplace_back(new checkbox(handle()));
|
||||
auto & opt = impl_->options.back();
|
||||
#endif
|
||||
opt->transparent(true);
|
||||
opt->caption(std::move(text));
|
||||
impl_->place_content[field_options] << *opt;
|
||||
@ -264,15 +268,22 @@ namespace nana{
|
||||
|
||||
drawing dw(*this);
|
||||
|
||||
//When the group is resized, the drawing is called before moving the caption, but
|
||||
//the drawing of group requires the lastest position of caption for gradual rectangle.
|
||||
//For the requirement, a move event handler is required for listning the change of caption's position.
|
||||
impl_->caption.events().move([this](const arg_move&){
|
||||
if (align::left != impl_->caption_align)
|
||||
API::refresh_window(*this);
|
||||
});
|
||||
|
||||
// This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !!
|
||||
dw.draw([this](paint::graphics& graph)
|
||||
{
|
||||
auto gap_px = impl_->gap - 1;
|
||||
|
||||
graph.rectangle(true, API::bgcolor(this->parent()));
|
||||
|
||||
auto const top_round_line = static_cast<int>(impl_->caption_dimension.height) / 2;
|
||||
|
||||
graph.rectangle(true, API::bgcolor(this->parent()));
|
||||
graph.round_rectangle(rectangle(point(gap_px, top_round_line),
|
||||
nana::size(graph.width() - 2 * gap_px, graph.height() - top_round_line - gap_px)
|
||||
),
|
||||
@ -281,11 +292,10 @@ namespace nana{
|
||||
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) } };
|
||||
rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width + 4, 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
|
||||
|
||||
@ -28,14 +28,28 @@ namespace nana
|
||||
{
|
||||
class renderer
|
||||
{
|
||||
typedef widgets::skeletons::dstream::linecontainer::iterator iterator;
|
||||
//Iterator of content element in a line.
|
||||
using content_element_iterator = widgets::skeletons::dstream::linecontainer::const_iterator; //subsitute for member type iterator
|
||||
|
||||
struct pixel_tag
|
||||
struct visual_line //subsitute of pixel_tag
|
||||
{
|
||||
int x_base; //The x position where this line starts.
|
||||
std::size_t pixels;
|
||||
std::size_t baseline; //The baseline for drawing text.
|
||||
std::vector<iterator> values; //line values
|
||||
struct element
|
||||
{
|
||||
content_element_iterator content_element;
|
||||
std::pair<std::size_t, std::size_t> range; //A part of text in a text element. first: text begin, second: text length
|
||||
|
||||
element(const content_element_iterator& iterator, std::size_t range_begin, std::size_t range_end):
|
||||
content_element(iterator),
|
||||
range(range_begin, range_end)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
int x_base; //The x position where this line starts.
|
||||
|
||||
std::size_t extent_height_px;
|
||||
std::size_t baseline; //The baseline for rendering text.
|
||||
std::vector<element> elements; //description of text element in this rendering line.
|
||||
};
|
||||
|
||||
//this is a helper variable, it just keeps the status while drawing.
|
||||
@ -46,8 +60,8 @@ namespace nana
|
||||
align_v text_align_v;
|
||||
|
||||
nana::point pos;
|
||||
std::vector<pixel_tag> pixels;
|
||||
std::size_t index;
|
||||
std::vector<visual_line> vslines; //The lines description of a line of text. substitute of member pixels.
|
||||
std::size_t index; //indicates the current rendering visual line.
|
||||
};
|
||||
|
||||
struct traceable
|
||||
@ -102,18 +116,19 @@ namespace nana
|
||||
rs.text_align = th;
|
||||
rs.text_align_v = tv;
|
||||
|
||||
std::deque<std::vector<pixel_tag> > pixel_lines;
|
||||
//All visual lines data of whole text.
|
||||
std::deque<std::vector<visual_line>> content_lines;
|
||||
|
||||
std::size_t extent_v_pixels = 0; //the pixels, in height, that text will be painted.
|
||||
|
||||
for (auto & line : dstream_)
|
||||
{
|
||||
_m_line_pixels(line, def_line_pixels, rs);
|
||||
_m_prepare_visual_lines(graph, line, def_line_pixels, rs);
|
||||
|
||||
for (auto & m : rs.pixels)
|
||||
extent_v_pixels += m.pixels;
|
||||
for (auto & vsline : rs.vslines)
|
||||
extent_v_pixels += vsline.extent_height_px;
|
||||
|
||||
pixel_lines.emplace_back(std::move(rs.pixels));
|
||||
content_lines.emplace_back(std::move(rs.vslines));
|
||||
|
||||
if(extent_v_pixels >= graph.height())
|
||||
break;
|
||||
@ -129,25 +144,21 @@ namespace nana
|
||||
else
|
||||
rs.pos.y = 0;
|
||||
|
||||
auto pixels_iterator = pixel_lines.begin();
|
||||
|
||||
auto vsline_iterator = content_lines.begin();
|
||||
for (auto & line : dstream_)
|
||||
{
|
||||
if (rs.pos.y >= static_cast<int>(graph.height()))
|
||||
break;
|
||||
|
||||
rs.index = 0;
|
||||
rs.pixels.clear();
|
||||
rs.vslines.clear();
|
||||
rs.vslines.swap(*vsline_iterator++);
|
||||
rs.pos.x = rs.vslines.front().x_base;
|
||||
|
||||
rs.pixels.swap(*pixels_iterator++);
|
||||
|
||||
rs.pos.x = rs.pixels.front().x_base;
|
||||
|
||||
//Stop drawing when it goes out of range.
|
||||
if(false == _m_each_line(graph, line, rs))
|
||||
if (!_m_foreach_visual_line(graph, rs))
|
||||
break;
|
||||
|
||||
rs.pos.y += static_cast<int>(rs.pixels.back().pixels);
|
||||
rs.pos.y += static_cast<int>(rs.vslines.back().extent_height_px);
|
||||
}
|
||||
|
||||
graph.typeface(pre_font);
|
||||
@ -194,8 +205,8 @@ namespace nana
|
||||
|
||||
for(auto & line: dstream_)
|
||||
{
|
||||
rs.pixels.clear();
|
||||
unsigned w = _m_line_pixels(line, def_line_pixels, rs);
|
||||
rs.vslines.clear();
|
||||
auto w = _m_prepare_visual_lines(graph, line, def_line_pixels, rs);
|
||||
|
||||
if(limited && (w > limited))
|
||||
w = limited;
|
||||
@ -203,8 +214,8 @@ namespace nana
|
||||
if(retsize.width < w)
|
||||
retsize.width = w;
|
||||
|
||||
for (auto & px : rs.pixels)
|
||||
retsize.height += static_cast<unsigned>(px.pixels);
|
||||
for (auto& vsline : rs.vslines)
|
||||
retsize.height += static_cast<unsigned>(vsline.extent_height_px);
|
||||
}
|
||||
|
||||
return retsize;
|
||||
@ -215,8 +226,12 @@ namespace nana
|
||||
{
|
||||
if(fbp->target.size() || fbp->url.size())
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & tr = traceable_.emplace_back();
|
||||
#else
|
||||
traceable_.emplace_back();
|
||||
auto & tr = traceable_.back();
|
||||
#endif
|
||||
tr.r.x = x;
|
||||
tr.r.y = y;
|
||||
tr.r.dimension(sz);
|
||||
@ -315,291 +330,303 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
void _m_align_x_base(const render_status& rs, pixel_tag & px, unsigned w) noexcept
|
||||
void _m_prepare_x(const render_status& rs, visual_line & vsline, unsigned w) noexcept
|
||||
{
|
||||
switch(rs.text_align)
|
||||
switch (rs.text_align)
|
||||
{
|
||||
case align::left:
|
||||
px.x_base = 0;
|
||||
vsline.x_base = 0;
|
||||
break;
|
||||
case align::center:
|
||||
px.x_base = (static_cast<int>(rs.allowed_width - w) >> 1);
|
||||
vsline.x_base = (static_cast<int>(rs.allowed_width - w) >> 1);
|
||||
break;
|
||||
case align::right:
|
||||
px.x_base = static_cast<int>(rs.allowed_width - w);
|
||||
vsline.x_base = static_cast<int>(rs.allowed_width - w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned _m_line_pixels(dstream::linecontainer& line, unsigned def_line_pixels, render_status & rs)
|
||||
/**
|
||||
* prepare data for rendering a line of text.
|
||||
*/
|
||||
unsigned _m_prepare_visual_lines(graph_reference graph, dstream::linecontainer& line, unsigned def_line_px, render_status& rs)
|
||||
{
|
||||
if (line.empty())
|
||||
{
|
||||
pixel_tag px;
|
||||
px.baseline = 0;
|
||||
px.pixels = def_line_pixels;
|
||||
px.x_base = 0;
|
||||
|
||||
rs.pixels.emplace_back(px);
|
||||
//Insert an empty visual line for empty content.
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & vsline = rs.vslines.emplace_back();
|
||||
#else
|
||||
rs.vslines.emplace_back();
|
||||
auto & vsline = rs.vslines.back();
|
||||
|
||||
vsline.baseline = 0;
|
||||
vsline.extent_height_px = def_line_px;
|
||||
vsline.x_base = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned total_w = 0;
|
||||
unsigned w = 0;
|
||||
unsigned abs_text_px = 0;
|
||||
unsigned max_ascent = 0;
|
||||
unsigned max_descent = 0;
|
||||
unsigned max_px = 0;
|
||||
unsigned max_content_height = 0;
|
||||
|
||||
//Bidi reorder is requried here
|
||||
int text_pos = 0;
|
||||
|
||||
std::vector<iterator> line_values;
|
||||
std::vector<visual_line::element> vsline_elements;
|
||||
|
||||
for(auto i = line.begin(); i != line.end(); ++i)
|
||||
for (auto i = line.cbegin(); i != line.cend(); ++i)
|
||||
{
|
||||
data * data_ptr = i->data_ptr;
|
||||
nana::size sz = data_ptr->size();
|
||||
total_w += sz.width;
|
||||
auto const data = i->data_ptr;
|
||||
auto fblock = i->fblock_ptr;
|
||||
|
||||
unsigned as = 0; //ascent
|
||||
unsigned ds = 0; //descent
|
||||
abs_text_px += data->size().width;
|
||||
|
||||
if(fblock::aligns::baseline == i->fblock_ptr->text_align)
|
||||
unsigned ascent = 0;
|
||||
unsigned descent = 0;
|
||||
|
||||
|
||||
auto extent_size = data->size();
|
||||
|
||||
//Check if the content is displayed in current line.
|
||||
if ((0 == rs.allowed_width) || (text_pos + extent_size.width <= rs.allowed_width))
|
||||
{
|
||||
as = static_cast<unsigned>(data_ptr->ascent());
|
||||
ds = static_cast<unsigned>(sz.height - as);
|
||||
text_pos += static_cast<int>(extent_size.width);
|
||||
|
||||
if(max_descent < ds)
|
||||
max_descent = ds;
|
||||
|
||||
if((false == data_ptr->is_text()) && (sz.height < max_ascent + max_descent))
|
||||
sz.height = max_ascent + max_descent;
|
||||
}
|
||||
|
||||
//Check if the content is displayed in a new line.
|
||||
if((0 == rs.allowed_width) || (w + sz.width <= rs.allowed_width))
|
||||
{
|
||||
w += sz.width;
|
||||
|
||||
if(max_ascent < as) max_ascent = as;
|
||||
if(max_descent < ds) max_descent = ds;
|
||||
if(max_px < sz.height) max_px = sz.height;
|
||||
line_values.emplace_back(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel_tag px;
|
||||
_m_align_x_base(rs, px, (w ? w : sz.width));
|
||||
|
||||
if(w)
|
||||
//Adjust height of extent_size for special text alignement.
|
||||
if (fblock::aligns::baseline == fblock->text_align)
|
||||
{
|
||||
if(max_ascent + max_descent > max_px)
|
||||
max_px = max_descent + max_ascent;
|
||||
else
|
||||
max_ascent = max_px - max_descent;
|
||||
ascent = static_cast<unsigned>(data->ascent());
|
||||
descent = static_cast<unsigned>(extent_size.height - ascent);
|
||||
|
||||
px.pixels = max_px;
|
||||
px.baseline = max_ascent;
|
||||
px.values.swap(line_values);
|
||||
if (max_descent < descent)
|
||||
max_descent = descent;
|
||||
|
||||
w = sz.width;
|
||||
max_px = sz.height;
|
||||
max_ascent = as;
|
||||
max_descent = ds;
|
||||
line_values.emplace_back(i);
|
||||
if ((false == data->is_text()) && (extent_size.height < max_ascent + max_descent))
|
||||
extent_size.height = max_ascent + max_descent;
|
||||
}
|
||||
|
||||
if (max_ascent < ascent) max_ascent = ascent;
|
||||
if (max_descent < descent) max_descent = descent;
|
||||
if (max_content_height < extent_size.height) max_content_height = extent_size.height;
|
||||
vsline_elements.emplace_back(i, 0, data->text().size());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//make a visual line for existing vsline elements
|
||||
if (text_pos)
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & vsline = rs.vslines.emplace_back();
|
||||
#else
|
||||
rs.vslines.emplace_back();
|
||||
auto & vsline = rs.vslines.back();
|
||||
#endif
|
||||
_m_prepare_x(rs, vsline, static_cast<unsigned>(text_pos));
|
||||
|
||||
if (max_ascent + max_descent > max_content_height)
|
||||
max_content_height = max_descent + max_ascent;
|
||||
else
|
||||
max_ascent = max_content_height - max_descent;
|
||||
|
||||
vsline.extent_height_px = max_content_height;
|
||||
vsline.baseline = max_ascent;
|
||||
vsline.elements.swap(vsline_elements);
|
||||
}
|
||||
|
||||
text_pos = 0;
|
||||
max_content_height = max_ascent = max_descent = 0;
|
||||
//Adjust height of extent_size for special text alignement.
|
||||
if (fblock::aligns::baseline == fblock->text_align)
|
||||
{
|
||||
ascent = static_cast<unsigned>(data->ascent());
|
||||
descent = static_cast<unsigned>(extent_size.height - ascent);
|
||||
|
||||
if (max_descent < descent)
|
||||
max_descent = descent;
|
||||
|
||||
if ((false == data->is_text()) && (extent_size.height < max_ascent + max_descent))
|
||||
extent_size.height = max_ascent + max_descent;
|
||||
}
|
||||
|
||||
if (max_ascent < ascent) max_ascent = ascent;
|
||||
if (max_descent < descent) max_descent = descent;
|
||||
if (max_content_height < extent_size.height) max_content_height = extent_size.height;
|
||||
|
||||
if (data->is_text())
|
||||
{
|
||||
_m_change_font(graph, fblock);
|
||||
//Split a text into multiple lines
|
||||
auto rest_extent_size = extent_size.width;
|
||||
std::size_t text_begin = 0;
|
||||
while (text_begin < data->text().size())
|
||||
{
|
||||
px.pixels = sz.height;
|
||||
px.baseline = as;
|
||||
unsigned sub_text_px = 0;
|
||||
auto sub_text_len = _m_fit_text(graph, data->text().substr(text_begin), rs.allowed_width, sub_text_px);
|
||||
|
||||
px.values.emplace_back(i);
|
||||
if (text_begin + sub_text_len < data->text().size())
|
||||
{
|
||||
//make a new visual line
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & vsline = rs.vslines.emplace_back();
|
||||
#else
|
||||
rs.vslines.emplace_back();
|
||||
auto & vsline = rs.vslines.back();
|
||||
#endif
|
||||
_m_prepare_x(rs, vsline, sub_text_px);
|
||||
|
||||
max_px = 0;
|
||||
max_ascent = max_descent = 0;
|
||||
vsline.extent_height_px = max_content_height;
|
||||
vsline.baseline = max_ascent;
|
||||
vsline.elements.emplace_back(i, text_begin, sub_text_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
//the last part, write it to vsline_elements to keep the status for next line element(next i)
|
||||
vsline_elements.emplace_back(i, text_begin, sub_text_len);
|
||||
|
||||
text_pos = sub_text_px;
|
||||
}
|
||||
|
||||
text_begin += sub_text_len;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//the last part, write it to vsline_elements to keep the status for next line element(next i)
|
||||
vsline_elements.emplace_back(i, 0, 0);
|
||||
|
||||
rs.pixels.emplace_back(px);
|
||||
text_pos = static_cast<int>(i->data_ptr->size().width);
|
||||
}
|
||||
}
|
||||
|
||||
if (max_px)
|
||||
if (!vsline_elements.empty())
|
||||
{
|
||||
pixel_tag px;
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & vsline = rs.vslines.emplace_back();
|
||||
#else
|
||||
rs.vslines.emplace_back();
|
||||
auto & vsline = rs.vslines.back();
|
||||
#endif
|
||||
_m_prepare_x(rs, vsline, static_cast<unsigned>(text_pos));
|
||||
|
||||
_m_align_x_base(rs, px, w);
|
||||
|
||||
if (max_ascent + max_descent > max_px)
|
||||
max_px = max_descent + max_ascent;
|
||||
if (max_ascent + max_descent > max_content_height)
|
||||
max_content_height = max_descent + max_ascent;
|
||||
else
|
||||
max_ascent = max_px - max_descent;
|
||||
max_ascent = max_content_height - max_descent;
|
||||
|
||||
px.pixels = max_px;
|
||||
px.baseline = max_ascent;
|
||||
px.values.swap(line_values);
|
||||
rs.pixels.emplace_back(px);
|
||||
vsline.extent_height_px = max_content_height;
|
||||
vsline.baseline = max_ascent;
|
||||
vsline.elements.swap(vsline_elements);
|
||||
}
|
||||
return total_w;
|
||||
|
||||
return abs_text_px;
|
||||
}
|
||||
|
||||
bool _m_each_line(graph_reference graph, dstream::linecontainer&, render_status& rs)
|
||||
//Get the length of characters in a text whose length in pixels doesn't beyond the limited width.
|
||||
static unsigned _m_fit_text(graph_reference graph, const std::wstring& text, unsigned limited_width_px, unsigned& text_px) noexcept
|
||||
{
|
||||
#ifdef _nana_std_has_string_view
|
||||
auto pxbuf = graph.glyph_pixels(text);
|
||||
#else
|
||||
std::unique_ptr<unsigned[]> pxbuf(new unsigned[text.size()]);
|
||||
graph.glyph_pixels(text.c_str(), text.size(), pxbuf.get());
|
||||
#endif
|
||||
|
||||
text_px = 0;
|
||||
for (unsigned i = 0; i < text.size(); ++i)
|
||||
{
|
||||
if (text_px + pxbuf[i] > limited_width_px)
|
||||
return i;
|
||||
|
||||
text_px += pxbuf[i];
|
||||
}
|
||||
return static_cast<unsigned>(text.size());
|
||||
}
|
||||
|
||||
bool _m_foreach_visual_line(graph_reference graph, render_status& rs)
|
||||
{
|
||||
std::wstring text;
|
||||
iterator block_start;
|
||||
|
||||
content_element_iterator block_start;
|
||||
|
||||
const int lastpos = static_cast<int>(graph.height()) - 1;
|
||||
auto const bottom = static_cast<int>(graph.height()) - 1;
|
||||
|
||||
for(auto & px : rs.pixels)
|
||||
for (auto & vsline : rs.vslines)
|
||||
{
|
||||
for(auto & render_iterator: px.values)
|
||||
rs.pos.x = vsline.x_base;
|
||||
for (auto& content_elm : vsline.elements)
|
||||
{
|
||||
auto & value = *render_iterator;
|
||||
if (value.data_ptr->is_text())
|
||||
{
|
||||
//hold the block while the text is empty,
|
||||
//it stands for the first block
|
||||
if (text.empty())
|
||||
block_start = render_iterator;
|
||||
|
||||
text += value.data_ptr->text();
|
||||
continue;
|
||||
}
|
||||
|
||||
if(text.size())
|
||||
{
|
||||
_m_draw_block(graph, text, block_start, rs);
|
||||
if(lastpos <= rs.pos.y)
|
||||
return false;
|
||||
text.clear();
|
||||
}
|
||||
nana::size sz = value.data_ptr->size();
|
||||
|
||||
pixel_tag px = rs.pixels[rs.index];
|
||||
if ((rs.allowed_width < rs.pos.x + sz.width) && (rs.pos.x != px.x_base))
|
||||
{
|
||||
//Change a line.
|
||||
rs.pos.y += static_cast<int>(px.pixels);
|
||||
px = rs.pixels[++rs.index];
|
||||
rs.pos.x = px.x_base;
|
||||
}
|
||||
|
||||
int y = rs.pos.y + _m_text_top(px, value.fblock_ptr, value.data_ptr);
|
||||
|
||||
value.data_ptr->nontext_render(graph, rs.pos.x, y);
|
||||
_m_insert_if_traceable(rs.pos.x, y, sz, value.fblock_ptr);
|
||||
rs.pos.x += static_cast<int>(sz.width);
|
||||
|
||||
if(lastpos < y)
|
||||
return false;
|
||||
_m_draw_vsline_element(graph, content_elm, rs);
|
||||
}
|
||||
|
||||
if(text.size())
|
||||
{
|
||||
_m_draw_block(graph, text, block_start, rs);
|
||||
text.clear();
|
||||
}
|
||||
++rs.index; //next line index
|
||||
rs.pos.y += static_cast<int>(vsline.extent_height_px);
|
||||
|
||||
if (rs.pos.y > bottom)
|
||||
return false;
|
||||
}
|
||||
return (rs.pos.y <= lastpos);
|
||||
|
||||
return (rs.pos.y <= bottom);
|
||||
}
|
||||
|
||||
static bool _m_overline(const render_status& rs, int right, bool equal_required) noexcept
|
||||
static int _m_vsline_element_top(const visual_line& vsline, fblock* fblock_ptr, const data* data_ptr) noexcept
|
||||
{
|
||||
if(align::left == rs.text_align)
|
||||
return (equal_required ? right >= static_cast<int>(rs.allowed_width) : right > static_cast<int>(rs.allowed_width));
|
||||
|
||||
return (equal_required ? rs.pixels[rs.index].x_base <= 0 : rs.pixels[rs.index].x_base < 0);
|
||||
}
|
||||
|
||||
static int _m_text_top(const pixel_tag& px, fblock* fblock_ptr, const data* data_ptr)
|
||||
{
|
||||
switch(fblock_ptr->text_align)
|
||||
switch (fblock_ptr->text_align)
|
||||
{
|
||||
case fblock::aligns::center:
|
||||
return static_cast<int>(px.pixels - data_ptr->size().height) / 2;
|
||||
return static_cast<int>(vsline.extent_height_px - data_ptr->size().height) / 2;
|
||||
case fblock::aligns::bottom:
|
||||
return static_cast<int>(px.pixels - data_ptr->size().height);
|
||||
return static_cast<int>(vsline.extent_height_px - data_ptr->size().height);
|
||||
case fblock::aligns::baseline:
|
||||
return static_cast<int>(px.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height));
|
||||
return static_cast<int>(vsline.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height));
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _m_draw_block(graph_reference graph, const std::wstring& s, dstream::linecontainer::iterator block_start, render_status& rs)
|
||||
void _m_draw_vsline_element(graph_reference graph, const visual_line::element& vsline_elm, render_status& rs)
|
||||
{
|
||||
auto const reordered = unicode_reorder(s.data(), s.length());
|
||||
auto data = vsline_elm.content_element->data_ptr;
|
||||
auto fblock = vsline_elm.content_element->fblock_ptr;
|
||||
|
||||
pixel_tag px = rs.pixels[rs.index];
|
||||
|
||||
for(auto & bidi : reordered)
|
||||
if (data->is_text())
|
||||
{
|
||||
std::size_t pos = bidi.begin - s.data();
|
||||
std::size_t len = bidi.end - bidi.begin;
|
||||
auto const text = data->text().c_str() + vsline_elm.range.first;
|
||||
auto const reordered = unicode_reorder(text, vsline_elm.range.second);
|
||||
|
||||
while (true)
|
||||
_m_change_font(graph, fblock);
|
||||
for (auto & bidi : reordered)
|
||||
{
|
||||
auto i = block_start;
|
||||
|
||||
//Text range indicates the position of text where begin to output
|
||||
//The output length is the min between len and the second of text range.
|
||||
auto text_range = _m_locate(i, pos);
|
||||
|
||||
if (text_range.second > len)
|
||||
text_range.second = len;
|
||||
|
||||
fblock * fblock_ptr = i->fblock_ptr;
|
||||
data * data_ptr = i->data_ptr;
|
||||
|
||||
const int w = static_cast<int>(rs.allowed_width) - rs.pos.x;
|
||||
nana::size sz = data_ptr->size();
|
||||
if ((static_cast<int>(sz.width) > w) && (rs.pos.x != px.x_base))
|
||||
{
|
||||
//Change a new line
|
||||
rs.pos.y += static_cast<int>(px.pixels);
|
||||
px = rs.pixels[++rs.index];
|
||||
rs.pos.x = px.x_base;
|
||||
}
|
||||
|
||||
const int y = rs.pos.y + _m_text_top(px, fblock_ptr, data_ptr);
|
||||
|
||||
_m_change_font(graph, fblock_ptr);
|
||||
|
||||
auto extent_size = data->size();
|
||||
#ifdef _nana_std_has_string_view
|
||||
std::wstring_view text_sv{ data_ptr->text() };
|
||||
if (text_range.second != text_sv.size())
|
||||
{
|
||||
text_sv = text_sv.substr(text_range.first, text_range.second);
|
||||
sz = graph.text_extent_size(text_sv);
|
||||
}
|
||||
std::wstring_view text_sv{ bidi.begin, static_cast<std::size_t>(bidi.end - bidi.begin) };
|
||||
if (data->text().size() != text_sv.size())
|
||||
extent_size = graph.text_extent_size(text_sv);
|
||||
|
||||
graph.string({ rs.pos.x, y }, text_sv, _m_fgcolor(fblock_ptr));
|
||||
const int y = rs.pos.y + _m_vsline_element_top(rs.vslines[rs.index], fblock, data);
|
||||
graph.string({ rs.pos.x, y }, text_sv, _m_fgcolor(fblock));
|
||||
#else
|
||||
if (text_range.second == data_ptr->text().length())
|
||||
{
|
||||
graph.string({ rs.pos.x, y }, data_ptr->text(), _m_fgcolor(fblock_ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto str = data_ptr->text().substr(text_range.first, text_range.second);
|
||||
sz = graph.text_extent_size(str);
|
||||
std::wstring text{ bidi.begin, static_cast<std::size_t>(bidi.end - bidi.begin) };
|
||||
if (data->text().size() != text.size())
|
||||
extent_size = graph.text_extent_size(text);
|
||||
|
||||
graph.string({ rs.pos.x, y }, str, _m_fgcolor(fblock_ptr));
|
||||
}
|
||||
const int y = rs.pos.y + _m_vsline_element_top(rs.vslines[rs.index], fblock, data);
|
||||
graph.string({ rs.pos.x, y }, text, _m_fgcolor(fblock));
|
||||
#endif
|
||||
|
||||
|
||||
_m_insert_if_traceable(rs.pos.x, y, sz, fblock_ptr);
|
||||
rs.pos.x += static_cast<int>(sz.width);
|
||||
|
||||
if(text_range.second < len)
|
||||
{
|
||||
len -= text_range.second;
|
||||
pos += text_range.second;
|
||||
}
|
||||
else
|
||||
break;
|
||||
_m_insert_if_traceable(rs.pos.x, y, extent_size, fblock);
|
||||
rs.pos.x += static_cast<int>(extent_size.width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int y = rs.pos.y + _m_vsline_element_top(rs.vslines[rs.index], fblock, data);
|
||||
|
||||
data->nontext_render(graph, rs.pos.x, y);
|
||||
_m_insert_if_traceable(rs.pos.x, y, data->size(), fblock);
|
||||
rs.pos.x += static_cast<int>(data->size().width);
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<std::size_t, std::size_t> _m_locate(dstream::linecontainer::iterator& i, std::size_t pos)
|
||||
|
||||
@ -312,8 +312,12 @@ namespace nana
|
||||
|
||||
size_type create(essence* ess, native_string_type&& text, unsigned pixels)
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
return cont_.emplace_back(ess, std::move(text), pixels, static_cast<size_type>(cont_.size())).index;
|
||||
#else
|
||||
cont_.emplace_back(ess, std::move(text), pixels, static_cast<size_type>(cont_.size()));
|
||||
return cont_.back().index;
|
||||
#endif
|
||||
}
|
||||
|
||||
void clear()
|
||||
@ -1023,9 +1027,15 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & last_cat = categories_.emplace_back();
|
||||
last_cat.key_ptr = ptr;
|
||||
return &last_cat;
|
||||
#else
|
||||
categories_.emplace_back();
|
||||
categories_.back().key_ptr = ptr;
|
||||
return &(categories_.back());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Inserts a new category at position specified by pos
|
||||
@ -1033,8 +1043,12 @@ namespace nana
|
||||
{
|
||||
if (::nana::npos == pos)
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
return &categories_.emplace_back(std::move(text));
|
||||
#else
|
||||
categories_.emplace_back(std::move(text));
|
||||
return &categories_.back();
|
||||
#endif
|
||||
}
|
||||
|
||||
return &(*categories_.emplace(this->get(pos), std::move(text)));
|
||||
@ -2622,8 +2636,12 @@ namespace nana
|
||||
|
||||
oresolver& oresolver::operator<<(std::nullptr_t)
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
cells_.emplace_back().text.assign(1, wchar_t{});
|
||||
#else
|
||||
cells_.emplace_back();
|
||||
cells_.back().text.assign(1, wchar_t(0)); //means invalid cell
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -2934,9 +2952,9 @@ namespace nana
|
||||
cat_proxy(ess_, pos.cat).at(pos.item).select(true);
|
||||
}
|
||||
|
||||
void hovered(index_type /*pos*/) override
|
||||
void hovered(index_type pos) override
|
||||
{
|
||||
auto offset = ess_->content_view->origin().y / ess_->item_height();
|
||||
auto offset = ess_->lister.distance(ess_->first_display(), ess_->lister.index_cast(pos, false));
|
||||
|
||||
if (ess_->pointer_where.first != parts::list || ess_->pointer_where.second != offset)
|
||||
{
|
||||
@ -2996,11 +3014,11 @@ namespace nana
|
||||
}
|
||||
else
|
||||
{
|
||||
auto last_off = this->distance(this->first(), this->last()) * ess_->item_height();
|
||||
if (last_off - off >= screen_px)
|
||||
auto const content_px = ess_->content_view->content_size().height;
|
||||
if (content_px - off >= screen_px)
|
||||
origin.y = static_cast<int>(off);
|
||||
else if (last_off >= screen_px)
|
||||
origin.y = static_cast<int>(last_off - screen_px);
|
||||
else if (content_px >= screen_px)
|
||||
origin.y = static_cast<int>(content_px - screen_px);
|
||||
}
|
||||
|
||||
if (ess_->content_view->move_origin(origin - ess_->content_view->origin()))
|
||||
@ -4378,9 +4396,11 @@ namespace nana
|
||||
essence_->ptr_state = item_state::highlighted;
|
||||
|
||||
bool need_refresh = false;
|
||||
//Do sort
|
||||
if (essence_->header.sortable() && essence_->pointer_where.first == parts::header && prev_state == item_state::pressed)
|
||||
|
||||
//Don't sort the column when the mouse is due to released for stopping resizing column.
|
||||
if ((drawer_header_->splitter() == npos) && essence_->header.sortable() && essence_->pointer_where.first == parts::header && prev_state == item_state::pressed)
|
||||
{
|
||||
//Try to sort the column
|
||||
if(essence_->pointer_where.second < essence_->header.cont().size())
|
||||
need_refresh = essence_->lister.sort_column(essence_->pointer_where.second, nullptr);
|
||||
}
|
||||
@ -4475,14 +4495,11 @@ namespace nana
|
||||
if (list.first().empty())
|
||||
return;
|
||||
|
||||
bool upward = false;
|
||||
|
||||
switch(arg.key)
|
||||
{
|
||||
case keyboard::os_arrow_up:
|
||||
upward = true;
|
||||
case keyboard::os_arrow_down:
|
||||
list.move_select(upward, !arg.shift, true);
|
||||
list.move_select((keyboard::os_arrow_up == arg.key), !arg.shift, true);
|
||||
break;
|
||||
case L' ':
|
||||
{
|
||||
@ -4494,9 +4511,9 @@ namespace nana
|
||||
}
|
||||
break;
|
||||
case keyboard::os_pageup :
|
||||
upward = true;
|
||||
case keyboard::os_pagedown:
|
||||
{
|
||||
auto const upward = (keyboard::os_pageup == arg.key);
|
||||
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]);
|
||||
@ -4825,6 +4842,17 @@ namespace nana
|
||||
}
|
||||
|
||||
//Behavior of Iterator's value_type
|
||||
#ifdef _nana_std_has_string_view
|
||||
bool item_proxy::operator==(std::string_view sv) const
|
||||
{
|
||||
return (text(0) == sv);
|
||||
}
|
||||
|
||||
bool item_proxy::operator==(std::wstring_view sv) const
|
||||
{
|
||||
return (text(0) == to_utf8(sv));
|
||||
}
|
||||
#else
|
||||
bool item_proxy::operator==(const char * s) const
|
||||
{
|
||||
return this->operator==(std::string(s));
|
||||
@ -4844,6 +4872,7 @@ namespace nana
|
||||
{
|
||||
return (text(0) == to_utf8(s));
|
||||
}
|
||||
#endif
|
||||
|
||||
item_proxy & item_proxy::operator=(const item_proxy& rhs)
|
||||
{
|
||||
@ -5378,6 +5407,7 @@ namespace nana
|
||||
|
||||
void listbox::auto_draw(bool enabled) noexcept
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
if (ess.auto_draw != enabled)
|
||||
{
|
||||
@ -5388,6 +5418,7 @@ namespace nana
|
||||
|
||||
void listbox::scroll(bool to_bottom, size_type cat_pos)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
auto cats = this->size_categ();
|
||||
|
||||
@ -5486,6 +5517,7 @@ namespace nana
|
||||
|
||||
rectangle listbox::content_area() const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
auto carea = ess.content_area();
|
||||
carea.x += ess.header.margin();
|
||||
@ -5532,29 +5564,34 @@ namespace nana
|
||||
|
||||
listbox::cat_proxy listbox::at(size_type pos)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
check_range(pos, size_categ());
|
||||
return{ &_m_ess(), pos };
|
||||
}
|
||||
|
||||
const listbox::cat_proxy listbox::at(size_type pos) const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
check_range(pos, size_categ());
|
||||
return{ &_m_ess(), pos };
|
||||
}
|
||||
|
||||
listbox::item_proxy listbox::at(const index_pair& abs_pos)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return at(abs_pos.cat).at(abs_pos.item);
|
||||
}
|
||||
|
||||
const listbox::item_proxy listbox::at(const index_pair& pos_abs) const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return at(pos_abs.cat).at(pos_abs.item);
|
||||
}
|
||||
|
||||
// Contributed by leobackes(pr#97)
|
||||
listbox::index_pair listbox::cast( const point& pos ) const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess=_m_ess();
|
||||
auto _where = ess.where(pos);
|
||||
|
||||
@ -5566,27 +5603,32 @@ namespace nana
|
||||
|
||||
auto listbox::column_at(size_type pos, bool disp_order) -> column_interface&
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().header.at(pos, disp_order);
|
||||
}
|
||||
|
||||
auto listbox::column_at(size_type pos, bool disp_order) const -> const column_interface&
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().header.at(pos, disp_order);
|
||||
}
|
||||
|
||||
auto listbox::column_size() const ->size_type
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().header.cont().size();
|
||||
}
|
||||
|
||||
//Contributed by leobackes(pr#97)
|
||||
listbox::size_type listbox::column_from_pos ( const point& pos ) const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().column_from_pos(pos.x);
|
||||
}
|
||||
|
||||
void listbox::checkable(bool chkable)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
if(ess.checkable != chkable)
|
||||
{
|
||||
@ -5597,11 +5639,13 @@ namespace nana
|
||||
|
||||
auto listbox::checked() const -> index_pairs
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().lister.pick_items(false);
|
||||
}
|
||||
|
||||
void listbox::clear(size_type cat)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
|
||||
auto origin = ess.content_view->origin();
|
||||
@ -5625,6 +5669,7 @@ namespace nana
|
||||
|
||||
void listbox::clear()
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
|
||||
ess.lister.clear();
|
||||
@ -5639,6 +5684,7 @@ namespace nana
|
||||
|
||||
void listbox::erase(size_type cat)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
|
||||
auto origin = ess.content_view->origin();
|
||||
@ -5663,6 +5709,7 @@ namespace nana
|
||||
|
||||
void listbox::erase()
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
auto & ess = _m_ess();
|
||||
ess.lister.erase();
|
||||
ess.calc_content_size();
|
||||
@ -5671,6 +5718,8 @@ namespace nana
|
||||
|
||||
void listbox::erase(index_pairs indexes)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
|
||||
std::sort(indexes.begin(), indexes.end(), [](const index_pair& pos1, const index_pair& pos2)
|
||||
{
|
||||
return (pos1 > pos2);
|
||||
@ -5720,6 +5769,7 @@ namespace nana
|
||||
if(ip.empty())
|
||||
return ip;
|
||||
|
||||
internal_scope_guard lock;
|
||||
auto * ess = ip._m_ess();
|
||||
auto _where = ip.pos();
|
||||
|
||||
@ -5742,48 +5792,57 @@ namespace nana
|
||||
|
||||
bool listbox::sortable() const
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().header.sortable();
|
||||
}
|
||||
|
||||
void listbox::sortable(bool enable)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().header.sortable(enable);
|
||||
}
|
||||
|
||||
void listbox::set_sort_compare(size_type col, std::function<bool(const std::string&, nana::any*, const std::string&, nana::any*, bool reverse)> strick_ordering)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().header.at(col).weak_ordering = std::move(strick_ordering);
|
||||
}
|
||||
|
||||
/// sort() and ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items
|
||||
void listbox::sort_col(size_type col, bool reverse)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().lister.sort_column(col, &reverse);
|
||||
}
|
||||
|
||||
auto listbox::sort_col() const -> size_type
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().lister.sort_attrs().column;
|
||||
}
|
||||
|
||||
/// potencially ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items
|
||||
void listbox::unsort()
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
this->sort_col(npos, false);
|
||||
}
|
||||
|
||||
bool listbox::freeze_sort(bool freeze)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return !_m_ess().lister.active_sort(!freeze);
|
||||
}
|
||||
|
||||
auto listbox::selected() const -> index_pairs
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
return _m_ess().lister.pick_items(true); // absolute positions, no relative to display
|
||||
}
|
||||
|
||||
void listbox::show_header(bool sh)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().header.visible(sh);
|
||||
_m_ess().update();
|
||||
}
|
||||
@ -5795,6 +5854,7 @@ namespace nana
|
||||
|
||||
void listbox::move_select(bool upwards) ///<Selects an item besides the current selected item in the display.
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().lister.move_select(upwards, true, true);
|
||||
_m_ess().update();
|
||||
}
|
||||
@ -5827,6 +5887,7 @@ namespace nana
|
||||
|
||||
listbox& listbox::category_icon(std::function<void(paint::graphics& graph, const rectangle& rt_icon, bool expanded)> icon_renderer)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().ctg_icon_renderer.swap(icon_renderer);
|
||||
_m_ess().update();
|
||||
return *this;
|
||||
@ -5834,6 +5895,7 @@ namespace nana
|
||||
|
||||
listbox& listbox::category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed)
|
||||
{
|
||||
internal_scope_guard lock;
|
||||
_m_ess().ctg_icon_renderer = [img_expanded, img_collapsed](paint::graphics& graph, const rectangle& rt_icon, bool expanded)
|
||||
{
|
||||
if (expanded)
|
||||
|
||||
@ -89,11 +89,18 @@ namespace nana
|
||||
|
||||
if (shortkey && shortkey < 0x61)
|
||||
shortkey += (0x61 - 0x41);
|
||||
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & last = items.emplace_back(new item_type{std::move(transformed_text), shortkey, shortkey_pos});
|
||||
API::refresh_window(*widget_ptr);
|
||||
return last->menu_obj;
|
||||
#else
|
||||
items.emplace_back(new item_type{ std::move(transformed_text), shortkey, shortkey_pos });
|
||||
|
||||
API::refresh_window(*widget_ptr);
|
||||
|
||||
return this->items.back()->menu_obj;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cancel()
|
||||
|
||||
@ -49,7 +49,7 @@ namespace nana
|
||||
if (value_ptr)
|
||||
{
|
||||
if (unknown_)
|
||||
value_ += (*value_ptr ? 5 : 0);
|
||||
value_ += 5;
|
||||
else
|
||||
value_ = (std::min)(max_, *value_ptr);
|
||||
|
||||
@ -99,7 +99,7 @@ namespace nana
|
||||
auto value_px = (widget_->size().width - border_px * 2);
|
||||
|
||||
//avoid overflow
|
||||
if (value_ < max_)
|
||||
if (unknown_ || (value_ < max_))
|
||||
value_px = static_cast<unsigned>(value_px * (double(value_) / double(max_)));
|
||||
|
||||
if (value_px != value_px_)
|
||||
|
||||
@ -99,12 +99,14 @@ namespace nana
|
||||
|
||||
//Check scroll_area to avoiding division by zero.
|
||||
if (scroll_area)
|
||||
metrics_.value = pos * value_max / scroll_area;
|
||||
metrics_.value = static_cast<std::size_t>(pos * (static_cast<double>(value_max) / scroll_area)); //converting to double to avoid overflow.
|
||||
|
||||
if(metrics_.value < value_max)
|
||||
if (metrics_.value < value_max)
|
||||
{
|
||||
int selfpos = static_cast<int>(metrics_.value * scroll_area / value_max);
|
||||
int nextpos = static_cast<int>((metrics_.value + 1) * scroll_area / value_max);
|
||||
//converting to double to avoid overflow.
|
||||
auto const px_per_value = static_cast<double>(scroll_area) / value_max;
|
||||
int selfpos = static_cast<int>(metrics_.value * px_per_value);
|
||||
int nextpos = static_cast<int>((metrics_.value + 1) * px_per_value);
|
||||
|
||||
if(selfpos != nextpos && (pos - selfpos > nextpos - pos))
|
||||
++metrics_.value;
|
||||
|
||||
@ -199,7 +199,7 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
editor_.select_.a = sel_a_;
|
||||
editor_.select_.b = sel_b_;
|
||||
editor_._m_erase_select();
|
||||
editor_._m_erase_select(false);
|
||||
editor_.select_.a = editor_.select_.b;
|
||||
editor_.points_.caret = sel_a_;
|
||||
}
|
||||
@ -208,7 +208,7 @@ namespace nana{ namespace widgets
|
||||
if (is_enter)
|
||||
{
|
||||
editor_.points_.caret = nana::upoint(0, pos_.y + 1);
|
||||
editor_.backspace(false);
|
||||
editor_.backspace(false, false);
|
||||
}
|
||||
else
|
||||
editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size());
|
||||
@ -218,11 +218,11 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
if (is_enter)
|
||||
{
|
||||
editor_.enter(false);
|
||||
editor_.enter(false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
editor_._m_put(selected_text_);
|
||||
editor_._m_put(selected_text_, false);
|
||||
if (sel_a_ != sel_b_)
|
||||
{
|
||||
editor_.select_.a = sel_a_;
|
||||
@ -234,6 +234,8 @@ namespace nana{ namespace widgets
|
||||
}
|
||||
}
|
||||
|
||||
editor_.textbase().text_changed();
|
||||
|
||||
editor_.reset_caret();
|
||||
}
|
||||
};
|
||||
@ -258,7 +260,7 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
if (is_enter)
|
||||
{
|
||||
editor_.enter(false);
|
||||
editor_.enter(false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -266,9 +268,9 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
editor_.select_.a = sel_a_;
|
||||
editor_.select_.b = sel_b_;
|
||||
editor_._m_erase_select();
|
||||
editor_._m_erase_select(false);
|
||||
}
|
||||
editor_.points_.caret = editor_._m_put(text_); //redo
|
||||
editor_.points_.caret = editor_._m_put(text_, false); //redo
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -277,7 +279,7 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
editor_.points_.caret.x = 0;
|
||||
++editor_.points_.caret.y;
|
||||
editor_.backspace(false);
|
||||
editor_.backspace(false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -286,7 +288,7 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
editor_.select_.a = pos_;
|
||||
editor_.select_.b = upoint(static_cast<unsigned>(lines.back().second - lines.back().first), static_cast<unsigned>(pos_.y + lines.size() - 1));
|
||||
editor_.backspace(false);
|
||||
editor_.backspace(false, false);
|
||||
editor_.select_.a = editor_.select_.b;
|
||||
}
|
||||
else
|
||||
@ -296,12 +298,14 @@ namespace nana{ namespace widgets
|
||||
if (!selected_text_.empty())
|
||||
{
|
||||
editor_.points_.caret = (std::min)(sel_a_, sel_b_);
|
||||
editor_._m_put(selected_text_);
|
||||
editor_._m_put(selected_text_, false);
|
||||
editor_.points_.caret = sel_b_;
|
||||
editor_.select_.a = sel_a_; //Reset the selected text
|
||||
editor_.select_.b = sel_b_;
|
||||
}
|
||||
}
|
||||
|
||||
editor_.textbase().text_changed();
|
||||
editor_.reset_caret();
|
||||
}
|
||||
private:
|
||||
@ -333,8 +337,8 @@ namespace nana{ namespace widgets
|
||||
|
||||
const auto text = editor_._m_make_select_string();
|
||||
|
||||
editor_._m_erase_select();
|
||||
editor_._m_put(text);
|
||||
editor_._m_erase_select(false);
|
||||
editor_._m_put(text, false);
|
||||
|
||||
editor_.select_.a = sel_a_;
|
||||
editor_.select_.b = sel_b_;
|
||||
@ -342,6 +346,7 @@ namespace nana{ namespace widgets
|
||||
editor_.points_.caret = sel_b_;
|
||||
editor_.reset_caret();
|
||||
}
|
||||
editor_.textbase().text_changed();
|
||||
}
|
||||
|
||||
void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b)
|
||||
@ -1004,8 +1009,12 @@ namespace nana{ namespace widgets
|
||||
auto ki = keywords.schemes.find(ds.scheme);
|
||||
if ((ki != keywords.schemes.end()) && ki->second)
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & last = entities.emplace_back();
|
||||
#else
|
||||
entities.emplace_back();
|
||||
auto & last = entities.back();
|
||||
#endif
|
||||
last.begin = c_str + pos;
|
||||
last.end = last.begin + ds.text.size();
|
||||
last.scheme = ki->second.get();
|
||||
@ -1192,9 +1201,9 @@ namespace nana{ namespace widgets
|
||||
switch (key)
|
||||
{
|
||||
case '\b':
|
||||
backspace(); break;
|
||||
backspace(true, true); break;
|
||||
case '\n': case '\r':
|
||||
enter(); break;
|
||||
enter(true, true); break;
|
||||
case keyboard::sync_idel:
|
||||
paste(); break;
|
||||
case keyboard::tab:
|
||||
@ -1681,7 +1690,7 @@ namespace nana{ namespace widgets
|
||||
auto undo_ptr = std::unique_ptr<undo_input_text>{ new undo_input_text(*this, str) };
|
||||
undo_ptr->set_caret_pos();
|
||||
|
||||
_m_put(std::move(str));
|
||||
_m_put(std::move(str), false);
|
||||
|
||||
impl_->undo.push(std::move(undo_ptr));
|
||||
|
||||
@ -1698,7 +1707,9 @@ namespace nana{ namespace widgets
|
||||
}
|
||||
}
|
||||
else
|
||||
put(std::move(str));
|
||||
put(std::move(str), false);
|
||||
|
||||
textbase().text_changed();
|
||||
}
|
||||
|
||||
std::wstring text_editor::text() const
|
||||
@ -1904,6 +1915,7 @@ namespace nana{ namespace widgets
|
||||
|
||||
if (_m_move_select(true))
|
||||
{
|
||||
textbase().text_changed();
|
||||
this->_m_adjust_view();
|
||||
impl_->try_refresh = sync_graph::refresh;
|
||||
return true;
|
||||
@ -2021,7 +2033,7 @@ namespace nana{ namespace widgets
|
||||
impl_->try_refresh = sync_graph::none;
|
||||
}
|
||||
//public:
|
||||
void text_editor::put(std::wstring text)
|
||||
void text_editor::put(std::wstring text, bool perform_event)
|
||||
{
|
||||
if (text.empty())
|
||||
return;
|
||||
@ -2032,14 +2044,16 @@ namespace nana{ namespace widgets
|
||||
|
||||
//Do not forget to assign the _m_erase_select() to caret
|
||||
//because _m_put() will insert the text at the position where the caret is.
|
||||
points_.caret = _m_erase_select();
|
||||
points_.caret = _m_erase_select(false);
|
||||
|
||||
undo_ptr->set_caret_pos();
|
||||
points_.caret = _m_put(std::move(text));
|
||||
points_.caret = _m_put(std::move(text), false);
|
||||
|
||||
impl_->undo.push(std::move(undo_ptr));
|
||||
|
||||
_m_reset_content_size(true);
|
||||
if (perform_event)
|
||||
textbase().text_changed();
|
||||
|
||||
if(graph_)
|
||||
{
|
||||
@ -2060,7 +2074,7 @@ namespace nana{ namespace widgets
|
||||
|
||||
undo_ptr->set_selected_text();
|
||||
if(refresh)
|
||||
points_.caret = _m_erase_select();
|
||||
points_.caret = _m_erase_select(false);
|
||||
|
||||
undo_ptr->set_caret_pos();
|
||||
|
||||
@ -2070,6 +2084,8 @@ namespace nana{ namespace widgets
|
||||
textbase().insert(points_.caret, std::move(ch_str));
|
||||
_m_pre_calc_lines(points_.caret.y, 1);
|
||||
|
||||
textbase().text_changed();
|
||||
|
||||
points_.caret.x ++;
|
||||
|
||||
_m_reset_content_size();
|
||||
@ -2086,6 +2102,10 @@ namespace nana{ namespace widgets
|
||||
|
||||
void text_editor::copy() const
|
||||
{
|
||||
//Disallows copying text if the text_editor is masked.
|
||||
if (mask_char_)
|
||||
return;
|
||||
|
||||
auto text = _m_make_select_string();
|
||||
if (!text.empty())
|
||||
nana::system::dataexch().set(text, API::root(this->window_));
|
||||
@ -2103,7 +2123,7 @@ namespace nana{ namespace widgets
|
||||
|
||||
if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive)
|
||||
{
|
||||
put(move(text));
|
||||
put(move(text), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2121,13 +2141,13 @@ namespace nana{ namespace widgets
|
||||
if (accepts::no_restrict != impl_->capacities.acceptive)
|
||||
{
|
||||
text.erase(i, text.end());
|
||||
put(move(text));
|
||||
put(move(text), true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void text_editor::enter(bool record_undo)
|
||||
void text_editor::enter(bool record_undo, bool perform_event)
|
||||
{
|
||||
if(false == attributes_.multi_lines)
|
||||
return;
|
||||
@ -2135,7 +2155,7 @@ namespace nana{ namespace widgets
|
||||
auto undo_ptr = std::unique_ptr<undo_input_text>(new undo_input_text(*this, std::wstring(1, '\n')));
|
||||
|
||||
undo_ptr->set_selected_text();
|
||||
points_.caret = _m_erase_select();
|
||||
points_.caret = _m_erase_select(false);
|
||||
|
||||
undo_ptr->set_caret_pos();
|
||||
|
||||
@ -2173,21 +2193,24 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
if (impl_->indent.generator)
|
||||
{
|
||||
put(to_wstring(impl_->indent.generator()));
|
||||
put(nana::to_wstring(impl_->indent.generator()), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto & text = textbase.getline(points_.caret.y - 1);
|
||||
auto indent_pos = text.find_first_not_of(L"\t ");
|
||||
if (indent_pos != std::wstring::npos)
|
||||
put(text.substr(0, indent_pos));
|
||||
put(text.substr(0, indent_pos), false);
|
||||
else
|
||||
put(text);
|
||||
put(text, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
_m_reset_content_size();
|
||||
|
||||
if (perform_event)
|
||||
textbase.text_changed();
|
||||
|
||||
auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin());
|
||||
|
||||
if (this->_m_adjust_view() || origin_moved)
|
||||
@ -2211,10 +2234,10 @@ namespace nana{ namespace widgets
|
||||
return; //No characters behind the caret
|
||||
}
|
||||
|
||||
backspace();
|
||||
backspace(true, true);
|
||||
}
|
||||
|
||||
void text_editor::backspace(bool record_undo)
|
||||
void text_editor::backspace(bool record_undo, bool perform_event)
|
||||
{
|
||||
auto undo_ptr = std::unique_ptr<undo_backspace>(new undo_backspace(*this));
|
||||
bool has_to_redraw = true;
|
||||
@ -2254,7 +2277,7 @@ namespace nana{ namespace widgets
|
||||
else
|
||||
{
|
||||
undo_ptr->set_selected_text();
|
||||
points_.caret = _m_erase_select();
|
||||
points_.caret = _m_erase_select(false);
|
||||
undo_ptr->set_caret_pos();
|
||||
}
|
||||
|
||||
@ -2263,6 +2286,11 @@ namespace nana{ namespace widgets
|
||||
|
||||
_m_reset_content_size(false);
|
||||
|
||||
if (perform_event)
|
||||
textbase().text_changed();
|
||||
|
||||
textbase().text_changed();
|
||||
|
||||
if(has_to_redraw)
|
||||
{
|
||||
this->_m_adjust_view();
|
||||
@ -2956,7 +2984,7 @@ namespace nana{ namespace widgets
|
||||
select_.a = select_.b;
|
||||
}
|
||||
|
||||
nana::upoint text_editor::_m_put(std::wstring text)
|
||||
nana::upoint text_editor::_m_put(std::wstring text, bool perform_event)
|
||||
{
|
||||
auto & textbase = this->textbase();
|
||||
auto crtpos = points_.caret;
|
||||
@ -2998,10 +3026,13 @@ namespace nana{ namespace widgets
|
||||
_m_pre_calc_lines(crtpos.y, 1);
|
||||
}
|
||||
|
||||
if (perform_event)
|
||||
textbase.text_changed();
|
||||
|
||||
return crtpos;
|
||||
}
|
||||
|
||||
nana::upoint text_editor::_m_erase_select()
|
||||
nana::upoint text_editor::_m_erase_select(bool perform_event)
|
||||
{
|
||||
nana::upoint a, b;
|
||||
if (get_selected_points(a, b))
|
||||
@ -3023,6 +3054,9 @@ namespace nana{ namespace widgets
|
||||
_m_pre_calc_lines(a.y, 1);
|
||||
}
|
||||
|
||||
if (perform_event)
|
||||
textbase.text_changed();
|
||||
|
||||
select_.a = select_.b;
|
||||
return a;
|
||||
}
|
||||
@ -3235,8 +3269,8 @@ namespace nana{ namespace widgets
|
||||
{//forward
|
||||
undo_ptr->set_caret_pos();
|
||||
|
||||
_m_erase_select();
|
||||
_m_put(text);
|
||||
_m_erase_select(false);
|
||||
_m_put(text, false);
|
||||
|
||||
select_.a = caret;
|
||||
select_.b.y = b.y + (caret.y - a.y);
|
||||
@ -3245,8 +3279,8 @@ namespace nana{ namespace widgets
|
||||
{
|
||||
undo_ptr->set_caret_pos();
|
||||
|
||||
_m_put(text);
|
||||
_m_erase_select();
|
||||
_m_put(text, false);
|
||||
_m_erase_select(false);
|
||||
|
||||
select_.b.y = caret.y;
|
||||
select_.a.y = caret.y - (b.y - a.y);
|
||||
|
||||
@ -750,8 +750,8 @@ namespace nana
|
||||
{
|
||||
if((pos == npos) || (pos >= list_.size()))
|
||||
{
|
||||
pos = list_.size();
|
||||
this->list_.emplace_back();
|
||||
pos = list_.size() - 1;
|
||||
}
|
||||
else
|
||||
list_.emplace(iterator_at(pos));
|
||||
|
||||
@ -377,6 +377,27 @@ namespace drawerbase {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> textbox::getline(std::size_t pos) const
|
||||
{
|
||||
auto result = std::string{};
|
||||
if ( getline(pos, result) )
|
||||
{
|
||||
return { std::move(result) };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string> textbox::getline(std::size_t line_index, std::size_t offset) const
|
||||
{
|
||||
auto result = std::string{};
|
||||
if ( getline(line_index, offset, result) )
|
||||
{
|
||||
return { std::move(result) };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/// Gets the caret position
|
||||
bool textbox::caret_pos(point& pos, bool text_coordinate) const
|
||||
{
|
||||
@ -428,7 +449,7 @@ namespace drawerbase {
|
||||
if(at_caret == false)
|
||||
editor->move_caret_end(false);
|
||||
|
||||
editor->put(to_wstring(text));
|
||||
editor->put(to_wstring(text), true);
|
||||
|
||||
editor->try_refresh();
|
||||
API::update_window(this->handle());
|
||||
@ -445,7 +466,7 @@ namespace drawerbase {
|
||||
if(at_caret == false)
|
||||
editor->move_caret_end(false);
|
||||
|
||||
editor->put(text);
|
||||
editor->put(text, true);
|
||||
|
||||
editor->try_refresh();
|
||||
API::update_window(this->handle());
|
||||
|
||||
@ -1853,7 +1853,6 @@ namespace nana
|
||||
item_locator nl(impl_, xpos, arg.pos.x, arg.pos.y);
|
||||
impl_->attr.tree_cont.for_each<item_locator&>(shape.first, nl);
|
||||
|
||||
|
||||
auto & node_state = impl_->node_state;
|
||||
node_state.pressed_node = nl.node();
|
||||
|
||||
@ -1869,7 +1868,6 @@ namespace nana
|
||||
else
|
||||
return;
|
||||
|
||||
|
||||
impl_->draw(true);
|
||||
API::dev::lazy_refresh();
|
||||
}
|
||||
|
||||
@ -417,7 +417,7 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _nana_cxx_folding_expression
|
||||
#ifndef __cpp_fold_expressions
|
||||
void internationalization::_m_fetch_args(std::vector<std::string>&)
|
||||
{}
|
||||
#endif
|
||||
|
||||
@ -498,41 +498,42 @@ namespace paint
|
||||
{
|
||||
if (nullptr == impl_->handle || nullptr == impl_->handle->context) return {};
|
||||
|
||||
if (text.empty()) return std::unique_ptr<unsigned[]>{new unsigned[1]};
|
||||
auto pxbuf = std::unique_ptr<unsigned[]>{ new unsigned[text.size() ? text.size() : 1] };
|
||||
|
||||
unsigned tab_pixels = impl_->handle->string.tab_length * impl_->handle->string.whitespace_pixels;
|
||||
#if defined(NANA_WINDOWS)
|
||||
int * dx = new int[text.size()];
|
||||
SIZE extents;
|
||||
::GetTextExtentExPoint(impl_->handle->context, text.data(), static_cast<int>(text.size()), 0, 0, dx, &extents);
|
||||
|
||||
auto pxbuf = std::unique_ptr<unsigned[]>{ new unsigned[text.size()] };
|
||||
|
||||
pxbuf[0] = (text[0] == '\t' ? tab_pixels : dx[0]);
|
||||
|
||||
for (std::size_t i = 1; i < text.size(); ++i)
|
||||
if (!text.empty())
|
||||
{
|
||||
pxbuf[i] = (text[i] == '\t' ? tab_pixels : dx[i] - dx[i - 1]);
|
||||
}
|
||||
delete[] dx;
|
||||
unsigned tab_pixels = impl_->handle->string.tab_length * impl_->handle->string.whitespace_pixels;
|
||||
#if defined(NANA_WINDOWS)
|
||||
int * dx = new int[text.size()];
|
||||
SIZE extents;
|
||||
::GetTextExtentExPoint(impl_->handle->context, text.data(), static_cast<int>(text.size()), 0, 0, dx, &extents);
|
||||
|
||||
pxbuf[0] = (text[0] == '\t' ? tab_pixels : dx[0]);
|
||||
|
||||
for (std::size_t i = 1; i < text.size(); ++i)
|
||||
{
|
||||
pxbuf[i] = (text[i] == '\t' ? tab_pixels : dx[i] - dx[i - 1]);
|
||||
}
|
||||
delete[] dx;
|
||||
#elif defined(NANA_X11) && defined(NANA_USE_XFT)
|
||||
|
||||
auto disp = nana::detail::platform_spec::instance().open_display();
|
||||
auto xft = reinterpret_cast<XftFont*>(impl_->handle->font->native_handle());
|
||||
auto disp = nana::detail::platform_spec::instance().open_display();
|
||||
auto xft = reinterpret_cast<XftFont*>(impl_->handle->font->native_handle());
|
||||
|
||||
XGlyphInfo extents;
|
||||
for (std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
if (text[i] != '\t')
|
||||
XGlyphInfo extents;
|
||||
for (std::size_t i = 0; i < text.size(); ++i)
|
||||
{
|
||||
FT_UInt glyphs = ::XftCharIndex(disp, xft, text[i]);
|
||||
::XftGlyphExtents(disp, xft, &glyphs, 1, &extents);
|
||||
pxbuf[i] = extents.xOff;
|
||||
if (text[i] != '\t')
|
||||
{
|
||||
FT_UInt glyphs = ::XftCharIndex(disp, xft, text[i]);
|
||||
::XftGlyphExtents(disp, xft, &glyphs, 1, &extents);
|
||||
pxbuf[i] = extents.xOff;
|
||||
}
|
||||
else
|
||||
pxbuf[i] = tab_pixels;
|
||||
}
|
||||
else
|
||||
pxbuf[i] = tab_pixels;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return pxbuf;
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ namespace system
|
||||
: impl_(new impl_t)
|
||||
{}
|
||||
|
||||
timepiece::timepiece(const volatile timepiece& rhs)
|
||||
timepiece::timepiece(const timepiece& rhs)
|
||||
: impl_(new impl_t(*rhs.impl_))
|
||||
{}
|
||||
|
||||
@ -33,7 +33,7 @@ namespace system
|
||||
delete impl_;
|
||||
}
|
||||
|
||||
timepiece & timepiece::operator=(const volatile timepiece & rhs)
|
||||
timepiece & timepiece::operator=(const timepiece & rhs)
|
||||
{
|
||||
if(this != &rhs)
|
||||
*impl_ = *rhs.impl_;
|
||||
@ -41,7 +41,7 @@ namespace system
|
||||
return *this;
|
||||
}
|
||||
|
||||
void timepiece::start() volatile
|
||||
void timepiece::start() noexcept
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
::QueryPerformanceCounter(&impl_->beg_timestamp);
|
||||
@ -51,7 +51,7 @@ namespace system
|
||||
#endif
|
||||
}
|
||||
|
||||
double timepiece::calc() const volatile
|
||||
double timepiece::calc() const noexcept
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
LARGE_INTEGER li;
|
||||
|
||||
@ -351,10 +351,17 @@ namespace threads
|
||||
}container_;
|
||||
};//end class impl
|
||||
|
||||
pool::pool()
|
||||
: impl_(new impl(4))
|
||||
#ifndef STD_THREAD_NOT_SUPPORTED
|
||||
pool::pool(unsigned thread_number)
|
||||
: impl_(new impl(thread_number ? thread_number : std::thread::hardware_concurrency()))
|
||||
{
|
||||
}
|
||||
#else
|
||||
pool::pool(unsigned thread_number)
|
||||
: impl_(new impl(0))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
pool::pool(pool&& other)
|
||||
: pool()
|
||||
@ -362,11 +369,6 @@ namespace threads
|
||||
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)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include <nana/unicode_bidi.hpp>
|
||||
#include <nana/c++defines.hpp>
|
||||
|
||||
namespace nana
|
||||
{
|
||||
@ -611,8 +612,12 @@ namespace nana
|
||||
|
||||
void unicode_bidi::_m_push_entity(const char_type * begin, const char_type *end, unsigned level, bidi_char bidi_char_type)
|
||||
{
|
||||
#ifdef _nana_std_has_emplace_return_type
|
||||
auto & e = levels_.emplace_back();
|
||||
#else
|
||||
levels_.emplace_back();
|
||||
auto & e = levels_.back();
|
||||
#endif
|
||||
e.begin = begin;
|
||||
e.end = end;
|
||||
e.level = level;
|
||||
@ -946,4 +951,68 @@ namespace nana
|
||||
{
|
||||
return unicode_bidi{}.reorder(text, length);
|
||||
}
|
||||
|
||||
enum class unicode_character_type
|
||||
{
|
||||
format,
|
||||
katakana,
|
||||
aletter,
|
||||
midletter,
|
||||
midnumlet,
|
||||
midnum,
|
||||
numeric,
|
||||
other
|
||||
};
|
||||
|
||||
//http://www.unicode.org/reports/tr29/WordBreakTest.html
|
||||
unicode_character_type unicode_char_type(unsigned long ch)
|
||||
{
|
||||
if ((0x0600 <= ch && ch <= 0x0603) || (0x06DD == ch || 0x070F == ch || 0x17B4 == ch || 0x17B5 == ch) || (0x200C <= ch && ch <= 0x200F) ||
|
||||
(0x202A <= ch && ch <= 0x202E) || (0x2060 <= ch && ch <= 0x2063) || (0x206A <= ch && ch <= 0x206F) || (0xFEFF == ch) || (0xFFF9 <= ch && ch <= 0xFFFB) ||
|
||||
(0x1D173 <= ch && ch <= 0x1D17A) || (0xE0001 == ch) || (0xE0020 <= ch && ch <= 0xE007F))
|
||||
return unicode_character_type::format;
|
||||
|
||||
if ((0x30A1 <= ch && ch <= 0x30FA) || (0x30FC <= ch && ch <= 0x30FF) || (0x31F0 <= ch && ch <= 0x31FF) || (0xFF66 <= ch && ch <= 0xFF9F))
|
||||
return unicode_character_type::katakana;
|
||||
|
||||
if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || (0x00AA == ch || 0x00B5 == ch || 0x00BA == ch) || (0x00C0 <= ch && ch <= 0x00D6) ||
|
||||
(0x00D8 <= ch && ch <= 0x00F6) || (0x00F8 <= ch && ch <= 0x0236) || (0x0250 <= ch || ch <= 0x02C1))
|
||||
return unicode_character_type::aletter;
|
||||
|
||||
if ('\'' == ch || 0x00AD == ch || 0x00B7 == ch || 0x05F4 == ch || 0x2019 == ch || 0x2027 == ch)
|
||||
return unicode_character_type::midletter;
|
||||
|
||||
if ('.' == ch || '\\' == ch || ':' == ch)
|
||||
return unicode_character_type::midnumlet;
|
||||
|
||||
if (0x2024 <= ch && ch <= 0x2026)
|
||||
return unicode_character_type::midnum;
|
||||
|
||||
if (('0' <= ch && ch <= '9') || (0x0660 <= ch && ch <= 0x0669) || (0x06F0 <= ch && ch <= 0x06F9))
|
||||
return unicode_character_type::numeric;
|
||||
|
||||
return unicode_character_type::other;
|
||||
}
|
||||
|
||||
bool unicode_wordbreak(wchar_t left, wchar_t right)
|
||||
{
|
||||
auto l_type = unicode_char_type(left);
|
||||
auto r_type = unicode_char_type(right);
|
||||
|
||||
switch (l_type)
|
||||
{
|
||||
case unicode_character_type::format:
|
||||
case unicode_character_type::midletter:
|
||||
case unicode_character_type::midnumlet:
|
||||
case unicode_character_type::midnum:
|
||||
case unicode_character_type::other:
|
||||
return (r_type != unicode_character_type::format);
|
||||
case unicode_character_type::katakana:
|
||||
return !(unicode_character_type::format == r_type) || (unicode_character_type::katakana == r_type);
|
||||
case unicode_character_type::aletter:
|
||||
case unicode_character_type::numeric:
|
||||
return !(unicode_character_type::format == r_type) || (unicode_character_type::aletter == r_type) || (unicode_character_type::numeric == r_type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}//end namespace nana
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user