529 lines
17 KiB
C++
529 lines
17 KiB
C++
/**
|
|
* A Tree Box Implementation
|
|
* Nana C++ Library(http://www.nanapro.org)
|
|
* Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
|
|
*
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
* @file nana/gui/widgets/treebox.hpp
|
|
* @brief
|
|
* The treebox organizes the nodes by a key string.
|
|
* The treebox would have a vertical scrollbar if there are too many nodes
|
|
* to display. It does not have an horizontal scrollbar:
|
|
* the widget will adjust the node's displaying position for fitting.
|
|
*/
|
|
|
|
#ifndef NANA_GUI_WIDGETS_TREEBOX_HPP
|
|
#define NANA_GUI_WIDGETS_TREEBOX_HPP
|
|
|
|
#include <nana/push_ignore_diagnostic>
|
|
#include "widget.hpp"
|
|
#include "detail/compset.hpp"
|
|
#include "detail/tree_cont.hpp"
|
|
#include <nana/gui/timer.hpp>
|
|
#include <nana/any.hpp>
|
|
#include <nana/pat/cloneable.hpp>
|
|
#include <stdexcept>
|
|
|
|
namespace nana
|
|
{
|
|
class treebox;
|
|
|
|
namespace drawerbase
|
|
{
|
|
namespace treebox
|
|
{
|
|
enum class component
|
|
{
|
|
begin, expander = begin, crook, icon, text, bground, end
|
|
};
|
|
|
|
struct node_image_tag
|
|
{
|
|
nana::paint::image normal;
|
|
nana::paint::image hovered;
|
|
nana::paint::image expanded;
|
|
};
|
|
|
|
struct node_attribute
|
|
{
|
|
bool has_children; ///< Determines whether the node has visible children
|
|
bool expended;
|
|
checkstate checked;
|
|
bool selected;
|
|
bool mouse_pointed;
|
|
nana::paint::image icon_normal;
|
|
nana::paint::image icon_hover;
|
|
nana::paint::image icon_expanded;
|
|
::std::string text;
|
|
};
|
|
|
|
struct scheme
|
|
: public widget_geometrics
|
|
{
|
|
color_proxy item_bg_selected{ static_cast<color_rgb>(0xD5EFFC) }; ///< item selected: background color
|
|
color_proxy item_fg_selected{ static_cast<color_rgb>(0x99DEFD) }; ///< item selected: foreground color
|
|
color_proxy item_bg_highlighted{ static_cast<color_rgb>(0xE8F5FD) }; ///< item highlighted: background color
|
|
color_proxy item_fg_highlighted{ static_cast<color_rgb>(0xD8F0FA) }; ///< item highlighted: foreground color
|
|
color_proxy item_bg_selected_and_highlighted{ static_cast<color_rgb>(0xC4E8FA) }; ///< item selected and highlighted: background color
|
|
color_proxy item_fg_selected_and_highlighted{ static_cast<color_rgb>(0xB6E6FB) }; ///< item selected and highlighted: foreground color
|
|
|
|
unsigned item_offset{ 16 }; ///< item position displacement in pixels
|
|
unsigned text_offset{ 4 }; ///< text position displacement in pixels
|
|
unsigned icon_size{ 16 }; ///< icon size in pixels
|
|
unsigned crook_size{ 16 }; ///< crook size in pixels (TODO: the function that draw the crook doesn't scale the shape)
|
|
|
|
unsigned indent_displacement{ 18 }; ///< children position displacement in pixels (def=18 (before was 10))
|
|
};
|
|
|
|
typedef widgets::detail::compset<component, node_attribute> compset_interface;
|
|
typedef widgets::detail::compset_placer<component, node_attribute, scheme> compset_placer_interface;
|
|
|
|
class renderer_interface
|
|
{
|
|
public:
|
|
typedef drawerbase::treebox::component component;
|
|
typedef ::nana::paint::graphics& graph_reference;
|
|
typedef drawerbase::treebox::compset_interface compset_interface;
|
|
typedef compset_interface::item_attribute_t item_attribute_t;
|
|
typedef compset_interface::comp_attribute_t comp_attribute_t;
|
|
|
|
virtual ~renderer_interface() = default;
|
|
|
|
virtual void begin_paint(::nana::widget&) = 0;
|
|
virtual void bground(graph_reference, const compset_interface *) const = 0;
|
|
virtual void expander(graph_reference, const compset_interface *) const = 0;
|
|
virtual void crook(graph_reference, const compset_interface *) const = 0;
|
|
virtual void icon(graph_reference, const compset_interface *) const = 0;
|
|
virtual void text(graph_reference, const compset_interface *) const = 0;
|
|
};
|
|
|
|
class item_proxy;
|
|
|
|
class trigger
|
|
:public drawer_trigger
|
|
{
|
|
class implementation;
|
|
class item_locator;
|
|
public:
|
|
struct treebox_node_type
|
|
{
|
|
treebox_node_type();
|
|
treebox_node_type(std::string);
|
|
treebox_node_type& operator=(const treebox_node_type&);
|
|
|
|
::std::string text;
|
|
nana::any value;
|
|
bool expanded;
|
|
bool hidden;
|
|
checkstate checked;
|
|
::std::string img_idstr;
|
|
};
|
|
|
|
struct pseudo_node_type{};
|
|
|
|
using tree_cont_type = widgets::detail::tree_cont<treebox_node_type>;
|
|
using node_type = tree_cont_type::node_type;
|
|
|
|
trigger();
|
|
~trigger();
|
|
|
|
implementation * impl() const;
|
|
|
|
void check(node_type*, checkstate);
|
|
|
|
pat::cloneable<renderer_interface>& renderer() const;
|
|
|
|
void placer(::nana::pat::cloneable<compset_placer_interface>&&);
|
|
const ::nana::pat::cloneable<compset_placer_interface>& placer() const;
|
|
|
|
node_type* insert(node_type*, const std::string& key, std::string&&);
|
|
node_type* insert(const std::string& path, std::string&&);
|
|
|
|
node_image_tag& icon(const ::std::string&);
|
|
void icon_erase(const ::std::string&);
|
|
void node_icon(node_type*, const ::std::string& id);
|
|
unsigned node_width(const node_type*) const;
|
|
|
|
bool rename(node_type*, const char* key, const char* name);
|
|
|
|
private:
|
|
//Overrides drawer_trigger methods
|
|
void attached(widget_reference, graph_reference) override;
|
|
void detached() override;
|
|
void refresh(graph_reference) override;
|
|
void dbl_click(graph_reference, const arg_mouse&) override;
|
|
void mouse_down(graph_reference, const arg_mouse&) override;
|
|
void mouse_up(graph_reference, const arg_mouse&) override;
|
|
void mouse_move(graph_reference, const arg_mouse&) override;
|
|
void mouse_wheel(graph_reference, const arg_wheel&) override;
|
|
void mouse_leave(graph_reference, const arg_mouse&) override;
|
|
void resized(graph_reference, const arg_resized&) override;
|
|
void key_press(graph_reference, const arg_keyboard&) override;
|
|
void key_char(graph_reference, const arg_keyboard&) override;
|
|
private:
|
|
implementation * const impl_;
|
|
}; //end class trigger
|
|
|
|
|
|
/// \brief A proxy for accessing the node. The key string is case sensitive.
|
|
class item_proxy
|
|
: public std::iterator<std::input_iterator_tag, item_proxy>
|
|
{
|
|
public:
|
|
item_proxy() = default; ///< The default constructor creates an end iterator.
|
|
|
|
//Undocumented constructor.
|
|
item_proxy(trigger*, trigger::node_type*);
|
|
|
|
/// Append a child.
|
|
item_proxy append(const ::std::string& key, ::std::string name);
|
|
|
|
/// Append a child with a specified value (user object.).
|
|
template<typename T>
|
|
item_proxy append(const ::std::string& key, ::std::string name, T&& t)
|
|
{
|
|
item_proxy ip = append(key, std::move(name));
|
|
if(false == ip.empty())
|
|
ip.value(std::forward<T>(t));
|
|
return ip;
|
|
}
|
|
|
|
/// Return true if the proxy does not refer to a node, as an end iterator.
|
|
bool empty() const;
|
|
|
|
/// \brief Return the distance between the ROOT node and this node.
|
|
/// @return only available when empty() is false.
|
|
std::size_t level() const;
|
|
|
|
/// Return the check state
|
|
bool checked() const;
|
|
|
|
/// Set the check state, and it returns itself.
|
|
item_proxy& check(bool);
|
|
|
|
/// Clears the child nodes
|
|
item_proxy& clear();
|
|
|
|
/// Return true when the node is expanded \todo change to expanded ??
|
|
bool expanded() const;
|
|
|
|
/// Expand/Shrink children of the node, and returns itself. \todo change to expand ??
|
|
item_proxy& expand(bool);
|
|
|
|
/// Return true when the node is selected.
|
|
bool selected() const;
|
|
|
|
/// Select the node, and returns itself..
|
|
item_proxy& select(bool);
|
|
|
|
/// Return true when the node is hidden.
|
|
bool hidden() const;
|
|
|
|
/// Hide the node, and returns itself.
|
|
item_proxy& hide(bool);
|
|
|
|
/// Return the icon.
|
|
const ::std::string& icon() const;
|
|
|
|
/// Set the icon, and returns itself..
|
|
item_proxy& icon(const ::std::string& id);
|
|
|
|
/// Return the text.
|
|
const ::std::string& text() const;
|
|
|
|
/// Set the text, and returns itself.
|
|
item_proxy& text(const ::std::string&);
|
|
|
|
/// Set a new key, and returns itself..
|
|
item_proxy& key(const ::std::string& s);
|
|
|
|
/// Return the key.
|
|
const ::std::string& key() const;
|
|
|
|
std::size_t size() const; ///< Returns the number of child nodes.
|
|
|
|
/// Return the first child of the node.
|
|
item_proxy child() const;
|
|
|
|
/// Return the owner of the node.
|
|
item_proxy owner() const;
|
|
|
|
/// Return the sibling of the node.
|
|
item_proxy sibling() const;
|
|
|
|
/// Return the first child of the node
|
|
item_proxy begin() const;
|
|
|
|
/// An end node.
|
|
item_proxy end() const;
|
|
|
|
/// Makes an action for each sub item recursively, returns the item that stops the action where action returns false.
|
|
item_proxy visit_recursively(std::function<bool(item_proxy)> action);
|
|
|
|
bool operator==(const ::std::string& s) const; ///< Compare the text of node with s.
|
|
bool operator==(const char* s ) const; ///< Compare the text of node with s.
|
|
bool operator==(const wchar_t* s ) const; ///< Compare the text of node with s.
|
|
|
|
/// Behavior of Iterator
|
|
item_proxy& operator=(const item_proxy&);
|
|
|
|
/// Behavior of Iterator
|
|
item_proxy & operator++();
|
|
|
|
/// Behavior of Iterator
|
|
item_proxy operator++(int);
|
|
|
|
/// Behavior of Iterator
|
|
item_proxy& operator*();
|
|
|
|
/// Behavior of Iterator
|
|
const item_proxy& operator*() const;
|
|
|
|
/// Behavior of Iterator
|
|
item_proxy* operator->();
|
|
|
|
/// Behavior of Iterator
|
|
const item_proxy* operator->() const;
|
|
|
|
/// Behavior of Iterator
|
|
bool operator==(const item_proxy&) const;
|
|
|
|
/// Behavior of Iterator
|
|
bool operator!=(const item_proxy&) const;
|
|
|
|
template<typename T>
|
|
const T * value_ptr() const
|
|
{
|
|
return any_cast<T>(&_m_value());
|
|
}
|
|
|
|
template<typename T>
|
|
T * value_ptr()
|
|
{
|
|
return any_cast<T>(&_m_value());
|
|
}
|
|
|
|
template<typename T>
|
|
const T& value() const
|
|
{
|
|
auto p = any_cast<T>(&_m_value());
|
|
if(nullptr == p)
|
|
throw std::runtime_error("treebox::value<T>() Invalid type of value.");
|
|
return *p;
|
|
}
|
|
|
|
template<typename T>
|
|
T& value()
|
|
{
|
|
auto p = any_cast<T>(&_m_value());
|
|
if (nullptr == p)
|
|
throw std::runtime_error("treebox::value<T>() Invalid type of value.");
|
|
return *p;
|
|
}
|
|
|
|
template<typename T>
|
|
item_proxy & value(T&& t)
|
|
{
|
|
_m_value() = std::forward<T>(t);
|
|
return *this;
|
|
}
|
|
|
|
// Undocumented methods for internal use
|
|
trigger::node_type * _m_node() const;
|
|
private:
|
|
nana::any& _m_value();
|
|
const nana::any& _m_value() const;
|
|
private:
|
|
trigger * trigger_{nullptr};
|
|
trigger::node_type * node_{nullptr};
|
|
};//end class item_proxy
|
|
}//end namespace treebox
|
|
}//end namespace drawerbase
|
|
|
|
/// a type of treebox event parameter
|
|
struct arg_treebox
|
|
: public event_arg
|
|
{
|
|
treebox& widget; ///< where the event occurs
|
|
drawerbase::treebox::item_proxy & item; ///< the operated node
|
|
bool operated; ///< operation state of the event
|
|
|
|
arg_treebox(treebox&, drawerbase::treebox::item_proxy&, bool operated);
|
|
};
|
|
|
|
namespace drawerbase
|
|
{
|
|
namespace treebox
|
|
{
|
|
struct treebox_events
|
|
: public general_events
|
|
{
|
|
basic_event<arg_treebox> expanded; ///< a user expands or shrinks a node
|
|
basic_event<arg_treebox> checked; ///< a user checks or unchecks a node
|
|
basic_event<arg_treebox> selected; ///< a user selects or unselects a node
|
|
basic_event<arg_treebox> hovered; ///< a user moves the cursor over a node
|
|
basic_event<arg_treebox> hidden; ///< a user hides or shows a node
|
|
};
|
|
}//end namespace treebox
|
|
}//end namespace drawerbase
|
|
|
|
/// \brief Displays a hierarchical list of items, such as the files and directories on a disk.
|
|
/// See also in [documentation](http://nanapro.org/en-us/documentation/widgets/treebox.htm)
|
|
class treebox
|
|
:public widget_object <category::widget_tag,
|
|
drawerbase::treebox::trigger,
|
|
drawerbase::treebox::treebox_events, drawerbase::treebox::scheme>
|
|
{
|
|
public:
|
|
/// A type refers to the item and is also used to iterate through the nodes.
|
|
typedef drawerbase::treebox::item_proxy item_proxy;
|
|
|
|
/// state images for the node
|
|
typedef drawerbase::treebox::node_image_tag node_image_type;
|
|
|
|
/// The interface of treebox user-defined item renderer
|
|
typedef drawerbase::treebox::renderer_interface renderer_interface;
|
|
|
|
/// The interface of treebox compset_placer to define the position of node components
|
|
typedef drawerbase::treebox::compset_placer_interface compset_placer_interface;
|
|
|
|
/// The default constructor without creating the widget.
|
|
treebox();
|
|
|
|
/// \brief The construct that creates a widget.
|
|
/// @param wd A handle to the parent window of the widget being created.
|
|
/// @param visible specifying the visibility after creating.
|
|
treebox(window wd, bool visible);
|
|
|
|
/// \brief The construct that creates a widget.
|
|
/// @param wd A handle to the parent window of the widget being created.
|
|
/// @param r the size and position of the widget in its parent window coordinate.
|
|
/// @param visible specifying if visible after creating.
|
|
treebox(window, const nana::rectangle& = rectangle(), bool visible = true);
|
|
|
|
template<typename ItemRenderer>
|
|
treebox & renderer(const ItemRenderer & rd) ///< set user-defined node renderer
|
|
{
|
|
get_drawer_trigger().renderer() = ::nana::pat::cloneable<renderer_interface>{rd};
|
|
return *this;
|
|
}
|
|
|
|
const nana::pat::cloneable<renderer_interface> & renderer() const; ///< get user-defined node renderer
|
|
|
|
template<typename Placer>
|
|
treebox & placer(const Placer & r) ///< location of a node components
|
|
{
|
|
get_drawer_trigger().placer(::nana::pat::cloneable<compset_placer_interface>(r));
|
|
return *this;
|
|
}
|
|
|
|
const nana::pat::cloneable<compset_placer_interface> & placer() const;
|
|
|
|
/// \brief Enable the widget to be draws automatically when it is operated.
|
|
///
|
|
/// The treebox automatically redraws after certain operations, but,
|
|
/// under some circumstances, it is good to disable the automatic drawing mode,
|
|
/// for example, before adding nodes in a loop, disable the mode avoiding
|
|
/// frequent and useless refresh for better performance, and then, after
|
|
/// the operations, enable the automatic redraw mode again.
|
|
/// @param enable bool whether to enable.
|
|
void auto_draw(bool enable);
|
|
|
|
/// Prevents drawing during execution.
|
|
template<typename Function>
|
|
void avoid_drawing(Function fn)
|
|
{
|
|
this->auto_draw(false);
|
|
try
|
|
{
|
|
fn();
|
|
}
|
|
catch (...)
|
|
{
|
|
this->auto_draw(true);
|
|
throw;
|
|
}
|
|
this->auto_draw(true);
|
|
}
|
|
|
|
/// \brief Enable the checkboxs for each item of the widget.
|
|
/// @param enable bool indicates whether to show or hide the checkboxs.
|
|
treebox & checkable(bool enable);
|
|
|
|
|
|
bool checkable() const; ///< Are the checkboxs are enabled?
|
|
|
|
/// Clears the contents
|
|
void clear();
|
|
|
|
/// \brief Creates an icon scheme with the specified name.
|
|
///
|
|
/// The icon scheme includes 3 images for node states.
|
|
/// These states are 'normal', 'hovered' and 'expanded'.
|
|
/// If 'hovered' or 'expanded' are not set, it uses 'normal' state image for these 2 states.
|
|
/// See also in [documentation](http://nanapro.org/en-us/help/widgets/treebox.htm)
|
|
/// @param id The name of an icon scheme. If the name is not existing, it creates a new scheme for the name.
|
|
/// @return The reference of node image scheme corresponding with the specified id.
|
|
node_image_type& icon(const ::std::string& id);
|
|
|
|
void icon_erase(const ::std::string& id);
|
|
|
|
item_proxy find(const ::std::string& keypath); ///< Find an item through a specified keypath.
|
|
|
|
/// Inserts a new node to treebox, but if the keypath exists change and returns the existing node.
|
|
item_proxy insert(const ::std::string& path_key, ///< specifies the node hierarchy
|
|
::std::string title ///< used for displaying
|
|
);
|
|
|
|
/// Inserts a new node to treebox, but if the keypath exists change and returns the existing node.
|
|
item_proxy insert( item_proxy pos, ///< the parent item node
|
|
const ::std::string& key, ///< specifies the new node
|
|
::std::string title ///< title used for displaying in the new node.
|
|
);
|
|
|
|
item_proxy erase(item_proxy i); ///< Removes the node at i and return the Item proxy following the removed node
|
|
|
|
void erase(const ::std::string& keypath); ///< Removes the node by the key path.
|
|
|
|
::std::string make_key_path(item_proxy i, const ::std::string& splitter) const;///<returns the key path
|
|
|
|
item_proxy selected() const; ///< returns the selected node
|
|
|
|
/// Scrolls a specified item into view.
|
|
/**
|
|
* @param item An item to be requested.
|
|
* @param bearing The position where the item to be positioned in the view.
|
|
*/
|
|
void scroll_into_view(item_proxy item, align_v bearing);
|
|
|
|
/// Scrolls a specified item into view.
|
|
/**
|
|
* @param item An item to be requested.
|
|
*/
|
|
void scroll_into_view(item_proxy item);
|
|
|
|
/// Gets the current hovered node.
|
|
item_proxy hovered(bool exclude_expander) const;
|
|
|
|
|
|
/// Enable/disable the interactions (selection, click, ...) on the entire line of the treebox.
|
|
/**
|
|
* @param enable bool whether to enable.
|
|
*/
|
|
void use_entire_line(bool enable);
|
|
|
|
/// Return the first node of treebox
|
|
item_proxy first() const;
|
|
private:
|
|
std::shared_ptr<scroll_operation_interface> _m_scroll_operation() override;
|
|
|
|
};//end class treebox
|
|
}//end namespace nana
|
|
|
|
#include <nana/pop_ignore_diagnostic>
|
|
|
|
#endif
|