WIP: Applying new layout system

This commit is contained in:
Joel Holdsworth 2009-01-01 18:43:46 +00:00
parent ac28251915
commit afb5b7298f
9 changed files with 190 additions and 234 deletions

View file

@ -32,6 +32,7 @@
#include <nobug.h>
#include <vector>
#include <boost/utility.hpp>
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include "lib/util.hpp"

View file

@ -50,12 +50,12 @@ TimelineWidget::TimelineWidget(
playbackPeriodStart(0),
playbackPeriodEnd(0),
playbackPoint(GAVL_TIME_UNDEFINED),
totalHeight(0),
horizontalAdjustment(0, 0, 0),
verticalAdjustment(0, 0, 0),
horizontalScroll(horizontalAdjustment),
verticalScroll(verticalAdjustment)
{
g_message("TimelineWidget::TimelineWidget");
REQUIRE(sequence);
body = new TimelineBody(this);
@ -64,6 +64,8 @@ TimelineWidget::TimelineWidget(
ENSURE(headerContainer != NULL);
ruler = new TimelineRuler(this);
ENSURE(ruler != NULL);
g_message("Widgets Constructed");
horizontalAdjustment.signal_value_changed().connect( sigc::mem_fun(
this, &TimelineWidget::on_scroll) );
@ -77,6 +79,7 @@ TimelineWidget::TimelineWidget(
viewWindow.set_time_scale(GAVL_TIME_SCALE / 200);
set_selection(2000000, 4000000);
g_message("update_tracks");
update_tracks();
attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND);
@ -90,6 +93,7 @@ TimelineWidget::TimelineWidget(
// Receive notifications of changes to the tracks
sequence->get_child_track_list().signal_changed().connect(
sigc::mem_fun( this, &TimelineWidget::on_track_list_changed ) );
g_message("Constructor }");
}
TimelineWidget::~TimelineWidget()
@ -296,6 +300,10 @@ TimelineWidget::update_tracks()
// Create timeline tracks from all the model tracks
create_timeline_tracks();
// Update the layout helper
layoutHelper.clone_tree_from_sequence();
layoutHelper.update_layout();
// Update the header container
REQUIRE(headerContainer != NULL);
headerContainer->show_all_children();
@ -303,15 +311,6 @@ TimelineWidget::update_tracks()
// Update the body
body->queue_draw();
// Recalculate the total height of the timeline scrolled area
totalHeight = 0;
BOOST_FOREACH(shared_ptr<model::Track> track,
sequence->get_child_tracks())
{
REQUIRE(track);
totalHeight += measure_branch_height(track);
}
}
void
@ -339,9 +338,6 @@ TimelineWidget::create_timeline_tracks_from_branch(
// We will need to create one
trackMap[model_track] =
create_timeline_track_from_model_track(model_track);
// Hook up
}
// Recurse to child tracks
@ -419,6 +415,9 @@ TimelineWidget::lookup_timeline_track(
shared_ptr<model::Track> model_track) const
{
REQUIRE(sequence);
REQUIRE(model_track);
REQUIRE(model_track != sequence); // The sequence isn't really a track
std::map<shared_ptr<model::Track>, shared_ptr<timeline::Track> >::
const_iterator iterator = trackMap.find(model_track);
if(iterator == trackMap.end())
@ -434,31 +433,6 @@ TimelineWidget::lookup_timeline_track(
return iterator->second;
}
boost::shared_ptr<model::Track>
TimelineWidget::lookup_model_track(
const timeline::Track *timeline_track) const
{
REQUIRE(sequence);
std::pair<shared_ptr<model::Track>, shared_ptr<timeline::Track> >
pair;
BOOST_FOREACH( pair, trackMap )
{
if(pair.second.get() == timeline_track)
{
ENSURE(pair.first);
return pair.first;
}
}
// The track is not present in the map
// We are in an error condition if the timeline track is not found
// - the timeline tracks must always be synchronous with the model
// tracks.
ENSURE(0);
return shared_ptr<model::Track>();
}
void
TimelineWidget::update_scroll()
{
@ -479,7 +453,8 @@ TimelineWidget::update_scroll()
// Calculate the vertical length that can be scrolled:
// the total height of all the tracks minus one screenful
int y_scroll_length = totalHeight - body_allocation.get_height();
int y_scroll_length = layoutHelper.get_total_height() -
body_allocation.get_height();
if(y_scroll_length < 0) y_scroll_length = 0;
// If by resizing we're now over-scrolled, scroll back to
@ -501,29 +476,6 @@ TimelineWidget::update_scroll()
}
int
TimelineWidget::measure_branch_height(
shared_ptr<model::Track> model_track)
{
REQUIRE(model_track);
const shared_ptr<timeline::Track> timeline_track =
lookup_timeline_track(model_track);
ENSURE(timeline_track);
int height = timeline_track->get_height() + TrackPadding;
// Recurse through all the children
BOOST_FOREACH( shared_ptr<model::Track> child,
model_track->get_child_tracks() )
{
REQUIRE(child);
height += measure_branch_height(child);
}
return height;
}
int
TimelineWidget::get_y_scroll_offset() const
{

View file

@ -217,25 +217,11 @@ private:
**/
boost::shared_ptr<timeline::Track> lookup_timeline_track(
boost::shared_ptr<model::Track> model_track) const;
/**
* Looks up a model track in trackMap that corresponds to a
* given timeline track.
* @param timeline_track The timeline UI track to look up.
* @returns The model track found, or an empty shared_ptr if
* timeline_track has no corresponding timeline UI track (this is an
* error condition).
**/
boost::shared_ptr<model::Track> lookup_model_track(
const timeline::Track *timeline_track) const;
// ----- Layout Functions ----- //
void update_scroll();
int measure_branch_height(
boost::shared_ptr<model::Track> model_track);
int get_y_scroll_offset() const;
// ----- Event Handlers -----//
@ -293,8 +279,6 @@ protected:
boost::shared_ptr<timeline::Track> hoveringTrack;
int totalHeight;
// Child Widgets
timeline::TimelineHeaderContainer *headerContainer;
timeline::TimelineBody *body;

View file

@ -135,6 +135,7 @@ TimelineHeaderContainer::on_unrealize()
bool TimelineHeaderContainer::on_button_press_event (
GdkEventButton* event)
{
REQUIRE(timelineWidget != NULL);
REQUIRE(event != NULL);
switch(event->button)
@ -152,8 +153,9 @@ bool TimelineHeaderContainer::on_button_press_event (
case 3: // Right Click
{
// Popup the context menu
shared_ptr<Track> header = header_from_point(
Gdk::Point(event->x, event->y));
shared_ptr<Track> header(
timelineWidget->layoutHelper.header_from_point(
Gdk::Point(event->x, event->y)));
// Are we hovering on a header?
if(header)
@ -183,6 +185,8 @@ bool TimelineHeaderContainer::on_button_release_event (
// Yes? The toggle the expanding
clickedExpander->set_expanded(!clickedExpander->get_expanded());
clickedExpander.reset();
timelineWidget->layoutHelper.update_layout();
layout_headers();
}
@ -304,88 +308,58 @@ TimelineHeaderContainer::on_hovering_track_changed(
void
TimelineHeaderContainer::layout_headers()
{
{
REQUIRE(timelineWidget != NULL);
// We can't layout before the widget has been set up
if(!gdkWindow)
return;
// Make sure the style are loaded
read_styles();
// Clear previously cached layout
headerBoxes.clear();
// Start at minus-the-scroll offset
int offset = -timelineWidget->get_y_scroll_offset();
const Allocation container_allocation = get_allocation();
const int header_width = container_allocation.get_width();
BOOST_FOREACH( shared_ptr<model::Track> model_track, get_tracks() )
layout_headers_recursive(
model_track, offset, header_width, 0, true);
TimelineLayoutHelper &layoutHelper =
timelineWidget->layoutHelper;
const TimelineLayoutHelper::TrackTree &layoutTree =
layoutHelper.get_layout_tree();
TimelineLayoutHelper::TrackTree::pre_order_iterator iterator;
for(iterator = ++layoutTree.begin(); // ++ so that we skip the sequence root
iterator != layoutTree.end();
iterator++)
{
const shared_ptr<timeline::Track> timeline_track =
lookup_timeline_track(*iterator);
Widget &widget = timeline_track->get_header_widget();
optional<Gdk::Rectangle> header_rect =
layoutHelper.get_track_header_rect(timeline_track);
if(header_rect)
{
REQUIRE(header_rect->get_width() >= 0);
REQUIRE(header_rect->get_height() >= 0);
// Calculate the allocation of the header widget
Allocation header_allocation(
header_rect->get_x() + margin + expand_button_size, // x
header_rect->get_y() + margin, // y
max( header_rect->get_width() - expand_button_size -
margin * 2, 0 ), // width
header_rect->get_height() - margin * 2); // height
// Apply the allocation to the header
widget.size_allocate (header_allocation);
if(!widget.is_visible())
widget.show();
}
else // No header rect, so the track must be hidden
if(widget.is_visible())
widget.hide();
}
// Repaint the background of our parenting
queue_draw ();
}
void
TimelineHeaderContainer::layout_headers_recursive(
shared_ptr<model::Track> model_track, int &offset,
const int header_width, const int depth, bool parent_expanded)
{
REQUIRE(depth >= 0);
REQUIRE(model_track != NULL);
shared_ptr<timeline::Track> timeline_track =
lookup_timeline_track(model_track);
const int indent = depth * 10;
Widget &widget = timeline_track->get_header_widget();
if(parent_expanded)
{
const int track_height = timeline_track->get_height();
// Calculate the box of the header
Gdk::Rectangle header_box(
indent, // x
offset, // y
max( header_width - indent, 0 ), // width
track_height); // height
REQUIRE(header_box.get_height() >= 0);
// Cache the bounding box
headerBoxes[timeline_track] = header_box;
// Calculate the allocation of the header widget
Allocation header_allocation(
header_box.get_x() + margin + expand_button_size, // x
header_box.get_y() + margin, // y
max( header_box.get_width() - expand_button_size -
margin * 2, 0 ), // width
header_box.get_height() - margin * 2); // height
// Apply the allocation to the header
widget.size_allocate (header_allocation);
if(!widget.is_visible())
widget.show();
// Offset for the next header
offset += track_height + TimelineWidget::TrackPadding;
}
else
if(widget.is_visible())
widget.hide();
// Recurse through all the children
BOOST_FOREACH( boost::shared_ptr<model::Track> child,
model_track->get_child_tracks() )
layout_headers_recursive(
child, offset, header_width, depth + 1,
timeline_track->get_expanded() && parent_expanded);
}
void
TimelineHeaderContainer::set_parent_recursive(
boost::shared_ptr<model::Track> model_track)
@ -442,6 +416,7 @@ TimelineHeaderContainer::draw_header_decoration(
shared_ptr<model::Track> model_track,
const Gdk::Rectangle &clip_rect)
{
REQUIRE(timelineWidget != NULL);
REQUIRE(model_track != NULL);
REQUIRE(clip_rect.get_width() > 0);
REQUIRE(clip_rect.get_height() > 0);
@ -452,10 +427,11 @@ TimelineHeaderContainer::draw_header_decoration(
shared_ptr<timeline::Track> timeline_track =
lookup_timeline_track(model_track);
// Get the cached header box
weak_ptr<timeline::Track> ptr(timeline_track);
REQUIRE(contains(headerBoxes, ptr));
const Gdk::Rectangle &box = headerBoxes[timeline_track];
// Get the header box
const optional<Gdk::Rectangle> &optional_box =
timelineWidget->layoutHelper.get_track_header_rect(timeline_track);
REQUIRE(optional_box);
const Gdk::Rectangle box = *optional_box;
// Paint the box, if it will be visible
if(box.get_x() < clip_rect.get_width() &&
@ -498,58 +474,55 @@ TimelineHeaderContainer::draw_header_decoration(
}
}
boost::shared_ptr<timeline::Track>
TimelineHeaderContainer::header_from_point(const Gdk::Point &point)
{
std::pair<shared_ptr<timeline::Track>, Gdk::Rectangle> pair;
BOOST_FOREACH( pair, headerBoxes )
{
// Hit test the rectangle
const Gdk::Rectangle &rect = pair.second;
if(point.get_x() >= rect.get_x() &&
point.get_x() < rect.get_x() + rect.get_width() &&
point.get_y() >= rect.get_y() &&
point.get_y() < rect.get_y() + rect.get_height())
return pair.first;
}
return shared_ptr<timeline::Track>();
}
shared_ptr<timeline::Track>
TimelineHeaderContainer::expander_button_from_point(
const Gdk::Point &point)
{
std::pair<shared_ptr<timeline::Track>, Gdk::Rectangle> pair;
BOOST_FOREACH( pair, headerBoxes )
{
const TimelineLayoutHelper::TrackTree &layoutTree =
timelineWidget->layoutHelper.get_layout_tree();
TimelineLayoutHelper::TrackTree::pre_order_iterator iterator;
for(iterator = ++layoutTree.begin(); // ++ so we skip the sequence root
iterator != layoutTree.end();
iterator++)
{
// Hit test the rectangle
const Gdk::Rectangle rect =
get_expander_button_rectangle(pair.first);
const shared_ptr<timeline::Track> timeline_track =
lookup_timeline_track(*iterator);
if(point.get_x() >= rect.get_x() &&
point.get_x() < rect.get_x() + rect.get_width() &&
point.get_y() >= rect.get_y() &&
point.get_y() < rect.get_y() + rect.get_height())
return pair.first;
// Hit test the rectangle
const optional<Gdk::Rectangle> rect =
get_expander_button_rectangle(timeline_track);
if(rect)
{
if(point.get_x() >= rect->get_x() &&
point.get_x() < rect->get_x() + rect->get_width() &&
point.get_y() >= rect->get_y() &&
point.get_y() < rect->get_y() + rect->get_height())
return timeline_track;
}
}
return shared_ptr<timeline::Track>();
}
const Gdk::Rectangle
const optional<Gdk::Rectangle>
TimelineHeaderContainer::get_expander_button_rectangle(
shared_ptr<Track> track)
{
REQUIRE(timelineWidget != NULL);
REQUIRE(track != NULL);
weak_ptr<timeline::Track> ptr(track);
REQUIRE(contains(headerBoxes, ptr));
const Gdk::Rectangle &box = headerBoxes[track];
return Gdk::Rectangle(
margin + box.get_x(), margin + box.get_y(),
expand_button_size, box.get_height() - margin * 2);
optional<Gdk::Rectangle> box =
timelineWidget->layoutHelper.get_track_header_rect(track);
if(box)
{
return optional<Gdk::Rectangle>(Gdk::Rectangle(
margin + box->get_x(), margin + box->get_y(),
expand_button_size, box->get_height() - margin * 2));
}
return optional<Gdk::Rectangle>();
}
shared_ptr<timeline::Track>

View file

@ -149,19 +149,7 @@ private:
* stacking etc.
*/
void layout_headers();
/**
* Recursively lays out all the controls in the header widget.
* @param track The parent track object which will be recursed into.
* @param offset A shared value used to accumulate the y-offset of
* header widgets.
* @param header_width The width of this widget in pixels.
* @param depth The depth within the tree of track.
**/
void layout_headers_recursive(boost::shared_ptr<model::Track> track,
int &offset, const int header_width, const int depth,
bool parent_expanded);
/**
* Recursively sets all the track header widgets to be child widgets
* of this widget.
@ -199,9 +187,6 @@ private:
void draw_header_decoration(
boost::shared_ptr<model::Track> model_track,
const Gdk::Rectangle &clip_rect);
boost::shared_ptr<timeline::Track> header_from_point(
const Gdk::Point &point);
/**
* Given a point, expander_button_from_point finds the track of the
@ -219,7 +204,7 @@ private:
* @param track The track to get the expander button rectangle of.
* @return Returns the rectangle of the expander button of track.
**/
const Gdk::Rectangle get_expander_button_rectangle(
const boost::optional<Gdk::Rectangle> get_expander_button_rectangle(
boost::shared_ptr<timeline::Track> track);
/**
@ -278,16 +263,7 @@ private:
* click is not processed by track headers.
**/
Gtk::Menu contextMenu;
/**
* A map of tracks to the rectangles of their headers.
* @remarks This map is used as a cache, so that the rectangles don't
* need to be perpetually recalculated. This cache is regenerated by
* the layout_headers method.
**/
std::map<boost::weak_ptr<timeline::Track>, Gdk::Rectangle>
headerBoxes;
//----- User Interaction State -----//
boost::shared_ptr<timeline::Track> hoveringExpander;

View file

@ -31,13 +31,15 @@ using namespace Gtk;
using namespace std;
using namespace boost;
using namespace lumiera;
using namespace util;
namespace gui {
namespace widgets {
namespace timeline {
TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner)
: timelineWidget(owner)
TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner) :
timelineWidget(owner),
totalHeight(0)
{
}
@ -48,12 +50,16 @@ TimelineLayoutHelper::clone_tree_from_sequence()
REQUIRE(sequence);
layoutTree.clear();
TrackTree::iterator_base iterator =
layoutTree.set_head(sequence);
TrackTree::iterator_base iterator = layoutTree.set_head(sequence);
add_branch(iterator, sequence);
}
TimelineLayoutHelper::TrackTree&
TimelineLayoutHelper::get_layout_tree()
{
return layoutTree;
}
void
TimelineLayoutHelper::add_branch(
TrackTree::iterator_base parent_iterator,
@ -68,6 +74,45 @@ TimelineLayoutHelper::add_branch(
}
}
optional<Gdk::Rectangle>
TimelineLayoutHelper::get_track_header_rect(
boost::weak_ptr<timeline::Track> track)
{
if(contains(headerBoxes, track))
{
Gdk::Rectangle rect(headerBoxes[track]);
rect.set_y(rect.get_y() - timelineWidget.get_y_scroll_offset());
return optional<Gdk::Rectangle>(rect);
}
return optional<Gdk::Rectangle>();
}
weak_ptr<timeline::Track>
TimelineLayoutHelper::header_from_point(const Gdk::Point &point)
{
std::pair<weak_ptr<timeline::Track>, Gdk::Rectangle> pair;
BOOST_FOREACH( pair, headerBoxes )
{
// Hit test the rectangle
const Gdk::Rectangle &rect = pair.second;
if(point.get_x() >= rect.get_x() &&
point.get_x() < rect.get_x() + rect.get_width() &&
point.get_y() >= rect.get_y() &&
point.get_y() < rect.get_y() + rect.get_height())
return pair.first;
}
return shared_ptr<timeline::Track>();
}
int
TimelineLayoutHelper::get_total_height() const
{
ENSURE(totalHeight >= 0);
return totalHeight;
}
void
TimelineLayoutHelper::update_layout()
{
@ -81,11 +126,13 @@ TimelineLayoutHelper::update_layout()
int offset = 0;//-timelineWidget->get_y_scroll_offset();
//const Allocation container_allocation = get_allocation();
const int header_width = 100;//container_allocation.get_width();
const int header_width = 150;//container_allocation.get_width();
const int indent_width = 10;
layout_headers_recursive(layoutTree.begin(),
offset, header_width, indent_width, 0, true);
totalHeight = offset;
}
void

View file

@ -56,17 +56,30 @@ public:
public:
TimelineLayoutHelper(TimelineWidget &owner);
protected:
void clone_tree_from_sequence();
TrackTree& get_layout_tree();
/**
* Recalculates the track layout from layoutTree.
**/
void update_layout();
boost::optional<Gdk::Rectangle> get_track_header_rect(
boost::weak_ptr<timeline::Track> track);
boost::weak_ptr<timeline::Track> header_from_point(
const Gdk::Point &point);
int get_total_height() const;
protected:
void add_branch(TrackTree::iterator_base parent_iterator,
boost::shared_ptr<model::Track> parent);
/**
* Recaculates the track layout from layoutTree.
**/
void update_layout();
/**
* Recursively lays out all the controls in the header widget.
@ -113,6 +126,8 @@ protected:
**/
std::map<boost::weak_ptr<timeline::Track>, Gdk::Rectangle>
headerBoxes;
int totalHeight;
};
} // namespace timeline

View file

@ -90,6 +90,12 @@ Track::get_header_widget()
return headerWidget;
}
shared_ptr<model::Track>
Track::get_model_track() const
{
return model_track;
}
int
Track::get_height() const
{

View file

@ -45,6 +45,8 @@ public:
Gtk::Widget& get_header_widget();
boost::shared_ptr<model::Track> get_model_track() const;
int get_height() const;
bool get_expanded() const;