From 067616a84f6ee6be2d2f77877fbf5f8bdc10775a Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 27 May 2016 12:58:55 -0400 Subject: various work on Pane, including cursors, more styling stuff, and making the forall_vfunc safe against gtk_container_remove --- libs/gtkmm2ext/gtkmm2ext/pane.h | 13 +++-- libs/gtkmm2ext/pane.cc | 110 ++++++++++++++++++++++++++++++---------- 2 files changed, 91 insertions(+), 32 deletions(-) (limited to 'libs/gtkmm2ext') diff --git a/libs/gtkmm2ext/gtkmm2ext/pane.h b/libs/gtkmm2ext/gtkmm2ext/pane.h index 452c9f9f0a..ecf466833a 100644 --- a/libs/gtkmm2ext/gtkmm2ext/pane.h +++ b/libs/gtkmm2ext/gtkmm2ext/pane.h @@ -25,6 +25,7 @@ #include +#include #include #include @@ -47,8 +48,9 @@ class LIBGTKMM2EXT_API Pane : public Gtk::Container float get_divider (std::vector::size_type divider = 0); GType child_type_vfunc() const; + void set_drag_cursor (Gdk::Cursor); - + protected: bool horizontal; void on_add (Gtk::Widget*); @@ -60,10 +62,15 @@ class LIBGTKMM2EXT_API Pane : public Gtk::Container bool handle_press_event (GdkEventButton*, Divider*); bool handle_release_event (GdkEventButton*, Divider*); bool handle_motion_event (GdkEventMotion*, Divider*); + bool handle_enter_event (GdkEventCrossing*, Divider*); + bool handle_leave_event (GdkEventCrossing*, Divider*); void forall_vfunc (gboolean include_internals, GtkCallback callback, gpointer callback_data); private: + Gdk::Cursor drag_cursor; + bool did_move; + void reallocate (Gtk::Allocation const &); typedef std::list Children; @@ -76,11 +83,9 @@ class LIBGTKMM2EXT_API Pane : public Gtk::Container bool dragging; bool on_expose_event (GdkEventExpose* ev); - bool on_enter_notify_event (GdkEventCrossing*); - bool on_leave_notify_event (GdkEventCrossing*); }; - typedef std::vector Dividers; + typedef std::list Dividers; Dividers dividers; int divider_width; void add_divider (); diff --git a/libs/gtkmm2ext/pane.cc b/libs/gtkmm2ext/pane.cc index 2472d0257d..5945ec40dc 100644 --- a/libs/gtkmm2ext/pane.cc +++ b/libs/gtkmm2ext/pane.cc @@ -17,6 +17,7 @@ */ +#include #include "gtkmm2ext/pane.h" #include "i18n.h" @@ -28,10 +29,25 @@ using namespace std; Pane::Pane (bool h) : horizontal (h) + , did_move (false) , divider_width (5) { + using namespace Gdk; + set_name ("Pane"); set_has_window (false); + + if (horizontal) { + drag_cursor = Cursor (SB_H_DOUBLE_ARROW); + } else { + drag_cursor = Cursor (SB_H_DOUBLE_ARROW); + } +} + +void +Pane::set_drag_cursor (Gdk::Cursor c) +{ + drag_cursor = c; } void @@ -41,11 +57,11 @@ Pane::on_size_request (GtkRequisition* req) /* iterate over all children, get their size requests */ - /* horizontal pane is as high as its tallest child, but has no width - * requirement. + /* horizontal pane is as high as its tallest child, including the dividers. + * Its width is the sum of the children plus the dividers. * - * vertical pane is as wide as its widest child, but has no height - * requirement. + * vertical pane is as wide as its widest child, including the dividers. + * Its height is the sum of the children plus the dividers. */ if (horizontal) { @@ -76,6 +92,7 @@ Pane::on_size_request (GtkRequisition* req) GType Pane::child_type_vfunc() const { + /* We accept any number of any types of widgets */ return Gtk::Widget::get_type(); } @@ -86,6 +103,8 @@ Pane::add_divider () d->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_press_event), d), false); d->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_release_event), d), false); d->signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_motion_event), d), false); + d->signal_enter_notify_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_enter_event), d), false); + d->signal_leave_notify_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_leave_event), d), false); d->set_parent (*this); d->show (); d->fract = 0.5; @@ -121,7 +140,6 @@ Pane::on_size_allocate (Gtk::Allocation& alloc) void Pane::reallocate (Gtk::Allocation const & alloc) { - Children::size_type n = 0; int remaining; int xpos = alloc.get_x(); int ypos = alloc.get_y(); @@ -132,6 +150,7 @@ Pane::reallocate (Gtk::Allocation const & alloc) } if (children.size() == 1) { + /* only child gets the full allocation */ children.front()->size_allocate (alloc); return; } @@ -146,7 +165,7 @@ Pane::reallocate (Gtk::Allocation const & alloc) Children::iterator next; Dividers::iterator div; - for (child = children.begin(), div = dividers.begin(); child != children.end(); ++n) { + for (child = children.begin(), div = dividers.begin(); child != children.end(); ) { Gtk::Allocation child_alloc; next = child; @@ -155,12 +174,12 @@ Pane::reallocate (Gtk::Allocation const & alloc) child_alloc.set_x (xpos); child_alloc.set_y (ypos); - if (n >= dividers.size()) { - /* the next child gets all the remaining space */ + if (next == children.end()) { + /* last child gets all the remaining space */ fract = 1.0; } else { - /* the next child gets the fraction of the remaining space given by the divider that follows it */ - fract = dividers[n]->fract; + /* child gets the fraction of the remaining space given by the divider that follows it */ + fract = (*div)->fract; } Gtk::Requisition cr; @@ -213,14 +232,15 @@ Pane::reallocate (Gtk::Allocation const & alloc) bool Pane::on_expose_event (GdkEventExpose* ev) { - Children::size_type n = 0; + Children::iterator child; + Dividers::iterator div; - for (Children::iterator child = children.begin(); child != children.end(); ++child, ++n) { + for (child = children.begin(), div = dividers.begin(); child != children.end(); ++child, ++div) { propagate_expose (**child, ev); - if (n < dividers.size()) { - propagate_expose (*dividers[n], ev); + if (div != dividers.end()) { + propagate_expose (**div, ev); } } @@ -240,7 +260,11 @@ bool Pane::handle_release_event (GdkEventButton* ev, Divider* d) { d->dragging = false; - children.front()->queue_resize (); + + if (did_move) { + children.front()->queue_resize (); + did_move = false; + } return false; } @@ -248,6 +272,8 @@ Pane::handle_release_event (GdkEventButton* ev, Divider* d) bool Pane::handle_motion_event (GdkEventMotion* ev, Divider* d) { + did_move = true; + if (!d->dragging) { return true; } @@ -305,12 +331,20 @@ Pane::set_divider (Dividers::size_type div, float fract) { bool redraw = false; - while (dividers.size() <= div) { - add_divider (); + Dividers::iterator d = dividers.begin(); + + while (div--) { + ++d; + if (d == dividers.end()) { + /* caller is trying to set divider that does not exist + * yet. + */ + return; + } } - if (fract != dividers[div]->fract) { - dividers[div]->fract = fract; + if (fract != (*d)->fract) { + (*d)->fract = fract; redraw = true; } @@ -324,23 +358,41 @@ Pane::set_divider (Dividers::size_type div, float fract) float Pane::get_divider (Dividers::size_type div) { - if (div >= dividers.size()) { - return -1; + Dividers::iterator d = dividers.begin(); + + while (div--) { + ++d; + if (d == dividers.end()) { + /* caller is trying to set divider that does not exist + * yet. + */ + return -1.0f; + } } - return dividers[div]->fract; + return (*d)->fract; } void Pane::forall_vfunc (gboolean include_internals, GtkCallback callback, gpointer callback_data) { - for (Children::iterator w = children.begin(); w != children.end(); ++w) { + /* since the callback could modify the child list(s), make sure we keep + * the iterators safe; + */ + + for (Children::iterator w = children.begin(); w != children.end(); ) { + Children::iterator next = w; + ++next; callback ((*w)->gobj(), callback_data); + w = next; } if (include_internals) { - for (Dividers::iterator d = dividers.begin(); d != dividers.end(); ++d) { + for (Dividers::iterator d = dividers.begin(); d != dividers.end(); ) { + Dividers::iterator next = d; + ++next; callback (GTK_WIDGET((*d)->gobj()), callback_data); + d = next; } } } @@ -372,15 +424,17 @@ Pane::Divider::on_expose_event (GdkEventExpose* ev) } bool -Pane::Divider::on_enter_notify_event (GdkEventCrossing*) +Pane::handle_enter_event (GdkEventCrossing*, Divider* d) { - set_state (Gtk::STATE_SELECTED); + d->get_window()->set_cursor (drag_cursor); + d->set_state (Gtk::STATE_SELECTED); return true; } bool -Pane::Divider::on_leave_notify_event (GdkEventCrossing*) +Pane::handle_leave_event (GdkEventCrossing*, Divider* d) { - set_state (Gtk::STATE_NORMAL); + d->get_window()->set_cursor (); + d->set_state (Gtk::STATE_NORMAL); return true; } -- cgit v1.2.3