x::w::container
/* ** Copyright 2018-2021 Double Precision, Inc. ** See COPYING for distribution information. */ #include "config.h" #include <x/mpobj.H> #include <x/exception.H> #include <x/destroy_callback.H> #include <x/ref.H> #include <x/obj.H> #include <x/appid.H> #include <x/w/main_window.H> #include <x/w/gridlayoutmanager.H> #include <x/w/gridfactory.H> #include <x/w/input_field.H> #include <x/w/impl/background_color_element.H> #include <x/w/impl/container.H> #include <x/w/impl/container_element.H> #include <x/w/impl/child_element.H> #include <x/w/impl/layoutmanager.H> #include <string> #include <iostream> #include "close_flag.H" std::string x::appid() noexcept { return "customcontainer.examples.w.libcxx.com"; } // Custom container implementation object. // // Use the background_color_elementObj template mixin together with the // container_elementObj mixin. The background_color_elementObj mixin keeps // tab on the custom background color the container displays when it has // pointer focus. class my_custom_container_implObj : public x::w::background_color_elementObj<x::w::container_elementObj <x::w::child_elementObj>> { // Alias for the superclass. typedef x::w::background_color_elementObj<x::w::container_elementObj <x::w::child_elementObj> > superclass_t; // Pretty color gradient for the background color. static x::w::radial_gradient custom_gradient() { x::w::radial_gradient g; g.inner_radius_axis= x::w::radial_gradient::radius_axis::shortest; g.inner_radius_axis= x::w::radial_gradient::radius_axis::shortest; g.gradient={ {0, {0, 0, 0}}, {1, {x::w::rgb::maximum/3, x::w::rgb::maximum/3, x::w::rgb::maximum/3}}}; return g; } public: // Initialize the background_color_elementObj mixin to the theme "30%" // color. my_custom_container_implObj(const x::w::container_impl &parent) : superclass_t{ custom_gradient(), // child_elementObj constructor parameter. parent} { } ~my_custom_container_implObj()=default; private: bool previous_pointer_focus=false; // Override pointer_focus() // pointer_focus() gets invoked when the pointer moves into or out of // the display element, or our custom container. void pointer_focus(ONLY IN_THREAD, const x::w::callback_trigger_t &trigger) override { // Invoke the overridden pointer_focus(). superclass_t::pointer_focus(IN_THREAD, trigger); // pointer_focus() also gets invoked when the pointer // moves in and out of any of the container's child elements. // switching background colors (may) trigger a redraw, which // is relatively expensive. Optimize things a bit, and update // our background color only when we actually lose or gain // pointer focus. auto new_pointer_focus=current_pointer_focus(IN_THREAD); if (new_pointer_focus==previous_pointer_focus) return; previous_pointer_focus=new_pointer_focus; // If the pointer was moved into, install our background color. // If the pointer was moved out of, remove our custom // background color. if (new_pointer_focus) set_background_color(IN_THREAD, x::w::background_color_element<> ::get(IN_THREAD)); else remove_background_color(IN_THREAD); } }; typedef x::ref<my_custom_container_implObj> my_custom_container_impl; // Might as well have a custom public object. class my_custom_containerObj : public x::w::containerObj { public: const my_custom_container_impl impl; // My implementation object. // Constructor my_custom_containerObj(const my_custom_container_impl &impl, const x::w::layout_impl &container_layout_impl) : x::w::containerObj{impl, container_layout_impl}, impl{impl} { } ~my_custom_containerObj()=default; }; typedef x::ref<my_custom_containerObj> my_custom_container; // Create a custom container object. // // The parent parameter comes from the factory. The caller provides the // parent, and is responsible for invoking the factory's create_internally(). my_custom_container new_custom_container(const x::w::container_impl &parent) { // Use it to construct our custom container // implementation object. auto impl=my_custom_container_impl::create(parent); // Create a new layout manager object. x::w::new_gridlayoutmanager new_container_lm; // Pass the new custom container to // new_container_lm.create() in order to construct // the internal layout manager implementation object // for the new container. x::w::layout_impl new_layout_impl=new_container_lm.create(impl); // Construct the container public object. return my_custom_container::create(impl, new_layout_impl); } void testcustomcontainer() { x::destroy_callback::base::guard guard; auto close_flag=close_flag_ref::create(); auto main_window=x::w::main_window::create ([&] (const auto &main_window) { auto layout=main_window->gridlayout(); auto factory=layout->append_row(); // Give ample, generous, 10mm padding for our // custom container. factory->padding(10.0); // Obtain the parent container from the factory. x::w::container_impl p=factory->get_container_impl(); auto new_container=new_custom_container(p); // Before calling created_internally(), in order // to complete the process, we should initialize // the new container's contents. auto custom_grid_layout=new_container->gridlayout(); auto inner_factory=custom_grid_layout->append_row(); // Also give the sample, generous, 10mm padding around // the input field in our custom container. inner_factory->padding(10.0); x::w::input_field_config config{10}; inner_factory->create_input_field("", config); // And now that the custom container's contents // are all set, formally install the custom container // in *it's* container. factory->created_internally(new_container); }, x::w::new_gridlayoutmanager{}); main_window->set_window_title("Custom container"); guard(main_window->connection_mcguffin()); main_window->on_disconnect([] { _exit(1); }); main_window->on_delete ([close_flag] (THREAD_CALLBACK, const auto &ignore) { close_flag->close(); }); main_window->show_all(); close_flag->wait(); } int main(int argc, char **argv) { try { testcustomcontainer(); } catch (const x::exception &e) { e->caught(); exit(1); } return 0; }
x::w::container_elementObj
template mixinCreating a custom container widget involves several additional steps beyond the ones for creating a custom widget.
Declare a custom implementation object class that's derived from
x::w::child_elementObj
and
x::w::container_elementObj
;
typically x::w::container_elementObj<x::w::child_elementObj>
.
x::w::container_elementObj
is a
template mixin that creates an implementation object class for
a container. The template inherits from the class specified
by its template parameter, and the template forwards its constructor
to its superclass. The template also multiply-inherits from
x::w::containerObj::implObj
.
Declare a custom public object class that's derived from
x::w::containerObj
.
Creating each custom container requires obtaining a factory from an existing container, like for an ordinary widget. This is followed by some additional steps, similar to the ones taken when creating a custom widget.
The first step is the same as when
creating a
custom widget:
use the
factory's get_container_impl
() method.
to obtain an
x::w::container_impl
representing the parent container, and then use it to create the
custom implementation object class instance.
Prepare a new container specifier, like
an x::w::new_gridlayoutmanager
,
in the same way it's used to specify the attributes of a new
container.
Invoke the new container specifier's create
()
method and pass the new implementation object as its parameter.
This create
() returns
an x::w::layout_impl
,
a reference handler for the new container's layout manager's
implementation class.
Construct a new instance of the public object class which is derived
from x::w::containerObj
.
x::w::containerObj
's constructor takes
two parameters, the implementation class constructed by
x::w::container_elementObj
template mixin,
and the x::w::layout_impl
.
The usual approach is pass both the new implementation object and the layout manager implementation object to the new public object; with its constructor forwarding these parameters to its superclass, in addition to storing the implementation object for its own use.
At this point, the new container is ready to be populated with its
contents. The last step is to invoke the original factory's
created_internally
() method, just like when
creating a
custom widget.
This can be done immediately and before populating the contents of
the new container, but optimally the new container's contents should
be created first.