various work on Pane, including cursors, more styling stuff, and making the forall_vfunc safe against gtk_container_remove

This commit is contained in:
Paul Davis 2016-05-27 12:58:55 -04:00
parent 95be25047c
commit 067616a84f
2 changed files with 91 additions and 32 deletions

View file

@ -25,6 +25,7 @@
#include <stdint.h> #include <stdint.h>
#include <gdkmm/cursor.h>
#include <gtkmm/container.h> #include <gtkmm/container.h>
#include <gtkmm/eventbox.h> #include <gtkmm/eventbox.h>
@ -47,8 +48,9 @@ class LIBGTKMM2EXT_API Pane : public Gtk::Container
float get_divider (std::vector<float>::size_type divider = 0); float get_divider (std::vector<float>::size_type divider = 0);
GType child_type_vfunc() const; GType child_type_vfunc() const;
void set_drag_cursor (Gdk::Cursor);
protected:
bool horizontal; bool horizontal;
void on_add (Gtk::Widget*); void on_add (Gtk::Widget*);
@ -60,10 +62,15 @@ class LIBGTKMM2EXT_API Pane : public Gtk::Container
bool handle_press_event (GdkEventButton*, Divider*); bool handle_press_event (GdkEventButton*, Divider*);
bool handle_release_event (GdkEventButton*, Divider*); bool handle_release_event (GdkEventButton*, Divider*);
bool handle_motion_event (GdkEventMotion*, 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); void forall_vfunc (gboolean include_internals, GtkCallback callback, gpointer callback_data);
private: private:
Gdk::Cursor drag_cursor;
bool did_move;
void reallocate (Gtk::Allocation const &); void reallocate (Gtk::Allocation const &);
typedef std::list<Gtk::Widget*> Children; typedef std::list<Gtk::Widget*> Children;
@ -76,11 +83,9 @@ class LIBGTKMM2EXT_API Pane : public Gtk::Container
bool dragging; bool dragging;
bool on_expose_event (GdkEventExpose* ev); bool on_expose_event (GdkEventExpose* ev);
bool on_enter_notify_event (GdkEventCrossing*);
bool on_leave_notify_event (GdkEventCrossing*);
}; };
typedef std::vector<Divider*> Dividers; typedef std::list<Divider*> Dividers;
Dividers dividers; Dividers dividers;
int divider_width; int divider_width;
void add_divider (); void add_divider ();

View file

@ -17,6 +17,7 @@
*/ */
#include <gdkmm/cursor.h>
#include "gtkmm2ext/pane.h" #include "gtkmm2ext/pane.h"
#include "i18n.h" #include "i18n.h"
@ -28,10 +29,25 @@ using namespace std;
Pane::Pane (bool h) Pane::Pane (bool h)
: horizontal (h) : horizontal (h)
, did_move (false)
, divider_width (5) , divider_width (5)
{ {
using namespace Gdk;
set_name ("Pane"); set_name ("Pane");
set_has_window (false); 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 void
@ -41,11 +57,11 @@ Pane::on_size_request (GtkRequisition* req)
/* iterate over all children, get their size requests */ /* iterate over all children, get their size requests */
/* horizontal pane is as high as its tallest child, but has no width /* horizontal pane is as high as its tallest child, including the dividers.
* requirement. * Its width is the sum of the children plus the dividers.
* *
* vertical pane is as wide as its widest child, but has no height * vertical pane is as wide as its widest child, including the dividers.
* requirement. * Its height is the sum of the children plus the dividers.
*/ */
if (horizontal) { if (horizontal) {
@ -76,6 +92,7 @@ Pane::on_size_request (GtkRequisition* req)
GType GType
Pane::child_type_vfunc() const Pane::child_type_vfunc() const
{ {
/* We accept any number of any types of widgets */
return Gtk::Widget::get_type(); 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_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_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_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->set_parent (*this);
d->show (); d->show ();
d->fract = 0.5; d->fract = 0.5;
@ -121,7 +140,6 @@ Pane::on_size_allocate (Gtk::Allocation& alloc)
void void
Pane::reallocate (Gtk::Allocation const & alloc) Pane::reallocate (Gtk::Allocation const & alloc)
{ {
Children::size_type n = 0;
int remaining; int remaining;
int xpos = alloc.get_x(); int xpos = alloc.get_x();
int ypos = alloc.get_y(); int ypos = alloc.get_y();
@ -132,6 +150,7 @@ Pane::reallocate (Gtk::Allocation const & alloc)
} }
if (children.size() == 1) { if (children.size() == 1) {
/* only child gets the full allocation */
children.front()->size_allocate (alloc); children.front()->size_allocate (alloc);
return; return;
} }
@ -146,7 +165,7 @@ Pane::reallocate (Gtk::Allocation const & alloc)
Children::iterator next; Children::iterator next;
Dividers::iterator div; 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; Gtk::Allocation child_alloc;
next = child; next = child;
@ -155,12 +174,12 @@ Pane::reallocate (Gtk::Allocation const & alloc)
child_alloc.set_x (xpos); child_alloc.set_x (xpos);
child_alloc.set_y (ypos); child_alloc.set_y (ypos);
if (n >= dividers.size()) { if (next == children.end()) {
/* the next child gets all the remaining space */ /* last child gets all the remaining space */
fract = 1.0; fract = 1.0;
} else { } else {
/* the next child gets the fraction of the remaining space given by the divider that follows it */ /* child gets the fraction of the remaining space given by the divider that follows it */
fract = dividers[n]->fract; fract = (*div)->fract;
} }
Gtk::Requisition cr; Gtk::Requisition cr;
@ -213,14 +232,15 @@ Pane::reallocate (Gtk::Allocation const & alloc)
bool bool
Pane::on_expose_event (GdkEventExpose* ev) 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); propagate_expose (**child, ev);
if (n < dividers.size()) { if (div != dividers.end()) {
propagate_expose (*dividers[n], ev); propagate_expose (**div, ev);
} }
} }
@ -240,7 +260,11 @@ bool
Pane::handle_release_event (GdkEventButton* ev, Divider* d) Pane::handle_release_event (GdkEventButton* ev, Divider* d)
{ {
d->dragging = false; d->dragging = false;
children.front()->queue_resize ();
if (did_move) {
children.front()->queue_resize ();
did_move = false;
}
return false; return false;
} }
@ -248,6 +272,8 @@ Pane::handle_release_event (GdkEventButton* ev, Divider* d)
bool bool
Pane::handle_motion_event (GdkEventMotion* ev, Divider* d) Pane::handle_motion_event (GdkEventMotion* ev, Divider* d)
{ {
did_move = true;
if (!d->dragging) { if (!d->dragging) {
return true; return true;
} }
@ -305,12 +331,20 @@ Pane::set_divider (Dividers::size_type div, float fract)
{ {
bool redraw = false; bool redraw = false;
while (dividers.size() <= div) { Dividers::iterator d = dividers.begin();
add_divider ();
while (div--) {
++d;
if (d == dividers.end()) {
/* caller is trying to set divider that does not exist
* yet.
*/
return;
}
} }
if (fract != dividers[div]->fract) { if (fract != (*d)->fract) {
dividers[div]->fract = fract; (*d)->fract = fract;
redraw = true; redraw = true;
} }
@ -324,23 +358,41 @@ Pane::set_divider (Dividers::size_type div, float fract)
float float
Pane::get_divider (Dividers::size_type div) Pane::get_divider (Dividers::size_type div)
{ {
if (div >= dividers.size()) { Dividers::iterator d = dividers.begin();
return -1;
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 void
Pane::forall_vfunc (gboolean include_internals, GtkCallback callback, gpointer callback_data) 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); callback ((*w)->gobj(), callback_data);
w = next;
} }
if (include_internals) { 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); callback (GTK_WIDGET((*d)->gobj()), callback_data);
d = next;
} }
} }
} }
@ -372,15 +424,17 @@ Pane::Divider::on_expose_event (GdkEventExpose* ev)
} }
bool 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; return true;
} }
bool 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; return true;
} }