nana/include/nana/gui/widgets/treebox.hpp

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