Timeline: arrange for a tabbed notebook to hold the timeline widgets

...and remove all the leftover test and research code from 2016
which was archived to /research some days ago
in 3f87ef43ec
This commit is contained in:
Fischlurch 2018-10-13 21:56:10 +02:00
parent 202b1e4dbd
commit 6e18452c37
11 changed files with 129 additions and 417 deletions

View file

@ -105,7 +105,7 @@ namespace dialog {
/**
* Helper widget to simplify construction and wiring of a [Notebook] widget.
* Gtk::Notebook is quite powerful container foundation to build complex dialog widgets with
* multiple pages on tabs. However, the construction, wiring an setup is notoriously tedious,
* multiple pages on tabs. However, the construction, wiring and setup is notoriously tedious,
* due to the repetitiveness and the sheer amount of child widgets spread over various pages.
*
* This design draft is an attempt to mitigate the required boilerplate, without overly much
@ -157,10 +157,10 @@ namespace dialog {
/**
* Ticket #1099 : perform a dummy round-trip to verify Proc-GUI integration.
* This routine invokes the command `test_meta_displayInfo` down in Proc-Layer, passing the settings
* from the radio buttons to select the flavour of feedback, and the text for feedback content.
* The expected behaviour is for the invoked command to send a feedback via UI-Bus towards
* the ErrorLogDisplay within the InfoboxPanel.
* This routine invokes the command `test_meta_displayInfo` and friends down in Proc-Layer,
* passing the settings from the radio buttons to select the flavour of feedback, and the text
* for feedback content. The expected behaviour is for the invoked command to send a feedback
* via UI-Bus towards the ErrorLogDisplay within the InfoboxPanel.
*/
struct Page1 : Page
{

View file

@ -42,7 +42,8 @@ namespace panel{
,Gdl::DockItem& dockItem
,const gchar* longName
,const gchar* stockID)
: panelManager_(panelManager)
: Gtk::Box{Gtk::ORIENTATION_VERTICAL}
, panelManager_(panelManager)
, dockItem_(dockItem)
, panelBar_(*this, stockID)
{

View file

@ -36,18 +36,18 @@
#include <gdlmm.h>
namespace gui {
namespace workspace {
class PanelManager;
class WorkspaceWindow;
}
namespace panel {
namespace workspace {
class PanelManager;
class WorkspaceWindow;
}
namespace panel {
/**
* The base class for all dockable panels.
*/
class Panel
: public Gtk::VBox
: public Gtk::Box
{
protected:

View file

@ -33,105 +33,48 @@
#include "gui/gtk-base.hpp"
#include "gui/panel/timeline-panel.hpp"
#include "gui/timeline/timeline-widget.hpp"
#include "gui/timeline/timeline-widget-empty.hpp"
//#include "gui/workspace/workspace-window.hpp"
#include "gui/ui-bus.hpp"
#include "lib/format-string.hpp"
#include "lib/format-cout.hpp"
//#include "gui/ui-bus.hpp"
//#include "lib/format-string.hpp"
//#include "lib/format-cout.hpp"
//#include "lib/util.hpp"
#include <algorithm>
#include <cstdlib>
#include <string>
//#include <algorithm>
//#include <cstdlib>
//#include <string>
using util::_Fmt;
//using util::_Fmt;
//using std::shared_ptr;
//using std::weak_ptr;
using std::make_unique;
//using util::contains;
using Gtk::Widget;
using sigc::mem_fun;
using sigc::ptr_fun;
using std::string;
using std::rand;
using std::max;
//using Gtk::Widget;
//using sigc::mem_fun;
//using sigc::ptr_fun;
//using std::string;
namespace gui {
namespace panel {
using timeline::TimelineWidget;
using timeline::TimelineWidgetEmpty;
TimelinePanel::TimelinePanel (workspace::PanelManager& panelManager,
Gdl::DockItem& dockItem)
: Panel(panelManager, dockItem, getTitle(), getStockID())
, twoParts_(Gtk::ORIENTATION_VERTICAL)
, buttons_()
, frame_("Gtk::Layout Experiments")
, scroller_()
, canvas_()
, tabs_{}
, pages_{}
{
twoParts_.pack_start(buttons_, Gtk::PACK_SHRINK);
twoParts_.pack_start(frame_);
buttons_.set_layout(Gtk::BUTTONBOX_START);
// buttons to trigger experiments
button_1_.set_label("_place");
button_1_.set_use_underline();
button_1_.set_tooltip_markup("<b>Experiment 1</b>:\nplace new child widget\nat random position on the canvas");
button_1_.signal_clicked().connect(
mem_fun(*this, &TimelinePanel::experiment_1));
buttons_.add(button_1_);
button_2_.set_label("_move");
button_2_.set_use_underline();
button_2_.set_tooltip_markup("<b>Experiment 2</b>:\nmove all child widgets randomly");
button_2_.signal_clicked().connect(
mem_fun(*this, &TimelinePanel::experiment_2));
buttons_.add(button_2_);
button_3_.set_label("a_lign");
button_3_.set_use_underline();
button_3_.set_tooltip_markup("<b>Experiment 3</b>:\nalign all child widgets in a row\nwith silight random vertical offset");
button_3_.signal_clicked().connect(
mem_fun(*this, &TimelinePanel::experiment_3));
buttons_.add(button_3_);
button_4_.set_label("_grow");
button_4_.set_use_underline();
button_4_.set_tooltip_markup("<b>Experiment 4</b>:\nextend arbitrary child widget's text");
button_4_.signal_clicked().connect(
mem_fun(*this, &TimelinePanel::experiment_4));
buttons_.add(button_4_);
button_5_.set_label("_kill");
button_5_.set_use_underline();
button_5_.set_tooltip_markup("<b>Experiment 5</b>:\nkill arbitrary child widget");
button_5_.signal_clicked().connect(
mem_fun(*this, &TimelinePanel::experiment_5));
buttons_.add(button_5_);
toggleDraw_.set_label("draw");
toggleDraw_.signal_clicked().connect(
[this]() {
canvas_.enableDraw (this->toggleDraw_.get_active());
});
buttons_.add(toggleDraw_);
//(End)buttons...
frame_.add(scroller_);
frame_.set_border_width(5);
scroller_.set_shadow_type(Gtk::SHADOW_NONE);
scroller_.set_border_width(10);
scroller_.add(canvas_);
canvas_.adjustSize();
pages_.emplace_back (new TimelineWidgetEmpty{});
// show everything....
this->add(twoParts_);
this->add(tabs_);
this->show_all();
}
@ -150,236 +93,10 @@ namespace panel {
void
TimelinePanel::addTimeline (std::unique_ptr<timeline::TimelineWidget>&& widget)
TimelinePanel::addTimeline (PageHandle&& pTimelineWidget)
{
UNIMPLEMENTED ("take ownership of the widget and place it into a new tab");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1016 : WIP kill everything below....
void
TimelinePanel::experiment_1()
{
frame_.set_label("Experiment 1... PLACE");
ChildEx* chld = makeChld();
childz_.push_back(chld);
uint x = rand() % 1000;
uint y = rand() % 500;
canvas_.put(*chld, x, y);
chld->show();
canvas_.adjustSize();
}
void
TimelinePanel::experiment_2()
{
frame_.set_label("Experiment 2... MOVE");
for (Widget* chld : childz_)
{
uint x = canvas_.child_property_x(*chld);
uint y = canvas_.child_property_y(*chld);
int deltaX = -20 + rand() % 41;
int deltaY = -15 + rand() % 31;
x = uint(max (0, int(x) + deltaX));
y = uint(max (0, int(y) + deltaY));
canvas_.move (*chld, x,y);
}
canvas_.adjustSize();
}
void
TimelinePanel::experiment_3()
{
frame_.set_label("Experiment 3... ALIGN");
uint pos=0;
for (Widget* chld : childz_)
{
uint y = rand() % 30;
canvas_.move (*chld, pos, y);
int width = chld->get_allocated_width();
pos += 0.6 * width;
}
canvas_.adjustSize();
}
void
TimelinePanel::experiment_4()
{
frame_.set_label("Experiment 4... GROW");
uint selector = rand() % childz_.size();
ChildEx& toGrow = *childz_[selector];
toGrow.set_label ("***"+toGrow.get_label()+"***");
}
void
TimelinePanel::experiment_5()
{
frame_.set_label("Experiment 5... KILL");
uint killPos = rand() % childz_.size();
ChildV::iterator killThat(&childz_[killPos]);
ChildEx* victim = *killThat;
childz_.erase (killThat);
canvas_.remove (*victim);
delete victim;
}
void
Canvas::enableDraw (bool yes)
{
shallDraw_ = yes;
// force redrawing of the visible area...
auto win = get_window();
if (win)
{
int w = get_allocation().get_width();
int h = get_allocation().get_height();
Gdk::Rectangle rect{0, 0, w, h};
win->invalidate_rect(rect, false);
}
}
void
Canvas::adjustSize()
{
recalcExtension_ = true;
}
void
Canvas::determineExtension()
{
if (not recalcExtension_) return;
uint extH=20, extV=20;
Gtk::Container::ForeachSlot callback
= [&](Gtk::Widget& chld)
{
auto alloc = chld.get_allocation();
uint x = alloc.get_x();
uint y = alloc.get_y();
x += alloc.get_width();
y += alloc.get_height();
extH = max (extH, x);
extV = max (extV, y);
};
foreach(callback);
recalcExtension_ = false;
set_size (extH, extV);
}
bool
Canvas::on_draw(Cairo::RefPtr<Cairo::Context> const& cox)
{
if (shallDraw_)
{
uint extH, extV;
determineExtension();
get_size (extH, extV);
auto adjH = get_hadjustment();
auto adjV = get_vadjustment();
double offH = adjH->get_value();
double offV = adjV->get_value();
cox->save();
cox->translate(-offH, -offV);
// draw red diagonal line
cox->set_source_rgb(0.8, 0.0, 0.0);
cox->set_line_width (10.0);
cox->move_to(0, 0);
cox->line_to(extH, extV);
cox->stroke();
cox->restore();
// cause child widgets to be redrawn
bool event_is_handled = Gtk::Layout::on_draw(cox);
// any drawing which follows happens on top of child widgets...
cox->save();
cox->translate(-offH, -offV);
cox->set_source_rgb(0.2, 0.4, 0.9);
cox->set_line_width (2.0);
cox->rectangle(0,0, extH, extV);
cox->stroke();
cox->restore();
return event_is_handled;
}
else
return Gtk::Layout::on_draw(cox);
}
/* === Support for Investigation === */
namespace {
_Fmt childID("Chld-%02d");
int instanceCnt = 0;
}
uint ChildEx::childNo = 0;
ChildEx::ChildEx()
: Gtk::Button(string (childID % childNo++))
{
++instanceCnt;
}
void
ChildEx::on_clicked()
{
cout << "|=="<<get_label()<<endl;
}
ChildEx*
TimelinePanel::makeChld()
{
return Gtk::manage(new ChildEx);
}
////////////////////////////////////////////////////////////////////TICKET #1020 : verification code for instance management
ChildEx::~ChildEx()
{
--instanceCnt;
if (instanceCnt > 0)
cout << " ↯↯ still "<<instanceCnt<<" children to kill..."<<endl;
else
if (instanceCnt == 0)
cout << "+++ Success: all children are dead..."<<endl;
else
cout << "### ALARM ###"<<endl
<< "instanceCnt == "<<instanceCnt <<endl;
}
void
__verifyDeadChildren()
{
if (instanceCnt == 0)
cout << "+++ Success: all children are dead..."<<endl;
else
cout << "### ALARM ###"<<endl
<< "instanceCnt == "<<instanceCnt <<endl;
}
////////////////////////////////////////////////////////////////////TICKET #1020 : verification code for instance management
}} // namespace gui::panel

View file

@ -46,122 +46,44 @@
#include "gui/panel/panel.hpp"
#include "gui/timeline/timeline-widget.hpp"
#include <gtkmm/notebook.h>
#include <memory>
#include <vector>
namespace gui {
namespace model{
class Sequence;
}
namespace panel {
//using std::shared_ptr;
/**
* "experimental" child widget for investigation of Gtk::Layout
*/
class ChildEx
: public Gtk::Button
{
static uint childNo;
public:
ChildEx();
~ChildEx();
private:
void on_clicked() override;
};
void __verifyDeadChildren();
/**
* "experimental" custom canvas, based on Gtk::Layout.
* In addition this customised widget supports direct drawing
*/
class Canvas
: public Gtk::Layout
{
bool shallDraw_;
bool recalcExtension_ = false;
public:
void enableDraw (bool);
void adjustSize();
private:
virtual bool on_draw (Cairo::RefPtr<Cairo::Context> const&) override;
void determineExtension();
};
namespace panel{
/**
* Dockable panel to hold timeline widget(s).
* @todo build the actual implementation, after finishing the investigation
*
* ## Investigation of gtk::GtkLayout
* As of 10/2016, we start this task with an exploration of GTK behaviour
*
* \par Plan of investigation
* 1. place some simple widgets (Buttons)
* 2. learn how to draw
* 3. place a huge number of widgets, to scrutinise scrolling and performance
* 4. place widgets overlapping and irregularily, beyond the scrollable area
* 5. bind signals to those widgets, to verify event dispatching
* 6. bind some further signal(s) to the ~GtkLayout container
* 7. hide and re-show a partially and a totally overlapped widget
* 8. find a way to move a widget and delete arbitrary widgets
* 9. expand an existing widget (text change)
* 10. build a custom "clip" widget
* 11. retrofit all preceding tests to use this "clip" widget
* @todo WIP 10/2018 the actual re-implementation of Lumiera's Timeline display
*/
class TimelinePanel
: public Panel
{
using PageHandle = std::unique_ptr<timeline::TimelinePage>;
using Timelines = std::vector<PageHandle>;
Gtk::Notebook tabs_;
Timelines pages_;
public:
/**
* @param panel_manager The owner panel manager widget.
* @param dock_item The GdlDockItem that will host this panel.
*/
TimelinePanel(workspace::PanelManager&, Gdl::DockItem&);
TimelinePanel (workspace::PanelManager&, Gdl::DockItem&);
static const char* getTitle();
static const gchar* getStockID();
/////////////////////////////////////////////////////////////TODO WIP
void addTimeline (std::unique_ptr<timeline::TimelineWidget>&&);
void addTimeline (PageHandle &&);
private:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1016 : WIP kill everything below....
Gtk::Box twoParts_;
Gtk::ButtonBox buttons_;
Gtk::Button button_1_;
Gtk::Button button_2_;
Gtk::Button button_3_;
Gtk::Button button_4_;
Gtk::Button button_5_;
Gtk::CheckButton toggleDraw_;
Gtk::Frame frame_;
Gtk::ScrolledWindow scroller_;
Canvas canvas_;
ChildEx* makeChld();
using ChildV = std::vector<ChildEx*>;
ChildV childz_;
void experiment_1();
void experiment_2();
void experiment_3();
void experiment_4();
void experiment_5();
};

View file

@ -41,7 +41,7 @@
#include <utility>
using lib::diff::TreeMutator;
using std::make_unique;
using std::unique_ptr;
namespace gui {
@ -77,15 +77,24 @@ namespace timeline {
/**
* actually build a TimelineWidget to enact the role
* represented by this smart-handle
* actually build a TimelineWidget to enact the role represented by this smart-handle.
* @remark some implementation twist involved here, since TimelinePanel manages a collection
* of `unique_ptr<TimelinePage>`, in order to be able to hold an empty placeholder page.
* Since C++ unfortunately does not support _Covariance_ proper, we need to fabricate a
* unique_ptr<TimelinePage> right here, which can than be handed over to TimelinePanel,
* yet still we _do inherit_ from WLink<TimelineWidget>, i.e. expose the subclass.
* The `unque_ptr` owns and manages the TimelineWidget, which is itself non-copyable
* and stays at a fixed location in heap memory, as is required by gui::ctrl::Nexus
* to maintain a registration of the UI-Bus connection. WLink to the contrary
* just connects to the widget, and is automatically disconnected when it dies.
*/
std::unique_ptr<TimelineWidget>
unique_ptr<TimelinePage>
TimelineGui::buildTimelineWidget (BusTerm& nexus)
{
auto widget = std::make_unique<TimelineWidget> (timelineID_, rootTrackID_, nexus);
TimelineWidget* widget;
unique_ptr<TimelinePage> pageHandle {widget = new TimelineWidget{timelineID_, rootTrackID_, nexus}};
this->connect (*widget);
return widget;
return pageHandle;
}

View file

@ -63,6 +63,7 @@
namespace gui {
namespace timeline {
class TimelinePage;
class TimelineWidget;
using ctrl::BusTerm;
@ -93,7 +94,7 @@ namespace timeline {
/** actually build a TimelineWidget to enact the role
* represented by this smart-handle */
std::unique_ptr<TimelineWidget> buildTimelineWidget (BusTerm&);
std::unique_ptr<TimelinePage> buildTimelineWidget (BusTerm&);
/** @internal this method is invoked by the UI-Bus when dispatching a MutationMessage... */
void buildMutator (lib::diff::TreeMutator::Handle buffer) override;

View file

@ -38,6 +38,7 @@
#include "gui/gtk-base.hpp"
#include "gui/timeline/timeline-widget.hpp"
#include "lib/time/timevalue.hpp"
//#include <memory>
@ -56,6 +57,7 @@ namespace timeline {
* and a scrollable timeline body (right). The layout of both parts is aligned.
*/
class TimelineWidgetEmpty
: public TimelinePage
{
public:
/**

View file

@ -66,7 +66,7 @@ namespace timeline {
TimelineWidget::TimelineWidget (BusTerm::ID identity, BusTerm::ID trackID, BusTerm& nexus)
: Gtk::Paned{Gtk::ORIENTATION_VERTICAL}
: TimelinePage{}
, control_{new TimelineController{identity, trackID, nexus}}
, layout_{new LayoutManager}
{

View file

@ -78,6 +78,20 @@ namespace timeline {
using ctrl::BusTerm;
/**
* Interface: GUI page holding a timeline display
*/
class TimelinePage
: public Gtk::Paned
{
public:
virtual ~TimelinePage() { } ///< this is an interface
TimelinePage()
: Gtk::Paned{Gtk::ORIENTATION_VERTICAL}
{ }
};
/**
* Core timeline display (custom widget).
* Top level entry point to the timeline display component.
@ -86,7 +100,7 @@ namespace timeline {
* and a scrollable timeline body (right). The layout of both parts is aligned.
*/
class TimelineWidget
: public Gtk::Paned
: public TimelinePage
{
std::unique_ptr<TimelineController> control_;
std::unique_ptr<LayoutManager> layout_;

View file

@ -19053,11 +19053,17 @@
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1480694550601" ID="ID_712786561" MODIFIED="1538942194204" TEXT="Timeline-Panel">
<linktarget COLOR="#fec680" DESTINATION="ID_712786561" ENDARROW="Default" ENDINCLINATION="-1077;-98;" ID="Arrow_ID_433721169" SOURCE="ID_1231562526" STARTARROW="None" STARTINCLINATION="860;206;"/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1539394757429" ID="ID_376064530" MODIFIED="1539394763517" TEXT="wegr&#xe4;umen des Testcodes">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1539394757429" ID="ID_376064530" MODIFIED="1539459391069" TEXT="wegr&#xe4;umen des Testcodes">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1539394765628" ID="ID_308731527" MODIFIED="1539394778891" TEXT="Widget-Struktur anlegen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1539459412979" ID="ID_1273129379" MODIFIED="1539459441106" TEXT="leere Platzhalter-Timeline">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1539459428928" ID="ID_1156538064" MODIFIED="1539459436200" TEXT="best&#xfc;cken">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1539394779490" ID="ID_1710199928" MODIFIED="1539394784490" TEXT="Verwalten der Tabs">
<icon BUILTIN="flag-yellow"/>
@ -35312,6 +35318,46 @@
</node>
<node CREATED="1539134848420" ID="ID_4911843" MODIFIED="1539134870669" TEXT="ein selbstbez&#xfc;glicher substanzloser Strukturkern"/>
<node CREATED="1539134911707" ID="ID_847488848" MODIFIED="1539134930189" TEXT="DiffConstituent -&gt; emanation as diff"/>
<node CREATED="1539395579133" ID="ID_589161293" MODIFIED="1539395583088" TEXT="Anforderungen">
<node CREATED="1539395591547" ID="ID_1807468145" MODIFIED="1539395602517" TEXT="Population-Diff erzeugen"/>
<node CREATED="1539395603034" ID="ID_1342955865" MODIFIED="1539395608301" TEXT="Delta-Diff erzeugen">
<node CREATED="1539395609169" ID="ID_1189203662" MODIFIED="1539395616803" TEXT="mu&#xdf; exakt den vorherigen Stand kennen"/>
<node CREATED="1539395620120" ID="ID_1953087376" MODIFIED="1539395775000" TEXT="Stichwort: asynchrone Kommunkiation">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
GUI und Session schicken Nachrichten.
</p>
<p>
W&#228;hrend der Builder l&#228;uft, kann das GUI schon weitere Nachrichten geschickt haben,
</p>
<p>
die dann noch in der ProcDispatcher-Queue h&#228;ngen. Daher kann sich die Antwort
</p>
<p>
als Resultat auf einen Builder-Lauf noch auf einen vorherigen Zustand beziehen.
</p>
<p>
Es kann aber auch ein Builder-Lauf die kummulierten Ergebnisse von mehreren Commands behandeln.
</p>
<p>
In jedem Fall mu&#223; der DiffConstituent genau wissen, was der zuletzt geschickte Stand war,
</p>
<p>
damit er einen Diff erzeugt, der garantiert auf der anderen Seite anwendbar ist.
</p>
<p>
Denn letzteres ist bei uns eine Grundannahme. Es gibt keine ungef&#228;hren Diffs!
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
</node>
<node CREATED="1539270491013" ID="ID_1435731309" MODIFIED="1539388289807" TEXT="Problem der korrekten Diff-Struktur">
<arrowlink COLOR="#d47366" DESTINATION="ID_1555798999" ENDARROW="Default" ENDINCLINATION="590;-127;" ID="Arrow_ID_308349501" STARTARROW="None" STARTINCLINATION="915;0;"/>