Browse Source

super early draft of Panda2d

Cary Sandvig 25 years ago
parent
commit
0c3195ffb8

+ 31 - 0
panda/src/gui/Sources.pp

@@ -0,0 +1,31 @@
+#define OTHER_LIBS dtool
+
+#begin lib_target
+  #define TARGET gui
+
+  #define LOCAL_LIBS \
+    putil display tform device pandabase dgraph sgattrib light gobj text
+
+  #define SOURCES \
+    config_gui.h config_gui.cxx \
+    guiManager.h guiManager.I guiManager.cxx \
+    guiRegion.h guiRegion.I guiRegion.cxx \
+    guiLabel.h guiLabel.I guiLabel.cxx \
+    guiRollover.h guiRollover.I guiRollover.cxx \
+    guiButton.h guiButton.I guiButton.cxx
+
+  #define INSTALL_HEADERS \
+    guiManager.h guiManager.I \
+    guiRegion.h guiRegion.I \
+    guiLabel.h guiLabel.I \
+    guiRollover.h guiRollover.I \
+    guiButton.h guiButton.I
+
+  #define IGATESCAN \
+    guiManager.h guiManager.I \
+    guiRegion.h guiRegion.I \
+    guiLabel.h guiLabel.I \
+    guiRollover.h guiRollover.I \
+    guiButton.h guiButton.I
+
+#end lib_target

+ 14 - 0
panda/src/gui/config_gui.cxx

@@ -0,0 +1,14 @@
+// Filename: config_gui.cxx
+// Created by:  cary (26Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "config_gui.h"
+
+#include <dconfig.h>
+
+Configure(config_gui);
+NotifyCategoryDef(gui, "");
+
+ConfigureFn(config_gui) {
+}

+ 10 - 0
panda/src/gui/config_gui.h

@@ -2,3 +2,13 @@
 // Created by:  cary (26Oct00)
 // 
 ////////////////////////////////////////////////////////////////////
+
+#ifndef __CONFIG_GUI_H__
+#define __CONFIG_GUI_H__
+
+#include <pandabase.h>
+#include <notifyCategoryProxy.h>
+
+NotifyCategoryDecl(gui, EXPCL_PANDA, EXPTP_PANDA);
+
+#endif /* __CONFIG_GUI_H__ */

+ 155 - 0
panda/src/gui/guiButton.I

@@ -2,3 +2,158 @@
 // Created by:  cary (30Oct00)
 // 
 ////////////////////////////////////////////////////////////////////
+
+INLINE GuiButton::GuiButton(void) {
+}
+
+INLINE void GuiButton::enter(void) {
+  switch (_state) {
+  case UP:
+    switch_state(UP_ROLLOVER);
+    break;
+  case DOWN:
+    switch_state(DOWN_ROLLOVER);
+    break;
+  case INACTIVE:
+    switch_state(INACTIVE_ROLLOVER);
+    break;
+  default:
+    break;
+  }
+}
+
+INLINE void GuiButton::exit(void) {
+  switch (_state) {
+  case UP_ROLLOVER:
+    switch_state(UP);
+    break;
+  case DOWN_ROLLOVER:
+    switch_state(DOWN);
+    break;
+  case INACTIVE_ROLLOVER:
+    switch_state(INACTIVE);
+    break;
+  default:
+    break;
+  }
+}
+
+INLINE void GuiButton::up(void) {
+  switch (_state) {
+  case DOWN:
+    switch_state(UP);
+    break;
+  case DOWN_ROLLOVER:
+    switch_state(UP_ROLLOVER);
+    break;
+  default:
+    gui_cat->warning() << "got up from invalid state (" << _state << ")"
+		       << endl;
+  }
+}
+
+INLINE void GuiButton::down(void) {
+  switch (_state) {
+  case UP:
+    switch_state(DOWN);
+    break;
+  case UP_ROLLOVER:
+    switch_state(DOWN_ROLLOVER);
+    break;
+  default:
+    gui_cat->warning() << "got down from invalid state (" << _state << ")"
+		       << endl;
+  }
+}
+
+INLINE void GuiButton::inactive(void) {
+  switch (_state) {
+  case UP:
+  case DOWN:
+    switch_state(INACTIVE);
+    break;
+  case UP_ROLLOVER:
+  case DOWN_ROLLOVER:
+    switch_state(INACTIVE_ROLLOVER);
+    break;
+  default:
+    gui_cat->warning() << "got inactive from invalid state (" << _state << ")"
+		       << endl;
+  }
+}
+
+INLINE void GuiButton::click(void) {
+  switch (_state) {
+  case UP:
+  case UP_ROLLOVER:
+    down();
+    break;
+  case DOWN:
+  case DOWN_ROLLOVER:
+    up();
+    break;
+  case INACTIVE:
+    break;
+  default:
+    gui_cat->warning() << "got click from invalid state (" << _state << ")"
+		       << endl;
+  }
+}
+
+INLINE bool GuiButton::is_up(void) const {
+  if ((_state == UP) || (_state == UP_ROLLOVER))
+    return true;
+  return false;
+}
+
+INLINE bool GuiButton::is_over(void) const {
+  if ((_state == UP_ROLLOVER) || (_state == DOWN_ROLLOVER))
+    return true;
+  return false;
+}
+
+INLINE bool GuiButton::is_active(void) const {
+  if ((_state == INACTIVE) || (_state == NONE))
+    return false;
+  return true;
+}
+
+INLINE void GuiButton::set_up_event(const string& s) {
+  _up_event = s;
+}
+
+INLINE void GuiButton::set_up_rollover_event(const string& s) {
+  _up_rollover_event = s;
+}
+
+INLINE void GuiButton::set_down_event(const string& s) {
+  _down_event = s;
+}
+
+INLINE void GuiButton::set_down_rollover_event(const string& s) {
+  _down_rollover_event = s;
+}
+
+INLINE void GuiButton::set_inactive_event(const string& s) {
+  _inactive_event = s;
+}
+
+INLINE const string& GuiButton::get_up_event(void) const {
+  return _up_event;
+}
+
+INLINE const string& GuiButton::get_up_rollover_event(void) const {
+  return _up_rollover_event;
+}
+
+INLINE const string& GuiButton::get_down_event(void) const {
+  return _down_event;
+}
+
+INLINE const string& GuiButton::get_down_rollover_event(void) const {
+  return _down_rollover_event;
+}
+
+INLINE const string& GuiButton::get_inactive_event(void) const {
+  return _inactive_event;
+}

+ 198 - 0
panda/src/gui/guiButton.cxx

@@ -2,3 +2,201 @@
 // Created by:  cary (30Oct00)
 // 
 ////////////////////////////////////////////////////////////////////
+
+#include "guiButton.h"
+#include "config_gui.h"
+
+#include <throw_event.h>
+
+#include <map>
+
+typedef map<string, GuiButton*> ButtonMap;
+static ButtonMap buttons;
+
+inline void GetExtents(GuiLabel* v, GuiLabel* w, GuiLabel* x, GuiLabel* y,
+		       GuiLabel* z, float& l, float& r, float& b, float& t) {
+  float l1, l2, r1, r2, b1, b2, t1, t2;
+  v->get_extents(l1, r1, b1, t1);
+  w->get_extents(l2, r2, b2, t2);
+  l1 = (l1<l2)?l1:l2;
+  r1 = (r1<r2)?r2:r1;
+  b1 = (b1<b2)?b1:b2;
+  t1 = (t1<t2)?t2:t1;
+  if (x != (GuiLabel*)0L) {
+    x->get_extents(l2, r2, b2, t2);
+    l1 = (l1<l2)?l1:l2;
+    r1 = (r1<r2)?r2:r1;
+    b1 = (b1<b2)?b1:b2;
+    t1 = (t1<t2)?t2:t1;
+  }
+  if (y != (GuiLabel*)0L) {
+    y->get_extents(l2, r2, b2, t2);
+    l1 = (l1<l2)?l1:l2;
+    r1 = (r1<r2)?r2:r1;
+    b1 = (b1<b2)?b1:b2;
+    t1 = (t1<t2)?t2:t1;
+  }
+  if (z != (GuiLabel*)0L) {
+    z->get_extents(l2, r2, b2, t2);
+    l = (l1<l2)?l1:l2;
+    r = (r1<r2)?r2:r1;
+    b = (b1<b2)?b1:b2;
+    t = (t1<t2)?t2:t1;
+  }
+}
+
+static void enter_button(CPT_Event e) {
+  GuiButton* val = buttons[e->get_name()];
+  val->enter();
+}
+
+static void exit_button(CPT_Event e) {
+  GuiButton* val = buttons[e->get_name()];
+  val->exit();
+}
+
+static void click_button(CPT_Event e) {
+  GuiButton* val = buttons[e->get_name()];
+  val->click();
+}
+
+void GuiButton::switch_state(GuiButton::States nstate) {
+  // cleanup old state
+  switch (_state) {
+  case NONE:
+    break;
+  case UP:
+    _mgr->remove_label(_up);
+    break;
+  case UP_ROLLOVER:
+    _mgr->remove_label(_up_rollover);
+    break;
+  case DOWN:
+    _mgr->remove_label(_down);
+    break;
+  case DOWN_ROLLOVER:
+    _mgr->remove_label(_down_rollover);
+    break;
+  case INACTIVE:
+    if (_inactive != (GuiLabel*)0L)
+      _mgr->remove_label(_inactive);
+    break;
+  case INACTIVE_ROLLOVER:
+    if (_inactive != (GuiLabel*)0L)
+      _mgr->remove_label(_inactive);
+    break;
+  default:
+    gui_cat->warning() << "switching away from invalid state (" << _state
+		       << ")" << endl;
+  }
+  _state = nstate;
+  // deal with new state
+  switch (_state) {
+  case NONE:
+    _rgn->trap_clicks(false);
+    break;
+  case UP:
+    _mgr->add_label(_up);
+    if (!_up_event.empty())
+      throw_event(_up_event);
+    _rgn->trap_clicks(true);
+    break;
+  case UP_ROLLOVER:
+    if (_up_rollover != (GuiLabel*)0L) {
+      _mgr->add_label(_up_rollover);
+      if (!_up_rollover_event.empty())
+	throw_event(_up_rollover_event);
+    } else {
+      _mgr->add_label(_up);
+      if (!_up_event.empty())
+	throw_event(_up_event);
+      _state = UP;
+    }
+    _rgn->trap_clicks(true);
+    break;
+  case DOWN:
+    _mgr->add_label(_down);
+    if (!_down_event.empty())
+      throw_event(_down_event);
+    _rgn->trap_clicks(true);
+    break;
+  case DOWN_ROLLOVER:
+    if (_down_rollover != (GuiLabel*)0L) {
+      _mgr->add_label(_down_rollover);
+      if (!_down_rollover_event.empty())
+	throw_event(_down_rollover_event);
+    } else {
+      _mgr->add_label(_down);
+      if (!_down_event.empty())
+	throw_event(_down_event);
+      _state = DOWN;
+    }
+    _rgn->trap_clicks(true);
+    break;
+  case INACTIVE:
+    if (_inactive != (GuiLabel*)0L) {
+      _mgr->add_label(_inactive);
+      if (!_inactive_event.empty())
+	throw_event(_inactive_event);
+    }
+    _rgn->trap_clicks(false);
+    break;
+  case INACTIVE_ROLLOVER:
+    if (_inactive != (GuiLabel*)0L) {
+      _mgr->add_label(_inactive);
+      if (!_inactive_event.empty())
+	throw_event(_inactive_event);
+    }
+    _rgn->trap_clicks(false);
+    break;
+  default:
+    gui_cat->warning() << "switched to invalid state (" << _state << ")"
+		       << endl;
+  }
+}
+
+GuiButton::GuiButton(const string& name, GuiLabel* up, GuiLabel* up_roll,
+		     GuiLabel* down, GuiLabel* down_roll, GuiLabel* inactive)
+  : Namable(name), _up(up), _up_rollover(up_roll), _down(down),
+    _down_rollover(down_roll), _inactive(inactive), _state(GuiButton::NONE),
+    _added_hooks(false), _mgr((GuiManager*)0L), _up_event(name + "-up"),
+    _up_rollover_event(name + "-up-rollover"), _down_event(name +"-down"),
+    _down_rollover_event(name + "-down-rollover"),
+    _inactive_event(name + "-inactive") {
+  float left, right, bottom, top;
+
+  GetExtents(up, down, up_roll, down_roll, inactive, left, right, bottom, top);
+  _rgn = new GuiRegion("button-" + name, left, right, bottom, top, true);
+  buttons["gui-in-button-" + name] = this;
+  buttons["gui-out-button-" + name] = this;
+  buttons["gui-button-" + name + "-mouse1"] = this;
+  buttons["gui-button-" + name + "-mouse2"] = this;
+  buttons["gui-button-" + name + "-mouse3"] = this;
+}
+
+GuiButton::~GuiButton(void) {
+}
+
+void GuiButton::manage(GuiManager* mgr, EventHandler& eh) {
+  if (!_added_hooks) {
+    eh.add_hook("gui-in-button-" + get_name(), enter_button);
+    eh.add_hook("gui-out-button-" + get_name(), exit_button);
+    eh.add_hook("gui-button-" + get_name() + "-mouse1", click_button);
+    eh.add_hook("gui-button-" + get_name() + "-mouse2", click_button);
+    eh.add_hook("gui-button-" + get_name() + "-mouse3", click_button);
+    _added_hooks = true;
+  }
+  if (_mgr == (GuiManager*)0L) {
+    mgr->add_region(_rgn);
+    _mgr = mgr;
+    switch_state(UP);
+  } else
+    gui_cat->warning() << "tried to manage button (0x" << (void*)this
+		       << ") that is already managed" << endl;
+}
+
+void GuiButton::unmanage(void) {
+  _mgr->remove_region(_rgn);
+  switch_state(NONE);
+  _mgr = (GuiManager*)0L;
+}

+ 63 - 0
panda/src/gui/guiButton.h

@@ -2,3 +2,66 @@
 // Created by:  cary (30Oct00)
 // 
 ////////////////////////////////////////////////////////////////////
+
+#ifndef __GUIBUTTON_H__
+#define __GUIBUTTON_H__
+
+#include "guiRegion.h"
+#include "guiLabel.h"
+#include "guiManager.h"
+
+#include <eventHandler.h>
+
+class GuiButton : public Namable {
+private:
+  GuiLabel* _up;
+  GuiLabel* _up_rollover;
+  GuiLabel* _down;
+  GuiLabel* _down_rollover;
+  GuiLabel* _inactive;
+  string _up_event, _up_rollover_event, _down_event, _down_rollover_event;
+  string _inactive_event;
+  GuiRegion* _rgn;
+
+  enum States { NONE, UP, UP_ROLLOVER, DOWN, DOWN_ROLLOVER, INACTIVE,
+		INACTIVE_ROLLOVER };
+  States _state;
+  bool _added_hooks;
+  GuiManager* _mgr;
+
+  INLINE GuiButton(void);
+  void switch_state(States);
+public:
+  GuiButton(const string&, GuiLabel*, GuiLabel*, GuiLabel*, GuiLabel*,
+	    GuiLabel*);
+  ~GuiButton(void);
+
+  void manage(GuiManager*, EventHandler&);
+  void unmanage(void);
+  INLINE void enter(void);
+  INLINE void exit(void);
+  INLINE void up(void);
+  INLINE void down(void);
+  INLINE void inactive(void);
+  INLINE void click(void);
+
+  INLINE bool is_up(void) const;
+  INLINE bool is_over(void) const;
+  INLINE bool is_active(void) const;
+
+  INLINE void set_up_event(const string&);
+  INLINE void set_up_rollover_event(const string&);
+  INLINE void set_down_event(const string&);
+  INLINE void set_down_rollover_event(const string&);
+  INLINE void set_inactive_event(const string&);
+
+  INLINE const string& get_up_event(void) const;
+  INLINE const string& get_up_rollover_event(void) const;
+  INLINE const string& get_down_event(void) const;
+  INLINE const string& get_down_rollover_event(void) const;
+  INLINE const string& get_inactive_event(void) const;
+};
+
+#include "guiButton.I"
+
+#endif /* __GUIBUTTON_H__ */

+ 13 - 1
panda/src/gui/guiLabel.I

@@ -3,5 +3,17 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-INLINE GuiLabel::GuiLabel(void) {
+INLINE GuiLabel::GuiLabel(void) : _type(GuiLabel::NONE) {
+}
+
+INLINE Node* GuiLabel::get_geometry(void) const {
+  return _geom;
+}
+
+INLINE void GuiLabel::set_arc(RenderRelation* r) {
+  _arc = r;
+}
+
+INLINE RenderRelation* GuiLabel::get_arc(void) const {
+  return _arc;
 }

+ 47 - 0
panda/src/gui/guiLabel.cxx

@@ -4,3 +4,50 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "guiLabel.h"
+
+GuiLabel::~GuiLabel(void) {
+}
+
+GuiLabel* GuiLabel::make_simple_texture_label(void) {
+  return new GuiLabel();
+}
+
+#include <textNode.h>
+
+GuiLabel* GuiLabel::make_simple_text_label(const string& text, Node* font) {
+  GuiLabel* ret = new GuiLabel();
+  ret->_type = SIMPLE_TEXT;
+  TextNode* n = new TextNode("GUI label");
+  ret->_geom = n;
+  LMatrix4f mat = LMatrix4f::scale_mat(0.1);
+  n->set_transform(mat);
+  n->set_font(font);
+  // n->set_card_color(1., 1., 1., 0.);
+  n->set_align(TM_ALIGN_CENTER);
+  n->set_text_color(1., 1., 1., 1.);
+  n->set_text(text);
+  return ret;
+}
+
+void GuiLabel::get_extents(float& l, float& r, float& b, float& t) {
+  switch (_type) {
+  case SIMPLE_TEXT:
+    {
+      TextNode* n = DCAST(TextNode, _geom);
+      LVector3f ul = n->get_upper_left_3d() - LPoint3f::origin();
+      LVector3f lr = n->get_lower_right_3d() - LPoint3f::origin();
+      LVector3f up = LVector3f::up();
+      LVector3f right = LVector3f::right();
+      l = ul.dot(right);
+      r = lr.dot(right);
+      b = lr.dot(up);
+      t = ul.dot(up);
+    }
+    break;
+  default:
+    gui_cat->warning()
+      << "trying to get extents from something I don't know how to" << endl;
+    l = b = 0.;
+    r = t = 1.;
+  }
+}

+ 27 - 0
panda/src/gui/guiLabel.h

@@ -6,12 +6,39 @@
 #ifndef __GUILABEL_H__
 #define __GUILABEL_H__
 
+#include "config_gui.h"
+
+#include <pandabase.h>
+#include <node.h>
+#include <pt_Node.h>
+#include <renderRelation.h>
+
 // label-ish behavior for GUI objects (labels, buttons, rollovers)
 
+class GuiManager;
+
 class GuiLabel {
 private:
+  enum LabelType { NONE, SIMPLE_TEXTURE, SIMPLE_TEXT };
+  LabelType _type;
+  PT_Node _geom;
+  RenderRelation* _arc;
+
+  INLINE Node* get_geometry(void) const;
+  INLINE void set_arc(RenderRelation*);
+  INLINE RenderRelation* get_arc(void) const;
+
+  friend GuiManager;
 public:
   INLINE GuiLabel(void);
+  virtual ~GuiLabel(void);
+
+  static GuiLabel* make_simple_texture_label(void);
+  static GuiLabel* make_simple_text_label(const string&, Node*);
+
+  void get_extents(float&, float&, float&, float&);
 };
 
+#include "guiLabel.I"
+
 #endif /* __GUILABEL_H__ */

+ 2 - 5
panda/src/gui/guiManager.I

@@ -3,9 +3,6 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-INLINE GuiManager* GuiManager::get_ptr(void) {
-  if (_singleton == (GuiManager*)0L) {
-    _singleton = new GuiManager;
-  }
-  return _singleton;
+INLINE GuiManager::GuiManager(MouseWatcher* w, Node* n) : _root(n),
+							  _watcher(w) {
 }

+ 92 - 4
panda/src/gui/guiManager.cxx

@@ -4,6 +4,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "guiManager.h"
+#include "config_gui.h"
 
 #include <dataRelation.h>
 #include <renderRelation.h>
@@ -19,14 +20,22 @@ GuiManager::GuiMap* GuiManager::_map = (GuiManager::GuiMap*)0L;
 
 GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak) {
   GuiManager* ret;
-  if (_map == (GuiMap*)0L)
+  if (_map == (GuiMap*)0L) {
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "allocating a manager map" << endl;
     _map = new GuiMap;
+  }
   GuiMap::const_iterator gi;
   gi = _map->find(w);
-  if (gi != _map->end())
+  if (gi != _map->end()) {
     ret = (*gi).second;
-  else {
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "a manager for this window already exists (0x"
+		       << (void*)ret << ")" << endl;
+  } else {
     // going to allocate a new GuiManager for this window
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "allocating a new manager for this window" << endl;
     // first see if there is a mouseWatcher already under the MouseAndKeyboard
     bool has_watcher = false;
     TypeHandle dgt = DataRelation::get_class_type();
@@ -40,6 +49,8 @@ GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak) {
     if (!has_watcher) {
       // there isn't already a mousewatcher in the data graph, so we'll make
       // one and re-parent everything to it.
+      if (gui_cat->is_debug())
+	gui_cat->debug() << "no MouseWatcher found, making one" << endl;
       watcher = new MouseWatcher("GUI watcher");
       DataRelation* tmp = new DataRelation(mak, watcher);
       for (int j=0; j<mak->get_num_children(dgt); ++j) {
@@ -48,7 +59,30 @@ GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak) {
 	  // it's not the node we just created, so reparent it to ours
 	  rel->change_parent(watcher);
       }
-    }
+    } else if (gui_cat->is_debug())
+      gui_cat->debug() << "found a MouseWatcher, don't have to make one"
+		       << endl;
+    // now setup event triggers for the watcher
+    if (has_watcher)
+      gui_cat->warning() << "overwriting existing button down pattern '"
+			 << watcher->get_button_down_pattern()
+			 << "' with 'gui-%r-%b'" << endl;
+    watcher->set_button_down_pattern("gui-%r-%b");
+    if (has_watcher)
+      gui_cat->warning() << "overwriting existing button up pattern '"
+			 << watcher->get_button_up_pattern()
+			 << "' with 'gui-%r-%b-up'" << endl;
+    watcher->set_button_up_pattern("gui-%r-%b");
+    if (has_watcher)
+      gui_cat->warning() << "overwriting existing enter pattern '"
+			 << watcher->get_enter_pattern()
+			 << "' with 'gui-in-%r'" << endl;
+    watcher->set_enter_pattern("gui-in-%r");
+    if (has_watcher)
+      gui_cat->warning() << "overwriting existing exit pattern '"
+			 << watcher->get_leave_pattern()
+			 << "' with 'gui-out-%r'" << endl;
+    watcher->set_leave_pattern("gui-out-%r");
     // next, create a 2d layer for the GUI stuff to live in.
     Node* root2d_top = new NamedNode("GUI_top");
     Node* root2d = new NamedNode("GUI");
@@ -71,9 +105,63 @@ GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak) {
     DisplayRegion *dr = layer->make_display_region();
     nassertv(dr != (DisplayRegion*)0L);
     dr->set_camera(cam);
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "2D layer created" << endl;
     // now make the manager for this window
     ret = new GuiManager(watcher, root2d);
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "new manager allocated (0x" << (void*)ret << ")"
+		       << endl;
     (*_map)[w] = ret;
   }
   return ret;
 }
+
+void GuiManager::add_region(GuiRegion* region) {
+  RegionSet::const_iterator ri;
+  ri = _regions.find(region);
+  if (ri == _regions.end()) {
+    _watcher->add_region(region->get_region());
+    _regions.insert(region);
+  } else
+    gui_cat->warning() << "tried adding region ('" << *region
+		       << "') more then once" << endl;
+}
+
+void GuiManager::add_label(GuiLabel* label) {
+  LabelSet::const_iterator li;
+  li = _labels.find(label);
+  if (li == _labels.end()) {
+    // add it to the scenegraph
+    label->set_arc(new RenderRelation(_root, label->get_geometry()));
+    _labels.insert(label);
+  } else
+    gui_cat->warning() << "tried adding label (0x" << (void*)label
+		       << ") more then once" << endl;
+}
+
+void GuiManager::remove_region(GuiRegion* region) {
+  RegionSet::const_iterator ri;
+  ri = _regions.find(region);
+  if (ri == _regions.end())
+    gui_cat->warning() << "tried removing region ('" << *region
+		       << "') that isn't there" << endl;
+  else {
+    _watcher->remove_region(region->get_region());
+    _regions.erase(ri);
+  }
+}
+
+void GuiManager::remove_label(GuiLabel* label) {
+  LabelSet::const_iterator li;
+  li = _labels.find(label);
+  if (li == _labels.end())
+    gui_cat->warning() << "label (0x" << (void*)label
+		       << ") is not there to be removed" << endl;
+  else {
+    // remove it to the scenegraph
+    remove_arc(label->get_arc());
+    label->set_arc((RenderRelation*)0L);
+    _labels.erase(li);
+  }
+}

+ 31 - 2
panda/src/gui/guiManager.h

@@ -6,11 +6,40 @@
 #ifndef __GUIMANAGER_H__
 #define __GUIMANAGER_H__
 
+#include <pandabase.h>
+#include <graphicsWindow.h>
+#include <mouse.h>
+#include <mouseWatcher.h>
+#include <node.h>
+#include <set>
+
+#include "guiRegion.h"
+#include "guiLabel.h"
+#include "config_gui.h"
+
 class GuiManager {
 private:
-  static GuiManager* _singleton;
+  typedef map<GraphicsWindow*, GuiManager*> GuiMap;
+  static GuiMap* _map;
+  typedef set<GuiRegion*> RegionSet;
+  RegionSet _regions;
+  typedef set<GuiLabel*> LabelSet;
+  LabelSet _labels;
+
+  Node* _root;
+  MouseWatcher* _watcher;
+
+  INLINE GuiManager(MouseWatcher*, Node*);
 public:
-  INLINE static GuiManager* get_ptr(void);
+  static GuiManager* get_ptr(GraphicsWindow*, MouseAndKeyboard*);
+
+  void add_region(GuiRegion*);
+  void add_label(GuiLabel*);
+
+  void remove_region(GuiRegion*);
+  void remove_label(GuiLabel*);
 };
 
+#include "guiManager.I"
+
 #endif /* __GUIMANAGER_H__ */

+ 15 - 2
panda/src/gui/guiRegion.I

@@ -3,8 +3,21 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-INLINE GiuRegion::GuiRegion(void) {
+INLINE GuiRegion::GuiRegion(void) {
 }
 
-INLINE GiuRegion::GuiRegion(float, float, float, float) {
+INLINE GuiRegion::GuiRegion(const string& name, float l, float r, float b,
+			    float t, bool buttons) : Namable(name), _left(l),
+						     _right(r), _bottom(b),
+						     _top(t) {
+  _region = new MouseWatcherRegion(name, l, r, b, t);
+  _region->set_suppress_below(buttons);
+}
+
+INLINE MouseWatcherRegion* GuiRegion::get_region(void) const {
+  return _region;
+}
+
+INLINE void GuiRegion::trap_clicks(bool t) {
+  _region->set_suppress_below(t);
 }

+ 3 - 0
panda/src/gui/guiRegion.cxx

@@ -4,3 +4,6 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "guiRegion.h"
+
+GuiRegion::~GuiRegion(void) {
+}

+ 18 - 2
panda/src/gui/guiRegion.h

@@ -6,13 +6,29 @@
 #ifndef __GUIREGION_H__
 #define __GUIREGION_H__
 
+#include <pandabase.h>
+#include <mouseWatcherRegion.h>
+#include <pointerTo.h>
+
 // container for active regions of a GUI
 
-class GuiRegion {
+class GuiManager;
+
+class GuiRegion : public Namable {
 private:
   float _left, _right, _bottom, _top;
+  PT(MouseWatcherRegion) _region;
+
+  INLINE GuiRegion(void);
+
+  INLINE MouseWatcherRegion* get_region(void) const;
+
+  friend GuiManager;
 public:
-  INLINE GuiRegion(float, float, float, float);
+  INLINE GuiRegion(const string&, float, float, float, float, bool);
+  ~GuiRegion(void);
+
+  INLINE void trap_clicks(bool);
 };
 
 #include "guiRegion.I"

+ 29 - 1
panda/src/gui/guiRollover.I

@@ -3,5 +3,33 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-INLINE GuiRollover::GuiRollover(float, float, float, float) {
+INLINE GuiRollover::GuiRollover(void) {
+}
+
+INLINE void GuiRollover::enter(void) {
+  if (_state) {
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "entered '" << *this
+		       << "' more then once without exit" << endl;
+  } else if (_mgr != (GuiManager*)0L) {
+    _mgr->remove_label(_off);
+    _mgr->add_label(_on);
+    _state = true;
+  }
+}
+
+INLINE void GuiRollover::exit(void) {
+  if (!_state) {
+    if (gui_cat->is_debug())
+      gui_cat->debug() << "exited '" << *this
+		       << "' more then once without enter" << endl;
+  } else if (_mgr != (GuiManager*)0L) {
+    _mgr->remove_label(_on);
+    _mgr->add_label(_off);
+    _state = false;
+  }
+}
+
+INLINE bool GuiRollover::is_over(void) const {
+  return _state;
 }

+ 64 - 0
panda/src/gui/guiRollover.cxx

@@ -4,3 +4,67 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "guiRollover.h"
+#include "config_gui.h"
+
+#include <map>
+
+typedef map<string, GuiRollover*> RolloverMap;
+static RolloverMap rollovers;
+
+inline void GetExtents(GuiLabel* x, GuiLabel* y, float& l, float& r, float& b,
+		       float& t) {
+  float l1, l2, r1, r2, b1, b2, t1, t2;
+  x->get_extents(l1, r1, b1, t1);
+  y->get_extents(l2, r2, b2, t2);
+  l = (l1<l2)?l1:l2;
+  r = (r1<r2)?r2:r1;
+  b = (b1<b2)?b1:b2;
+  t = (t1<t2)?t2:t1;
+}
+
+static void enter_rollover(CPT_Event e) {
+  GuiRollover* val = rollovers[e->get_name()];
+  val->enter();
+}
+
+static void exit_rollover(CPT_Event e) {
+  GuiRollover* val = rollovers[e->get_name()];
+  val->exit();
+}
+
+GuiRollover::GuiRollover(const string& name, GuiLabel* off, GuiLabel* on)
+  : Namable(name), _off(off), _on(on), _state(false), _added_hooks(false) {
+  float left, right, bottom, top;
+
+  GetExtents(off, on, left, right, bottom, top);
+  _rgn = new GuiRegion("rollover-" + name, left, right, bottom, top, false);
+  rollovers["gui-in-rollover-" + name] = this;
+  rollovers["gui-out-rollover-" + name] = this;
+  _mgr = (GuiManager*)0L;
+}
+
+GuiRollover::~GuiRollover(void) {
+}
+
+void GuiRollover::manage(GuiManager* mgr, EventHandler& eh) {
+  if (!_added_hooks) {
+    eh.add_hook("gui-in-rollover-" + get_name(), enter_rollover);
+    eh.add_hook("gui-out-rollover-" + get_name(), exit_rollover);
+    _added_hooks = true;
+  }
+  if (_mgr == (GuiManager*)0L) {
+    mgr->add_region(_rgn);
+    _state = false;
+    mgr->add_label(_off);
+    _mgr = mgr;
+  } else
+    gui_cat->warning() << "tried to manage rollover (0x" << (void*)this
+		       << ") that is already managed" << endl;
+}
+
+void GuiRollover::unmanage(void) {
+  _mgr->remove_region(_rgn);
+  _mgr->remove_label(_off);
+  _mgr->remove_label(_on);
+  _mgr = (GuiManager*)0L;
+}

+ 27 - 2
panda/src/gui/guiRollover.h

@@ -6,10 +6,35 @@
 #ifndef __GUIROLLOVER_H__
 #define __GUIROLLOVER_H__
 
-class GuiRollover : public GuiBaseButton {
+#include "guiRegion.h"
+#include "guiLabel.h"
+#include "guiManager.h"
+
+#include <eventHandler.h>
+
+class GuiRollover : public Namable {
 private:
+  GuiLabel* _off;
+  GuiLabel* _on;
+  GuiRegion* _rgn;
+
+  bool _state;
+  bool _added_hooks;
+  GuiManager* _mgr;
+
+  INLINE GuiRollover(void);
 public:
-  INLINE GuiRollover(float, float, float, float);
+  GuiRollover(const string&, GuiLabel*, GuiLabel*);
+  ~GuiRollover(void);
+
+  void manage(GuiManager*, EventHandler&);
+  void unmanage(void);
+  INLINE void enter(void);
+  INLINE void exit(void);
+
+  INLINE bool is_over(void) const;
 };
 
+#include "guiRollover.I"
+
 #endif /* __GUIROLLOVER_H__ */

+ 9 - 0
panda/src/testbed/Sources.pp

@@ -131,3 +131,12 @@
 
 #end test_bin_target
 
+#begin test_bin_target
+  #define TARGET gui_demo
+
+  #define SOURCES \
+    gui_demo.cxx
+
+  #define LOCAL_LIBS $[LOCAL_LIBS] gui
+
+#end test_bin_target

+ 20 - 0
panda/src/testbed/gui_demo.cxx

@@ -40,8 +40,11 @@
 #include <pta_Colorf.h>
 #include <pta_float.h>
 #include <pt_Node.h>
+#include <modelPool.h>
 
 #include <guiManager.h>
+#include <guiRollover.h>
+#include <guiButton.h>
 
 //From framework
 extern PT(GeomNode) geomnode;
@@ -49,6 +52,23 @@ extern RenderRelation* first_arc;
 
 static void setup_gui(void) {
   GuiManager* mgr = GuiManager::get_ptr(main_win, mak);
+  PT_Node font = ModelPool::load_model("ttf-comic");
+  // test 1
+  //  mgr->add_region(new GuiRegion("test1", 0., 0.25, 0., 0.25));
+  //  mgr->add_label(GuiLabel::make_simple_text_label("test2", font));
+  // test 2
+  //  GuiLabel* l1 = GuiLabel::make_simple_text_label("off", font);
+  //  GuiLabel* l2 = GuiLabel::make_simple_text_label("on", font);
+  //  GuiRollover* r1 = new GuiRollover("test2", l1, l2);
+  //  r1->manage(mgr, event_handler);
+  // test 3
+  GuiLabel* l1 = GuiLabel::make_simple_text_label("up", font);
+  GuiLabel* l2 = GuiLabel::make_simple_text_label("upr", font);
+  GuiLabel* l3 = GuiLabel::make_simple_text_label("down", font);
+  GuiLabel* l4 = GuiLabel::make_simple_text_label("downr", font);
+  GuiLabel* l5 = GuiLabel::make_simple_text_label("none", font);
+  GuiButton* b1 = new GuiButton("test3", l1, l2, l3, l4, l5);
+  b1->manage(mgr, event_handler);
 }
 
 static void event_2(CPT_Event) {