From 68e199f6966569aa0b5503908b423ea2a0c68973 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 3 Jul 2001 23:45:03 +0000 Subject: [PATCH] initial pgui stuff --- panda/metalibs/panda/Sources.pp | 2 +- panda/src/graph/Sources.pp | 6 +- panda/src/graph/arcChain.I | 69 +++- panda/src/graph/arcChain.cxx | 89 +++++ panda/src/graph/arcChain.h | 17 +- panda/src/pgui/Sources.pp | 40 ++ panda/src/pgui/config_pgui.cxx | 35 ++ panda/src/pgui/config_pgui.h | 27 ++ panda/src/pgui/pgButton.I | 78 ++++ panda/src/pgui/pgButton.cxx | 212 +++++++++++ panda/src/pgui/pgButton.h | 95 +++++ panda/src/pgui/pgFrameStyle.I | 150 ++++++++ panda/src/pgui/pgFrameStyle.cxx | 133 +++++++ panda/src/pgui/pgFrameStyle.h | 79 ++++ panda/src/pgui/pgItem.I | 233 ++++++++++++ panda/src/pgui/pgItem.cxx | 396 ++++++++++++++++++++ panda/src/pgui/pgItem.h | 145 +++++++ panda/src/pgui/pgMouseWatcherRegion.I | 18 + panda/src/pgui/pgMouseWatcherRegion.cxx | 106 ++++++ panda/src/pgui/pgMouseWatcherRegion.h | 71 ++++ panda/src/pgui/pgTop.I | 56 +++ panda/src/pgui/pgTop.cxx | 256 +++++++++++++ panda/src/pgui/pgTop.h | 99 +++++ panda/src/sgmanip/nodePath.I | 108 ++---- panda/src/sgmanip/nodePath.cxx | 99 ----- panda/src/sgmanip/nodePath.h | 13 +- panda/src/sgraphutil/frustumCullTraverser.I | 2 +- panda/src/tform/Sources.pp | 16 +- panda/src/tform/config_tform.cxx | 2 + panda/src/tform/mouseWatcher.I | 2 +- panda/src/tform/mouseWatcher.cxx | 135 ++++--- panda/src/tform/mouseWatcher.h | 19 +- panda/src/tform/mouseWatcherGroup.cxx | 87 +++++ panda/src/tform/mouseWatcherGroup.h | 60 +++ panda/src/tform/mouseWatcherRegion.cxx | 44 +++ panda/src/tform/mouseWatcherRegion.h | 6 + panda/src/tform/tform_composite1.cxx | 1 + panda/src/tform/tform_composite2.cxx | 2 +- 38 files changed, 2732 insertions(+), 276 deletions(-) create mode 100644 panda/src/pgui/Sources.pp create mode 100644 panda/src/pgui/config_pgui.cxx create mode 100644 panda/src/pgui/config_pgui.h create mode 100644 panda/src/pgui/pgButton.I create mode 100644 panda/src/pgui/pgButton.cxx create mode 100644 panda/src/pgui/pgButton.h create mode 100644 panda/src/pgui/pgFrameStyle.I create mode 100644 panda/src/pgui/pgFrameStyle.cxx create mode 100644 panda/src/pgui/pgFrameStyle.h create mode 100644 panda/src/pgui/pgItem.I create mode 100644 panda/src/pgui/pgItem.cxx create mode 100644 panda/src/pgui/pgItem.h create mode 100644 panda/src/pgui/pgMouseWatcherRegion.I create mode 100644 panda/src/pgui/pgMouseWatcherRegion.cxx create mode 100644 panda/src/pgui/pgMouseWatcherRegion.h create mode 100644 panda/src/pgui/pgTop.I create mode 100644 panda/src/pgui/pgTop.cxx create mode 100644 panda/src/pgui/pgTop.h create mode 100644 panda/src/tform/mouseWatcherGroup.cxx create mode 100644 panda/src/tform/mouseWatcherGroup.h diff --git a/panda/metalibs/panda/Sources.pp b/panda/metalibs/panda/Sources.pp index 988aac6371..0094c808f1 100644 --- a/panda/metalibs/panda/Sources.pp +++ b/panda/metalibs/panda/Sources.pp @@ -16,7 +16,7 @@ parametrics pnm \ pnmimagetypes pnmimage sgattrib sgmanip sgraph sgraphutil \ switchnode text tform tiff lerp loader putil effects \ - audio gui pandabase + audio gui pgui pandabase diff --git a/panda/src/graph/Sources.pp b/panda/src/graph/Sources.pp index 2b0c15fdcf..5110e7b697 100644 --- a/panda/src/graph/Sources.pp +++ b/panda/src/graph/Sources.pp @@ -48,8 +48,8 @@ #define INCLUDED_SOURCES \ allAttributesWrapper.cxx allTransitionsWrapper.cxx \ - arcChain.cxxbitMask32Transition.cxx \ - boundedObject.cxxconfig_graph.cxx graphReducer.cxx \ + arcChain.cxx bitMask32Transition.cxx \ + boundedObject.cxx config_graph.cxx graphReducer.cxx \ immediateAttribute.cxx immediateTransition.cxx \ lmatrix4fTransition.cxx multiNodeAttribute.cxx \ multiNodeTransition.cxx namedNode.cxx node.cxx \ @@ -58,7 +58,7 @@ nodeTransition.cxx nodeTransitionCache.cxx \ nodeTransitionCacheEntry.cxx nodeTransitionWrapper.cxx \ nodeTransitions.cxx \ - nullAttributeWrapper.cxxnullLevelState.cxx \ + nullAttributeWrapper.cxx nullLevelState.cxx \ nullTransitionWrapper.cxx onAttribute.cxx onOffAttribute.cxx \ onOffTransition.cxx onTransition.cxx pt_NamedNode.cxx \ pt_Node.cxx pt_NodeRelation.cxx vector_PT_Node.cxx \ diff --git a/panda/src/graph/arcChain.I b/panda/src/graph/arcChain.I index 4915f268bb..71c6889d32 100644 --- a/panda/src/graph/arcChain.I +++ b/panda/src/graph/arcChain.I @@ -301,27 +301,82 @@ operator = (const ArcChain ©) { } //////////////////////////////////////////////////////////////////// -// Function: ArcChain::has_node +// Function: ArcChain::is_empty // Access: Public -// Description: Returns true if there is at least a top node in the -// chain, or false otherwise. +// Description: Returns true if the ArcChain contains no nodes and no +// arcs. //////////////////////////////////////////////////////////////////// INLINE_GRAPH bool ArcChain:: -has_node() const { - return (_head != (ArcComponent *)NULL); +is_empty() const { + return (_head == (ArcComponent *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::is_singleton +// Access: Public +// Description: Returns true if the ArcChain contains exactly one +// node, and no arcs. +//////////////////////////////////////////////////////////////////// +INLINE_GRAPH bool ArcChain:: +is_singleton() const { + return (_head != (ArcComponent *)NULL && _head->is_top_node()); } //////////////////////////////////////////////////////////////////// // Function: ArcChain::has_arcs // Access: Public -// Description: Returns true if there are any arcs in the chain, or -// false otherwise. +// Description: Returns true if the ArcChain contains at least one +// arc, and therefore at least two nodes. This is the +// same thing as asking get_num_arcs() > 0, but is +// easier to compute. //////////////////////////////////////////////////////////////////// INLINE_GRAPH bool ArcChain:: has_arcs() const { return (_head != (ArcComponent *)NULL && !_head->is_top_node()); } +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::get_num_arcs +// Access: Public +// Description: Returns the number of arcs in the path. +//////////////////////////////////////////////////////////////////// +INLINE_GRAPH int ArcChain:: +get_num_arcs() const { + if (!has_arcs()) { + return 0; + } + return get_num_nodes() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::node +// Access: Public +// Description: Returns the bottom node of the path, or NULL if the +// path is empty. +//////////////////////////////////////////////////////////////////// +INLINE_GRAPH Node *ArcChain:: +node() const { + if (is_empty()) { + return (Node *)NULL; + } + return _head->get_node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::arc +// Access: Public +// Description: Returns the bottom arc of the path, or NULL if the +// path is empty or is a singleton. +//////////////////////////////////////////////////////////////////// +INLINE_GRAPH NodeRelation *ArcChain:: +arc() const { + if (!has_arcs()) { + // A singleton or empty list. + return (NodeRelation *)NULL; + } + return _head->get_arc(); +} + //////////////////////////////////////////////////////////////////// // Function: ArcChain::begin // Access: Public diff --git a/panda/src/graph/arcChain.cxx b/panda/src/graph/arcChain.cxx index 9e05ac2563..965f775519 100644 --- a/panda/src/graph/arcChain.cxx +++ b/panda/src/graph/arcChain.cxx @@ -60,6 +60,95 @@ operator = (const ArcComponent ©) { } } +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::get_num_nodes +// Access: Public +// Description: Returns the number of nodes in the path. This is +// always one more than the number of arcs (except for +// a completely empty path). +//////////////////////////////////////////////////////////////////// +int ArcChain:: +get_num_nodes() const { + int num = 0; + ArcComponent *comp = _head; + while (comp != (ArcComponent *)NULL) { + num++; + comp = comp->get_next(); + } + return num; +} + +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::get_node +// Access: Public +// Description: Returns the nth node of the path, where 0 is the +// bottom node and get_num_nodes() - 1 is the top node. +// This requires iterating through the path. +//////////////////////////////////////////////////////////////////// +Node *ArcChain:: +get_node(int index) const { + nassertr(index >= 0 && index < get_num_nodes(), NULL); + + ArcComponent *comp = _head; + while (index > 0) { + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + comp = comp->get_next(); + index--; + } + + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + return comp->get_node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::get_arc +// Access: Public +// Description: Returns the nth arc of the path, where 0 is the arc +// above the bottom node node and get_num_arcs() - 1 is +// the arc below the top node. This requires iterating +// through the path. +//////////////////////////////////////////////////////////////////// +NodeRelation *ArcChain:: +get_arc(int index) const { + nassertr(index >= 0 && index < get_num_arcs(), NULL); + + ArcComponent *comp = _head; + while (index > 0) { + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + comp = comp->get_next(); + index--; + } + + // If either assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + nassertr(comp->has_arc(), NULL); + return comp->get_arc(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ArcChain::get_top_node +// Access: Public +// Description: Returns the top node of the path, or NULL if the path +// is empty. This requires iterating through the path. +//////////////////////////////////////////////////////////////////// +Node *ArcChain:: +get_top_node() const { + if (is_empty()) { + return (Node *)NULL; + } + + ArcComponent *comp = _head; + while (!comp->is_top_node()) { + comp = comp->get_next(); + nassertr(comp != (ArcComponent *)NULL, NULL); + } + + return comp->get_node(); +} + //////////////////////////////////////////////////////////////////// // Function: ArcChain::r_output // Access: Private diff --git a/panda/src/graph/arcChain.h b/panda/src/graph/arcChain.h index 53c95d26ff..a905bfdc5b 100644 --- a/panda/src/graph/arcChain.h +++ b/panda/src/graph/arcChain.h @@ -141,8 +141,23 @@ public: INLINE_GRAPH ArcChain(const ArcChain ©); INLINE_GRAPH void operator = (const ArcChain ©); - INLINE_GRAPH bool has_node() const; + // Methods to query an ArcChain's contents. +PUBLISHED: + INLINE_GRAPH bool is_empty() const; + INLINE_GRAPH bool is_singleton() const; INLINE_GRAPH bool has_arcs() const; + int get_num_nodes() const; + Node *get_node(int index) const; + + INLINE_GRAPH int get_num_arcs() const; + NodeRelation *get_arc(int index) const; + + Node *get_top_node() const; + INLINE_GRAPH Node *node() const; + INLINE_GRAPH NodeRelation *arc() const; + +public: + // Methods to make an ArcChain behave like an STL container. INLINE_GRAPH const_iterator begin() const; INLINE_GRAPH const_iterator end() const; diff --git a/panda/src/pgui/Sources.pp b/panda/src/pgui/Sources.pp new file mode 100644 index 0000000000..64fe4e4df1 --- /dev/null +++ b/panda/src/pgui/Sources.pp @@ -0,0 +1,40 @@ +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m + +#begin lib_target + #define TARGET pgui + #define LOCAL_LIBS \ + text tform graph linmath event putil gobj \ + mathutil sgraph sgraphutil + +// #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx + + #define SOURCES \ + config_pgui.h \ + pgButton.I pgButton.h \ + pgFrameStyle.I pgFrameStyle.h \ + pgItem.I pgItem.h \ + pgMouseWatcherRegion.I pgMouseWatcherRegion.h \ + pgTop.I pgTop.h + +// #define INCLUDED_SOURCES \ + #define SOURCES $[SOURCES] \ + config_pgui.cxx \ + pgButton.cxx \ + pgFrameStyle.cxx \ + pgItem.cxx \ + pgMouseWatcherRegion.cxx \ + pgTop.cxx + + #define INSTALL_HEADERS \ + pgButton.I pgButton.h \ + pgFrameStyle.I pgFrameStyle.h \ + pgItem.I pgItem.h \ + pgMouseWatcherRegion.I pgMouseWatcherRegion.h \ + pgTop.I pgTop.h + + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/pgui/config_pgui.cxx b/panda/src/pgui/config_pgui.cxx new file mode 100644 index 0000000000..da8f83fd08 --- /dev/null +++ b/panda/src/pgui/config_pgui.cxx @@ -0,0 +1,35 @@ +// Filename: config_pgui.cxx +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "config_pgui.h" +#include "pgButton.h" +#include "pgItem.h" +#include "pgMouseWatcherRegion.h" +#include "pgTop.h" + +#include "dconfig.h" + +Configure(config_pgui); +NotifyCategoryDef(pgui, ""); + +ConfigureFn(config_pgui) { + PGButton::init_type(); + PGItem::init_type(); + PGMouseWatcherRegion::init_type(); + PGTop::init_type(); +} diff --git a/panda/src/pgui/config_pgui.h b/panda/src/pgui/config_pgui.h new file mode 100644 index 0000000000..5cf474340c --- /dev/null +++ b/panda/src/pgui/config_pgui.h @@ -0,0 +1,27 @@ +// Filename: config_pgui.h +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PGUI_H +#define CONFIG_PGUI_H + +#include +#include + +NotifyCategoryDecl(pgui, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/pgui/pgButton.I b/panda/src/pgui/pgButton.I new file mode 100644 index 0000000000..b3bc798672 --- /dev/null +++ b/panda/src/pgui/pgButton.I @@ -0,0 +1,78 @@ +// Filename: pgButton.I +// Created by: drose (03Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::setup +// Access: Published +// Description: Sets up the button using the indicated NodePath as +// arbitrary geometry. +//////////////////////////////////////////////////////////////////// +INLINE void PGButton:: +setup(const ArcChain &ready) { + setup(ready, ready, ready, ready); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::setup +// Access: Published +// Description: Sets up the button using the indicated NodePath as +// arbitrary geometry. +//////////////////////////////////////////////////////////////////// +INLINE void PGButton:: +setup(const ArcChain &ready, const ArcChain &depressed) { + setup(ready, depressed, ready, ready); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::setup +// Access: Published +// Description: Sets up the button using the indicated NodePath as +// arbitrary geometry. +//////////////////////////////////////////////////////////////////// +INLINE void PGButton:: +setup(const ArcChain &ready, const ArcChain &depressed, + const ArcChain &rollover) { + setup(ready, depressed, rollover, ready); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::set_active +// Access: Public +// Description: Toggles the active/inactive state of the button. In +// the case of a PGButton, this also changes its visual +// appearance. +//////////////////////////////////////////////////////////////////// +INLINE void PGButton:: +set_active(bool active) { + if (active != get_active()) { + set_state(active ? S_ready : S_inactive); + PGItem::set_active(active); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::get_click_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// button is clicked normally. +//////////////////////////////////////////////////////////////////// +INLINE string PGButton:: +get_click_event() const { + return "click-" + get_id(); +} diff --git a/panda/src/pgui/pgButton.cxx b/panda/src/pgui/pgButton.cxx new file mode 100644 index 0000000000..f275262816 --- /dev/null +++ b/panda/src/pgui/pgButton.cxx @@ -0,0 +1,212 @@ +// Filename: pgButton.cxx +// Created by: drose (03Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pgButton.h" +#include "throw_event.h" +#include "renderRelation.h" +#include "colorTransition.h" + +TypeHandle PGButton::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +PGButton:: +PGButton(const string &name) : PGItem(name) +{ + _button_down = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGButton:: +~PGButton() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PGButton:: +PGButton(const PGButton ©) : + PGItem(copy) +{ + _button_down = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PGButton:: +operator = (const PGButton ©) { + PGItem::operator = (copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *PGButton:: +make_copy() const { + return new PGButton(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::enter +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse enters the region. +//////////////////////////////////////////////////////////////////// +void PGButton:: +enter() { + if (get_active()) { + set_state(_button_down ? S_depressed : S_rollover); + } + PGItem::enter(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::exit +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse exits the region. +//////////////////////////////////////////////////////////////////// +void PGButton:: +exit() { + if (get_active()) { + set_state(S_ready); + } + PGItem::exit(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::button_down +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button is depressed while the mouse +// is within the region. +//////////////////////////////////////////////////////////////////// +void PGButton:: +button_down(ButtonHandle button) { + if (get_active()) { + _button_down = true; + set_state(S_depressed); + } + PGItem::button_down(button); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::button_up +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button previously depressed with +// button_down() is release. The bool is_within flag is +// true if the button was released while the mouse was +// still within the region, or false if it was released +// outside the region. +//////////////////////////////////////////////////////////////////// +void PGButton:: +button_up(ButtonHandle button, bool is_within) { + _button_down = false; + if (get_active()) { + if (is_within) { + set_state(S_rollover); + click(); + } else { + set_state(S_ready); + } + } + PGItem::button_up(button, is_within); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::click +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// button is clicked normally. +//////////////////////////////////////////////////////////////////// +void PGButton:: +click() { + throw_event(get_click_event()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::setup +// Access: Published +// Description: Sets up the button as a default text button using the +// indicated label string. The TextNode defined by +// PGItem::get_text_node() will be used to create the +// label geometry. This automatically sets up the frame +// according to the size of the text. +//////////////////////////////////////////////////////////////////// +void PGButton:: +setup(const string &label) { + TextNode *text_node = get_text_node(); + text_node->set_text(label); + PT_Node geom = text_node->generate(); + + new RenderRelation(get_state_def(S_ready), geom); + NodeRelation *dep = new RenderRelation(get_state_def(S_depressed), geom); + new RenderRelation(get_state_def(S_rollover), geom); + new RenderRelation(get_state_def(S_inactive), geom); + + PGFrameStyle style; + style.set_type(PGFrameStyle::T_flat); + style.set_color(1.0, 1.0, 1.0, 1.0); + set_frame_style(S_ready, style); + + style.set_color(0.0, 0.0, 0.0, 1.0); + set_frame_style(S_depressed, style); + ColorTransition *col_trans = new ColorTransition(Colorf(0.0, 1.0, 1.0, 1.0)); + dep->set_transition(col_trans); + + style.set_color(1.0, 1.0, 0.0, 1.0); + set_frame_style(S_rollover, style); + + style.set_color(0.6, 0.6, 0.6, 1.0); + set_frame_style(S_inactive, style); + + set_frame(text_node->get_card_actual()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::setup +// Access: Published +// Description: Sets up the button using the indicated NodePath as +// arbitrary geometry. +//////////////////////////////////////////////////////////////////// +void PGButton:: +setup(const ArcChain &ready, const ArcChain &depressed, + const ArcChain &rollover, const ArcChain &inactive) { + instance_to_state_def(S_ready, ready); + instance_to_state_def(S_depressed, depressed); + instance_to_state_def(S_rollover, rollover); + instance_to_state_def(S_inactive, inactive); +} diff --git a/panda/src/pgui/pgButton.h b/panda/src/pgui/pgButton.h new file mode 100644 index 0000000000..d7bb3bf8f1 --- /dev/null +++ b/panda/src/pgui/pgButton.h @@ -0,0 +1,95 @@ +// Filename: pgButton.h +// Created by: drose (03Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGBUTTON_H +#define PGBUTTON_H + +#include "pandabase.h" + +#include "pgItem.h" +#include "arcChain.h" + +//////////////////////////////////////////////////////////////////// +// Class : PGButton +// Description : This is a particular kind of PGItem that is +// specialized to behave like a normal button object. +// It keeps track of its own state, and handles mouse +// events sensibly. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGButton : public PGItem { +PUBLISHED: + PGButton(const string &name = ""); + virtual ~PGButton(); + +public: + PGButton(const PGButton ©); + void operator = (const PGButton ©); + + virtual Node *make_copy() const; + + virtual void enter(); + virtual void exit(); + virtual void button_down(ButtonHandle button); + virtual void button_up(ButtonHandle button, bool is_within); + + virtual void click(); + +PUBLISHED: + enum State { + S_ready = 0, + S_depressed, + S_rollover, + S_inactive + }; + + void setup(const string &label); + INLINE void setup(const ArcChain &ready); + INLINE void setup(const ArcChain &ready, const ArcChain &depressed); + INLINE void setup(const ArcChain &ready, const ArcChain &depressed, + const ArcChain &rollover); + void setup(const ArcChain &ready, const ArcChain &depressed, + const ArcChain &rollover, const ArcChain &inactive); + + INLINE void set_active(bool active); + + INLINE string get_click_event() const; + +private: + bool _button_down; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PGItem::init_type(); + register_type(_type_handle, "PGButton", + PGItem::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "pgButton.I" + +#endif diff --git a/panda/src/pgui/pgFrameStyle.I b/panda/src/pgui/pgFrameStyle.I new file mode 100644 index 0000000000..8b367997e6 --- /dev/null +++ b/panda/src/pgui/pgFrameStyle.I @@ -0,0 +1,150 @@ +// Filename: pgFrameStyle.I +// Created by: drose (03Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGFrameStyle:: +PGFrameStyle() { + _type = T_none; + _color.set(1.0, 1.0, 1.0, 1.0); + _width = 0.01; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGFrameStyle:: +PGFrameStyle(const PGFrameStyle ©) : + _type(copy._type), + _color(copy._color), + _width(copy._width) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +operator = (const PGFrameStyle ©) { + _type = copy._type; + _color = copy._color; + _width = copy._width; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGFrameStyle:: +~PGFrameStyle() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_type +// Access: Published +// Description: Sets the basic type of frame. +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +set_type(PGFrameStyle::Type type) { + _type = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::get_type +// Access: Published +// Description: Returns the basic type of frame. +//////////////////////////////////////////////////////////////////// +INLINE PGFrameStyle::Type PGFrameStyle:: +get_type() const { + return _type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_color +// Access: Published +// Description: Sets the dominant color of the frame. +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +set_color(float r, float g, float b, float a) { + set_color(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_color +// Access: Published +// Description: Sets the dominant color of the frame. +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +set_color(const Colorf &color) { + _color = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_color +// Access: Published +// Description: Returns the dominant color of the frame. +//////////////////////////////////////////////////////////////////// +INLINE const Colorf &PGFrameStyle:: +get_color() const { + return _color; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_width +// Access: Published +// Description: Sets the width parameter, which has meaning only for +// certain frame types. For instance, this is the width +// of the bevel for T_bevel_in or T_bevel_out. The +// units are in screen units. +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +set_width(float width) { + _width = width; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::get_width +// Access: Published +// Description: Returns the width parameter, which has meaning only +// for certain frame types. For instance, this is the +// width of the bevel for T_bevel_in or T_bevel_out. +// The units are in screen units. +//////////////////////////////////////////////////////////////////// +INLINE float PGFrameStyle:: +get_width() const { + return _width; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle ostream output +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ostream & +operator << (ostream &out, const PGFrameStyle &pfs) { + pfs.output(out); + return out; +} diff --git a/panda/src/pgui/pgFrameStyle.cxx b/panda/src/pgui/pgFrameStyle.cxx new file mode 100644 index 0000000000..358927a369 --- /dev/null +++ b/panda/src/pgui/pgFrameStyle.cxx @@ -0,0 +1,133 @@ +// Filename: pgFrameStyle.cxx +// Created by: drose (03Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pgFrameStyle.h" +#include "geomTristrip.h" +#include "geomNode.h" +#include "transparencyProperty.h" +#include "transparencyTransition.h" +#include "renderRelation.h" + +ostream & +operator << (ostream &out, PGFrameStyle::Type type) { + switch (type) { + case PGFrameStyle::T_none: + return out << "none"; + + case PGFrameStyle::T_flat: + return out << "flat"; + + case PGFrameStyle::T_bevel_out: + return out << "bevel_out"; + + case PGFrameStyle::T_bevel_in: + return out << "bevel_in"; + } + + return out << "**unknown(" << (int)type << ")**"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void PGFrameStyle:: +output(ostream &out) const { + out << _type << " color = " << _color << " width = " << _width; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::generate_into +// Access: Public +// Description: Generates geometry representing a frame of the +// indicated size, and parents it to the indicated node, +// with a scene graph sort order of -1. +// +// The return value is the generated arc, if any, or +// NULL if nothing is generated. +//////////////////////////////////////////////////////////////////// +NodeRelation *PGFrameStyle:: +generate_into(Node *node, const LVecBase4f &frame) { + NodeRelation *arc = (NodeRelation *)NULL; + PT(Geom) geom; + + switch (_type) { + case T_none: + return (NodeRelation *)NULL; + + case T_flat: + geom = generate_flat_geom(frame); + break; + + default: + break; + } + + if (geom != (Geom *)NULL) { + // We've got a basic Geom; create a GeomNode for it. + PT(GeomNode) gnode = new GeomNode("frame"); + gnode->add_geom(geom); + arc = new RenderRelation(node, gnode, -1); + } + + if (arc != (NodeRelation *)NULL && _color[3] != 1.0) { + // We've got some alpha on the color; we need transparency. + TransparencyProperty::Mode mode = TransparencyProperty::M_alpha; + TransparencyTransition *tt = new TransparencyTransition(mode); + arc->set_transition(tt); + } + + return arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::generate_flat_geom +// Access: Public +// Description: Generates the Geom object appropriate to a T_flat +// frame. +//////////////////////////////////////////////////////////////////// +PT(Geom) PGFrameStyle:: +generate_flat_geom(const LVecBase4f &frame) { + PT(Geom) geom = new GeomTristrip; + + float left = frame[0]; + float right = frame[1]; + float bottom = frame[2]; + float top = frame[3]; + + PTA_int lengths(0); + lengths.push_back(4); + + PTA_Vertexf verts; + verts.push_back(Vertexf(left, 0.0, top)); + verts.push_back(Vertexf(left, 0.0, bottom)); + verts.push_back(Vertexf(right, 0.0, top)); + verts.push_back(Vertexf(right, 0.0, bottom)); + + geom->set_num_prims(1); + geom->set_lengths(lengths); + + geom->set_coords(verts, G_PER_VERTEX); + + PTA_Colorf colors; + colors.push_back(_color); + geom->set_colors(colors, G_OVERALL); + + return geom; +} diff --git a/panda/src/pgui/pgFrameStyle.h b/panda/src/pgui/pgFrameStyle.h new file mode 100644 index 0000000000..f5aa2d7985 --- /dev/null +++ b/panda/src/pgui/pgFrameStyle.h @@ -0,0 +1,79 @@ +// Filename: pgFrameStyle.h +// Created by: drose (03Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGFRAMESTYLE_H +#define PGFRAMESTYLE_H + +#include "pandabase.h" + +#include "luse.h" +#include "geom.h" +#include "pointerTo.h" + +class NodeRelation; +class Node; + +//////////////////////////////////////////////////////////////////// +// Class : PGFrameStyle +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGFrameStyle { +PUBLISHED: + INLINE PGFrameStyle(); + INLINE PGFrameStyle(const PGFrameStyle ©); + INLINE void operator = (const PGFrameStyle ©); + + INLINE ~PGFrameStyle(); + + enum Type { + T_none, + T_flat, + T_bevel_out, + T_bevel_in + }; + + INLINE void set_type(Type type); + INLINE Type get_type() const; + + INLINE void set_color(float r, float g, float b, float a); + INLINE void set_color(const Colorf &color); + INLINE const Colorf &get_color() const; + + INLINE void set_width(float width); + INLINE float get_width() const; + + void output(ostream &out) const; + +public: + NodeRelation *generate_into(Node *node, const LVecBase4f &frame); + +private: + PT(Geom) generate_flat_geom(const LVecBase4f &frame); + +private: + Type _type; + Colorf _color; + float _width; +}; + +INLINE ostream &operator << (ostream &out, const PGFrameStyle &pfs); +ostream &operator << (ostream &out, PGFrameStyle::Type type); + +#include "pgFrameStyle.I" + +#endif diff --git a/panda/src/pgui/pgItem.I b/panda/src/pgui/pgItem.I new file mode 100644 index 0000000000..6dc7812834 --- /dev/null +++ b/panda/src/pgui/pgItem.I @@ -0,0 +1,233 @@ +// Filename: pgItem.I +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_region +// Access: Public +// Description: Returns the MouseWatcherRegion associated with this +// item. Every PGItem has a MouseWatcherRegion +// associated with it, that is created when the PGItem +// is created; it does not change during the lifetime of +// the PGItem. Even items that do not have a frame have +// an associated MouseWatcherRegion, although it will +// not be used in this case. +//////////////////////////////////////////////////////////////////// +INLINE PGMouseWatcherRegion *PGItem:: +get_region() const { + return _region; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_frame +// Access: Published +// Description: Sets the bounding rectangle of the item, in local +// coordinates. This is the region on screen within +// which the mouse will be considered to be within the +// item. Normally, it should correspond to the bounding +// rectangle of the visible geometry of the item. +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +set_frame(float left, float right, float bottom, float top) { + set_frame(LVecBase4f(left, right, bottom, top)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_frame +// Access: Published +// Description: Sets the bounding rectangle of the item, in local +// coordinates. This is the region on screen within +// which the mouse will be considered to be within the +// item. Normally, it should correspond to the bounding +// rectangle of the visible geometry of the item. +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +set_frame(const LVecBase4f &frame) { + _has_frame = true; + _frame = frame; + mark_frames_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_frame +// Access: Published +// Description: Returns the bounding rectangle of the item. See +// set_frame(). It is an error to call this if +// has_frame() returns false. +//////////////////////////////////////////////////////////////////// +INLINE const LVecBase4f &PGItem:: +get_frame() const { + nassertr(has_frame(), _frame); + return _frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::has_frame +// Access: Published +// Description: Returns true if the item has a bounding rectangle; +// see set_frame(). +//////////////////////////////////////////////////////////////////// +INLINE bool PGItem:: +has_frame() const { + return _has_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::clear_frame +// Access: Published +// Description: Removes the bounding rectangle from the item. It +// will no longer be possible to position the mouse +// within the item; see set_frame(). +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +clear_frame() { + _has_frame = false; + mark_frames_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_state +// Access: Published +// Description: Sets the "state" of this particular PGItem. +// +// The PGItem node will render as if it were the +// subgraph assigned to the corresponding index via +// set_state_def(). +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +set_state(int state) { + _state = state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_state +// Access: Published +// Description: Returns the "state" of this particular PGItem. See +// set_state(). +//////////////////////////////////////////////////////////////////// +INLINE int PGItem:: +get_state() const { + return _state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_active +// Access: Published +// Description: Sets whether the PGItem is active for mouse watching. +// This is not necessarily related to the +// active/inactive appearance of the item, which is +// controlled by set_state(), but it does affect whether +// it responds to mouse events. +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +set_active(bool active) { + _active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_active +// Access: Published +// Description: Returns whether the PGItem is currently active for +// mouse events. See set_active(). +//////////////////////////////////////////////////////////////////// +INLINE bool PGItem:: +get_active() const { + return _active; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_id +// Access: Published +// Description: Returns the unique ID assigned to this PGItem. This +// will be assigned to the region created with the +// MouseWatcher, and will thus be used to generate event +// names. +//////////////////////////////////////////////////////////////////// +INLINE const string &PGItem:: +get_id() const { + return _region->get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_enter_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// item is active and the mouse enters its frame. +//////////////////////////////////////////////////////////////////// +INLINE string PGItem:: +get_enter_event() const { + return "enter-" + get_id(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_exit_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// item is active and the mouse exits its frame. +//////////////////////////////////////////////////////////////////// +INLINE string PGItem:: +get_exit_event() const { + return "exit-" + get_id(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_button_down_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// item is active and a mouse or keyboard button is +// depressed while the mouse is within the frame. +// +// This event will be thrown with one parameter, which +// will be the name of the button that generated the +// event. +//////////////////////////////////////////////////////////////////// +INLINE string PGItem:: +get_button_down_event() const { + return "bdown-" + get_id(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_button_up_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// item is active and a mouse or keyboard button, +// formerly clicked down is within the frame, is +// released. +// +// This event will be thrown with two parameters, the +// first of which will be the name of the button that +// generated the event, and the second of which will be +// a boolean flag indicating true if the mouse was +// within the frame while the button was released. +//////////////////////////////////////////////////////////////////// +INLINE string PGItem:: +get_button_up_event() const { + return "bup-" + get_id(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_text_node +// Access: Published, Static +// Description: Changes the TextNode object that will be used by all +// PGItems to generate default labels given a string. +// This can be loaded with the default font, etc. +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +set_text_node(TextNode *node) { + _text_node = node; +} diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx new file mode 100644 index 0000000000..e5243f3cf7 --- /dev/null +++ b/panda/src/pgui/pgItem.cxx @@ -0,0 +1,396 @@ +// Filename: pgItem.cxx +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pgItem.h" + +#include "namedNode.h" +#include "throw_event.h" +#include "string_utils.h" +#include "arcChain.h" + +TypeHandle PGItem::_type_handle; +PT(TextNode) PGItem::_text_node; + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +PGItem:: +PGItem(const string &name) : NamedNode(name) +{ + _has_frame = false; + _frame.set(0, 0, 0, 0); + _region = new PGMouseWatcherRegion(this); + _state = 0; + _active = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGItem:: +~PGItem() { + nassertv(_region->_item == this); + _region->_item = (PGItem *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PGItem:: +PGItem(const PGItem ©) : + NamedNode(copy), + _has_frame(copy._has_frame), + _frame(copy._frame), + _state(copy._state), + _active(copy._active) +{ + _region = new PGMouseWatcherRegion(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PGItem:: +operator = (const PGItem ©) { + NamedNode::operator = (copy); + _has_frame = copy._has_frame; + _frame = copy._frame; + _state = copy._state; + _active = copy._active; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *PGItem:: +make_copy() const { + return new PGItem(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::activate_region +// Access: Public, Virtual +// Description: Applies the indicated scene graph transform and order +// as determined by the traversal from PGTop. +//////////////////////////////////////////////////////////////////// +void PGItem:: +activate_region(const LMatrix4f &transform, int sort) { + LPoint3f ll(_frame[0], 0.0, _frame[2]); + LPoint3f ur(_frame[1], 0.0, _frame[3]); + ll = ll * transform; + ur = ur * transform; + _region->set_frame(ll[0], ur[0], ll[2], ur[2]); + _region->set_sort(sort); + _region->set_active(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::enter +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse enters the region. +//////////////////////////////////////////////////////////////////// +void PGItem:: +enter() { + throw_event(get_enter_event()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::exit +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse exits the region. +//////////////////////////////////////////////////////////////////// +void PGItem:: +exit() { + throw_event(get_exit_event()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::button_down +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button is depressed while the mouse +// is within the region. +//////////////////////////////////////////////////////////////////// +void PGItem:: +button_down(ButtonHandle button) { + throw_event(get_button_down_event(), button.get_name()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::button_up +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button previously depressed with +// button_down() is release. The bool is_within flag is +// true if the button was released while the mouse was +// still within the region, or false if it was released +// outside the region. +//////////////////////////////////////////////////////////////////// +void PGItem:: +button_up(ButtonHandle button, bool is_within) { + throw_event(get_button_up_event(), + EventParameter(button.get_name()), + EventParameter(is_within)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_num_state_defs +// Access: Published +// Description: Returns one more than the highest-numbered state def +// that was ever assigned to the PGItem. The complete +// set of state defs assigned may then be retrieved by +// indexing from 0 to (get_num_state_defs() - 1). +// +// This is only an upper limit on the actual number of +// state defs, since there may be holes in the list. +//////////////////////////////////////////////////////////////////// +int PGItem:: +get_num_state_defs() const { + return _state_defs.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::has_state_def +// Access: Published +// Description: Returns true if get_state_def() has ever been called +// for the indicated state (thus defining a render +// subgraph for this state index), false otherwise. +//////////////////////////////////////////////////////////////////// +bool PGItem:: +has_state_def(int state) const { + if (state < 0 || state >= (int)_state_defs.size()) { + return false; + } + return (_state_defs[state]._node != (Node *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::clear_state_def +// Access: Published +// Description: Resets the NodePath assigned to the indicated state +// to its initial default, with only a frame +// representation if appropriate. +//////////////////////////////////////////////////////////////////// +void PGItem:: +clear_state_def(int state) { + if (state < 0 || state >= (int)_state_defs.size()) { + return; + } + + Node *def = _state_defs[state]._node; + if (def != (Node *)NULL) { + // Remove all the children from this node. + int num_children = def->get_num_children(RenderRelation::get_class_type()); + while (num_children > 0) { + nassertv(num_children == def->get_num_children(RenderRelation::get_class_type())); + NodeRelation *arc = + def->get_child(RenderRelation::get_class_type(), num_children - 1); + remove_arc(arc); + num_children--; + } + } + + _state_defs[state]._frame_arc = (NodeRelation *)NULL; + _state_defs[state]._frame_stale = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_state_def +// Access: Published +// Description: Returns the Node that is the root of the subgraph +// that will be drawn when the PGItem is in the +// indicated state. The first time this is called for a +// particular state index, it may create the Node. +//////////////////////////////////////////////////////////////////// +Node *PGItem:: +get_state_def(int state) { + nassertr(state < 1000, (Node *)NULL); // Sanity check. + slot_state_def(state); + + if (_state_defs[state]._node == (Node *)NULL) { + // Create a new node. + _state_defs[state]._node = new NamedNode("state_" + format_string(state)); + _state_defs[state]._frame_stale = true; + } + + if (_state_defs[state]._frame_stale) { + update_frame(state); + } + + return _state_defs[state]._node; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::instance_to_state_def +// Access: Published +// Description: Parents an instance of the bottom node of the +// indicated ArcChain (normally a NodePath) to the +// indicated state index. +//////////////////////////////////////////////////////////////////// +void PGItem:: +instance_to_state_def(int state, const ArcChain &chain) { + if (chain.empty()) { + // If the chain is empty, quietly do nothing. + return; + } + + NodeRelation *new_arc = + new RenderRelation(get_state_def(state), chain.node()); + + if (chain.has_arcs()) { + // If the chain has an arc, copy the transitions from it. + new_arc->copy_transitions_from(chain.arc()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_frame_style +// Access: Published +// Description: Returns the kind of frame that will be drawn behind +// the item when it is in the indicated state. +//////////////////////////////////////////////////////////////////// +PGFrameStyle PGItem:: +get_frame_style(int state) { + if (state < 0 || state >= (int)_state_defs.size()) { + return PGFrameStyle(); + } + return _state_defs[state]._frame_style; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_frame_style +// Access: Published +// Description: Changes the kind of frame that will be drawn behind +// the item when it is in the indicated state. +//////////////////////////////////////////////////////////////////// +void PGItem:: +set_frame_style(int state, const PGFrameStyle &style) { + // Get the state def node, mainly to ensure that this state is + // slotted and listed as having been defined. + Node *def = get_state_def(state); + nassertv(def != (Node *)NULL); + + _state_defs[state]._frame_style = style; + _state_defs[state]._frame_stale = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_text_node +// Access: Published, Static +// Description: Returns the TextNode object that will be used by all +// PGItems to generate default labels given a string. +// This can be loaded with the default font, etc. +//////////////////////////////////////////////////////////////////// +TextNode *PGItem:: +get_text_node() { + if (_text_node == (TextNode *)NULL) { + _text_node = new TextNode("pguiText"); + _text_node->freeze(); + _text_node->set_text_color(0.0, 0.0, 0.0, 1.0); + _text_node->set_align(TM_ALIGN_CENTER); + } + return _text_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::slot_state_def +// Access: Private +// Description: Ensures there is a slot in the array for the given +// state definition. +//////////////////////////////////////////////////////////////////// +void PGItem:: +slot_state_def(int state) { + while (state >= (int)_state_defs.size()) { + StateDef def; + def._frame_stale = true; + _state_defs.push_back(def); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::update_frame +// Access: Private +// Description: Generates a new instance of the frame geometry for +// the indicated state. +//////////////////////////////////////////////////////////////////// +void PGItem:: +update_frame(int state) { + // First, remove the old frame geometry, if any. + if (state >= 0 && state < (int)_state_defs.size()) { + NodeRelation *old_arc = _state_defs[state]._frame_arc; + if (old_arc != (NodeRelation *)NULL) { + if (old_arc->is_attached()) { + remove_arc(old_arc); + _state_defs[state]._frame_arc = (NodeRelation *)NULL; + } + } + } + + // We must turn off the stale flag first, before we call + // get_state_def(), to prevent get_state_def() from being a + // recursive call. + _state_defs[state]._frame_stale = false; + + // Now create new frame geometry. + if (has_frame()) { + Node *node = get_state_def(state); + nassertv(node != (Node *)NULL); + + _state_defs[state]._frame_arc = + _state_defs[state]._frame_style.generate_into(node, _frame); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::mark_frames_stale +// Access: Private +// Description: Marks all the frames in all states stale, so that +// they will be regenerated the next time each state is +// requested. +//////////////////////////////////////////////////////////////////// +void PGItem:: +mark_frames_stale() { + StateDefs::iterator di; + for (di = _state_defs.begin(); di != _state_defs.end(); ++di) { + // Remove the old frame, if any. + NodeRelation *old_arc = (*di)._frame_arc; + if (old_arc != (NodeRelation *)NULL) { + if (old_arc->is_attached()) { + remove_arc(old_arc); + (*di)._frame_arc = (NodeRelation *)NULL; + } + } + + (*di)._frame_stale = true; + } +} diff --git a/panda/src/pgui/pgItem.h b/panda/src/pgui/pgItem.h new file mode 100644 index 0000000000..9c112d2a45 --- /dev/null +++ b/panda/src/pgui/pgItem.h @@ -0,0 +1,145 @@ +// Filename: pgItem.h +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGITEM_H +#define PGITEM_H + +#include "pandabase.h" + +#include "pgMouseWatcherRegion.h" +#include "pgFrameStyle.h" + +#include "namedNode.h" +#include "nodeRelation.h" +#include "luse.h" +#include "pointerTo.h" +#include "pt_Node.h" +#include "pt_NodeRelation.h" +#include "textNode.h" + +class ArcChain; + +//////////////////////////////////////////////////////////////////// +// Class : PGItem +// Description : This is the base class for all the various kinds of +// gui widget objects. +// +// It is a Node which corresponds to a rectangular +// region on the screen, and it may have any number of +// "state" subgraphs, one of which is rendered at any +// given time according to its current state. +// +// The PGItem node must be parented to the scene graph +// somewhere beneath a PGTop node in order for this +// behavior to work. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGItem : public NamedNode { +PUBLISHED: + PGItem(const string &name = ""); + virtual ~PGItem(); + +public: + PGItem(const PGItem ©); + void operator = (const PGItem ©); + + virtual Node *make_copy() const; + + void activate_region(const LMatrix4f &transform, int sort); + INLINE PGMouseWatcherRegion *get_region() const; + + virtual void enter(); + virtual void exit(); + virtual void button_down(ButtonHandle button); + virtual void button_up(ButtonHandle button, bool is_within); + +PUBLISHED: + INLINE void set_frame(float left, float right, float bottom, float top); + INLINE void set_frame(const LVecBase4f &frame); + INLINE const LVecBase4f &get_frame() const; + INLINE bool has_frame() const; + INLINE void clear_frame(); + + INLINE void set_state(int state); + INLINE int get_state() const; + + INLINE void set_active(bool active); + INLINE bool get_active() const; + + int get_num_state_defs() const; + void clear_state_def(int state); + bool has_state_def(int state) const; + Node *get_state_def(int state); + void instance_to_state_def(int state, const ArcChain &chain); + + PGFrameStyle get_frame_style(int state); + void set_frame_style(int state, const PGFrameStyle &style); + + INLINE const string &get_id() const; + INLINE string get_enter_event() const; + INLINE string get_exit_event() const; + INLINE string get_button_down_event() const; + INLINE string get_button_up_event() const; + + static TextNode *get_text_node(); + INLINE static void set_text_node(TextNode *node); + +private: + void slot_state_def(int state); + void update_frame(int state); + void mark_frames_stale(); + + bool _has_frame; + LVecBase4f _frame; + int _state; + bool _active; + + PT(PGMouseWatcherRegion) _region; + + class StateDef { + public: + PT_Node _node; + PGFrameStyle _frame_style; + PT_NodeRelation _frame_arc; + bool _frame_stale; + }; + typedef pvector StateDefs; + StateDefs _state_defs; + + static PT(TextNode) _text_node; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NamedNode::init_type(); + register_type(_type_handle, "PGItem", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "pgItem.I" + +#endif diff --git a/panda/src/pgui/pgMouseWatcherRegion.I b/panda/src/pgui/pgMouseWatcherRegion.I new file mode 100644 index 0000000000..32f1822089 --- /dev/null +++ b/panda/src/pgui/pgMouseWatcherRegion.I @@ -0,0 +1,18 @@ +// Filename: pgMouseWatcherRegion.I +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/pgui/pgMouseWatcherRegion.cxx b/panda/src/pgui/pgMouseWatcherRegion.cxx new file mode 100644 index 0000000000..6648246102 --- /dev/null +++ b/panda/src/pgui/pgMouseWatcherRegion.cxx @@ -0,0 +1,106 @@ +// Filename: pgMouseWatcherRegion.cxx +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pgMouseWatcherRegion.h" +#include "pgItem.h" + +#include "string_utils.h" + +TypeHandle PGMouseWatcherRegion::_type_handle; +int PGMouseWatcherRegion::_next_index = 0; + +//////////////////////////////////////////////////////////////////// +// Function: PGMouseWatcherRegion::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PGMouseWatcherRegion:: +PGMouseWatcherRegion(PGItem *item) : +#ifndef CPPPARSER + MouseWatcherRegion("pg" + format_string(_next_index++), 0, 0, 0, 0), +#endif + _item(item) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PGMouseWatcherRegion::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGMouseWatcherRegion:: +~PGMouseWatcherRegion() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: PGMouseWatcherRegion::enter +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse enters the region. +//////////////////////////////////////////////////////////////////// +void PGMouseWatcherRegion:: +enter() { + if (_item != (PGItem *)NULL) { + _item->enter(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGMouseWatcherRegion::exit +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse exits the region. +//////////////////////////////////////////////////////////////////// +void PGMouseWatcherRegion:: +exit() { + if (_item != (PGItem *)NULL) { + _item->exit(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGMouseWatcherRegion::button_down +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button is depressed while the mouse +// is within the region. +//////////////////////////////////////////////////////////////////// +void PGMouseWatcherRegion:: +button_down(ButtonHandle button) { + if (_item != (PGItem *)NULL) { + _item->button_down(button); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGMouseWatcherRegion::button_up +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button previously depressed with +// button_down() is release. The bool is_within flag is +// true if the button was released while the mouse was +// still within the region, or false if it was released +// outside the region. +//////////////////////////////////////////////////////////////////// +void PGMouseWatcherRegion:: +button_up(ButtonHandle button, bool is_within) { + if (_item != (PGItem *)NULL) { + _item->button_up(button, is_within); + } +} diff --git a/panda/src/pgui/pgMouseWatcherRegion.h b/panda/src/pgui/pgMouseWatcherRegion.h new file mode 100644 index 0000000000..1c71563066 --- /dev/null +++ b/panda/src/pgui/pgMouseWatcherRegion.h @@ -0,0 +1,71 @@ +// Filename: pgMouseWatcherRegion.h +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGMOUSEWATCHERREGION_H +#define PGMOUSEWATCHERREGION_H + +#include "pandabase.h" + +#include "mouseWatcherRegion.h" + +class PGItem; + +//////////////////////////////////////////////////////////////////// +// Class : PGMouseWatcherRegion +// Description : This is a specialization on MouseWatcherRegion, to +// add a bit more fields that are relevant to the PG +// system. Each PGItem corresponds to exactly one +// PGMouseWatcherRegion. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGMouseWatcherRegion : public MouseWatcherRegion { +public: + PGMouseWatcherRegion(PGItem *item); + virtual ~PGMouseWatcherRegion(); + + virtual void enter(); + virtual void exit(); + virtual void button_down(ButtonHandle button); + virtual void button_up(ButtonHandle button, bool is_within); + +private: + PGItem *_item; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MouseWatcherRegion::init_type(); + register_type(_type_handle, "PGMouseWatcherRegion", + MouseWatcherRegion::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + static int _next_index; + + friend class PGItem; +}; + +#include "pgMouseWatcherRegion.I" + +#endif diff --git a/panda/src/pgui/pgTop.I b/panda/src/pgui/pgTop.I new file mode 100644 index 0000000000..b68ad8a918 --- /dev/null +++ b/panda/src/pgui/pgTop.I @@ -0,0 +1,56 @@ +// Filename: pgTop.I +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGTop:: +PGTop(const PGTop ©) : + NamedNode(copy), + _watcher(copy._watcher) +{ + _gsg = (GraphicsStateGuardian *)NULL; + _trav = (RenderTraverser *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PGTop:: +operator = (const PGTop ©) { + NamedNode::operator = (copy); + _watcher = copy._watcher; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::get_mouse_watcher +// Access: Published +// Description: Returns the MouseWatcher pointer that the PGTop object +// registers its PG items with, or NULL if the +// MouseWatcher has not yet been set. +//////////////////////////////////////////////////////////////////// +INLINE MouseWatcher *PGTop:: +get_mouse_watcher() const { + return _watcher; +} diff --git a/panda/src/pgui/pgTop.cxx b/panda/src/pgui/pgTop.cxx new file mode 100644 index 0000000000..54c100aee8 --- /dev/null +++ b/panda/src/pgui/pgTop.cxx @@ -0,0 +1,256 @@ +// Filename: pgTop.cxx +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pgTop.h" +#include "pgItem.h" + +#include "arcChain.h" +#include "graphicsStateGuardian.h" +#include "config_sgraphutil.h" +#include "renderRelation.h" +#include "geomNode.h" +#include "allTransitionsWrapper.h" +#include "allAttributesWrapper.h" +#include "wrt.h" +#include "switchNode.h" +#include "transformTransition.h" +#include "nodeTransitionWrapper.h" +#include "directRenderTraverser.h" +#include "omniBoundingVolume.h" +#include "pruneTransition.h" + +TypeHandle PGTop::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +PGTop:: +PGTop(const string &name) : NamedNode(name) +{ + _gsg = (GraphicsStateGuardian *)NULL; + _trav = (RenderTraverser *)NULL; + + // A PGTop node normally has an infinite bounding volume. Screw + // culling. + set_bound(OmniBoundingVolume()); + set_final(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGTop:: +~PGTop() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *PGTop:: +make_copy() const { + return new PGTop(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::sub_render +// Access: Public, Virtual +// Description: Gets called during the draw traversal to render this +// node and all nodes below it. In the case of the +// PGTop node, this uses a depth-first left-to-right +// traversal to render all of the GeomNodes and PGItems +// in the scene graph order. +//////////////////////////////////////////////////////////////////// +bool PGTop:: +sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &, + RenderTraverser *trav) { + _attrib = attrib; + _gsg = trav->get_gsg(); + const ArcChain &chain = trav->get_arc_chain(); + + // Empty our set of regions in preparation for re-adding whichever + // ones we encounter in the traversal that are current. + clear_regions(); + _sort_index = 0; + + // Start the traversal below the current node. + const DownRelationPointers &drp = + find_connection(RenderRelation::get_class_type()).get_down(); + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + if (!arc->has_transition(PruneTransition::get_class_type())) { + ArcChain next_chain(chain); + next_chain.push_back(arc); + r_traverse(arc->get_child(), next_chain); + } + } + + // We don't need the normal render traverser to do anything else; + // we've done it all. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool PGTop:: +has_sub_render() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::set_mouse_watcher +// Access: Published +// Description: Sets the MouseWatcher pointer that the PGTop object +// registers its PG items with. This must be set before +// the PG items are active. +//////////////////////////////////////////////////////////////////// +void PGTop:: +set_mouse_watcher(MouseWatcher *watcher) { + if (_watcher != (MouseWatcher *)NULL) { + _watcher->remove_group(this); + } + _watcher = watcher; + if (_watcher != (MouseWatcher *)NULL) { + _watcher->add_group(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGTop::r_traverse +// Access: Private +// Description: Handles the main recursive traversal of the scene +// graph for nodes below the PGTop node. This +// implements a depth-first traversal. +//////////////////////////////////////////////////////////////////// +void PGTop:: +r_traverse(Node *node, const ArcChain &chain) { + if (implicit_app_traversal) { + node->app_traverse(); + } + node->draw_traverse(); + _gsg->_nodes_pcollector.add_level(1); + + + if (node->is_of_type(PGItem::get_class_type())) { + PGItem *pgi = DCAST(PGItem, node); + + if (pgi->has_frame() && pgi->get_active()) { + // The item has a frame, so we want to generate a region for it + // and update the MouseWatcher. + + // Get the complete net transform to the PGItem from the top. + LMatrix4f mat; + + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(pgi, chain.begin(), chain.end(), (Node *)NULL, + ntw, RenderRelation::get_class_type()); + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + mat = LMatrix4f::ident_mat(); + return; + } + + mat = tt->get_matrix(); + + // Now apply this transform to the item's frame. + pgi->activate_region(mat, _sort_index); + _sort_index++; + + add_region(pgi->get_region()); + } + + if (pgi->has_state_def(pgi->get_state())) { + // This item has a current state definition that we should use + // to render the item. + Node *def = pgi->get_state_def(pgi->get_state()); + + // Get the net transitions to the PGItem. + AllTransitionsWrapper complete_trans; + wrt(pgi, chain.begin(), chain.end(), this, + complete_trans, RenderRelation::get_class_type()); + + // We'll use a normal DirectRenderTraverser to do the rendering + // of the subgraph. + DirectRenderTraverser drt(_gsg, RenderRelation::get_class_type()); + drt.traverse(def, _attrib, complete_trans); + } + + } else if (node->is_of_type(GeomNode::get_class_type())) { + _gsg->_geom_nodes_pcollector.add_level(1); + GeomNode *geom = DCAST(GeomNode, node); + + // Get the complete state of the GeomNode. + AllTransitionsWrapper complete_trans; + wrt(geom, chain.begin(), chain.end(), this, + complete_trans, RenderRelation::get_class_type()); + AllAttributesWrapper complete_state; + complete_state.apply_from(_attrib, complete_trans); + _gsg->set_state(complete_state.get_attributes(), true); + + // Finally, draw the Geom. + _gsg->prepare_display_region(); + geom->draw(_gsg); + } + + + // Continue the traversal. + const DownRelationPointers &drp = + node->find_connection(RenderRelation::get_class_type()).get_down(); + + if (node->is_of_type(SwitchNode::get_class_type())) { + SwitchNode *swnode = DCAST(SwitchNode, node); + swnode->compute_switch(_trav); + size_t i = 0; + for (i = 0; i < drp.size(); i++) { + if (swnode->is_child_visible(RenderRelation::get_class_type(), i)) { + NodeRelation *arc = drp[i]; + if (!arc->has_transition(PruneTransition::get_class_type())) { + ArcChain next_chain(chain); + next_chain.push_back(arc); + r_traverse(arc->get_child(), next_chain); + } + } + } + } else { + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + if (!arc->has_transition(PruneTransition::get_class_type())) { + ArcChain next_chain(chain); + next_chain.push_back(arc); + r_traverse(arc->get_child(), next_chain); + } + } + } +} diff --git a/panda/src/pgui/pgTop.h b/panda/src/pgui/pgTop.h new file mode 100644 index 0000000000..eac01addf6 --- /dev/null +++ b/panda/src/pgui/pgTop.h @@ -0,0 +1,99 @@ +// Filename: pgTop.h +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGTOP_H +#define PGTOP_H + +#include "pandabase.h" + +#include "namedNode.h" +#include "mouseWatcherGroup.h" +#include "mouseWatcher.h" +#include "pointerTo.h" +#include "allAttributesWrapper.h" + +class GraphicsStateGuardian; +class RenderTraverser; +class ArcChain; + +//////////////////////////////////////////////////////////////////// +// Class : PGTop +// Description : The "top" node of the new Panda GUI system. This +// node must be parented to the 2-d scene graph, and all +// PG objects should be parented to this node or +// somewhere below it. PG objects not parented within +// this hierarchy will not even be visible. +// +// This node begins the special traversal of the PG +// objects that registers each node within the +// MouseWatcher and forces everything to render in a +// depth-first, left-to-right order, appropriate for 2-d +// objects. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGTop : public NamedNode, public MouseWatcherGroup { +PUBLISHED: + PGTop(const string &name = ""); + virtual ~PGTop(); + +public: + INLINE PGTop(const PGTop ©); + INLINE void operator = (const PGTop ©); + + virtual Node *make_copy() const; + + virtual bool sub_render(const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + RenderTraverser *trav); + virtual bool has_sub_render() const; + +PUBLISHED: + void set_mouse_watcher(MouseWatcher *watcher); + INLINE MouseWatcher *get_mouse_watcher() const; + +private: + void r_traverse(Node *node, const ArcChain &chain); + + PT(MouseWatcher) _watcher; + GraphicsStateGuardian *_gsg; + RenderTraverser *_trav; + AllAttributesWrapper _attrib; + int _sort_index; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NamedNode::init_type(); + MouseWatcherGroup::init_type(); + register_type(_type_handle, "PGTop", + NamedNode::get_class_type(), + MouseWatcherGroup::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "pgTop.I" + +#endif diff --git a/panda/src/sgmanip/nodePath.I b/panda/src/sgmanip/nodePath.I index 651cb446e1..917eb2cd2a 100644 --- a/panda/src/sgmanip/nodePath.I +++ b/panda/src/sgmanip/nodePath.I @@ -51,6 +51,26 @@ NodePath(Node *top_node, TypeHandle graph_type) : { } +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Constructor +// Access: Public +// Description: Creates a NodePath that contains one arc, and the two +// Nodes connected to that arc. +// +// If the NodeRelation pointer is NULL, this quietly +// creates an empty NodePath. +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(NodeRelation *arc) : + _graph_type(RenderRelation::get_class_type()), + _error_type(ET_ok) +{ + if (arc != (NodeRelation *)NULL) { + _graph_type = arc->get_type(); + extend_by(arc); + } +} + //////////////////////////////////////////////////////////////////// // Function: NodePath::Constructor // Access: Public @@ -183,12 +203,9 @@ compare_to(const NodePath &other) const { // Access: Public // Description: Changes the type of graph that the NodePath will // search for. By default, this is RenderRelation. -// This may only be called when the NodePath contains no -// arcs (e.g. is_empty() || is_singleton()). //////////////////////////////////////////////////////////////////// INLINE void NodePath:: set_graph_type(TypeHandle graph_type) { - nassertv(_head == (ArcComponent *)NULL); _graph_type = graph_type; } @@ -242,17 +259,6 @@ get_max_search_depth() { return _max_search_depth; } -//////////////////////////////////////////////////////////////////// -// Function: NodePath::is_empty -// Access: Public -// Description: Returns true if the NodePath contains no nodes and no -// arcs. -//////////////////////////////////////////////////////////////////// -INLINE bool NodePath:: -is_empty() const { - return !ArcChain::has_node(); -} - //////////////////////////////////////////////////////////////////// // Function: NodePath::get_error_type // Access: Public @@ -264,74 +270,6 @@ get_error_type() const { return _error_type; } -//////////////////////////////////////////////////////////////////// -// Function: NodePath::is_singleton -// Access: Public -// Description: Returns true if the NodePath contains exactly one -// node, and no arcs. -//////////////////////////////////////////////////////////////////// -INLINE bool NodePath:: -is_singleton() const { - return ArcChain::has_node() && !ArcChain::has_arcs(); -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::has_arcs -// Access: Public -// Description: Returns true if the NodePath contains at least one -// arc, and therefore at least two nodes. This is the -// same thing as asking get_num_arcs() > 0, but is -// easier to compute. -//////////////////////////////////////////////////////////////////// -INLINE bool NodePath:: -has_arcs() const { - return ArcChain::has_arcs(); -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_num_arcs -// Access: Public -// Description: Returns the number of arcs in the path. -//////////////////////////////////////////////////////////////////// -INLINE int NodePath:: -get_num_arcs() const { - if (!has_arcs()) { - return 0; - } - return get_num_nodes() - 1; -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::node -// Access: Public -// Description: Returns the bottom node of the path, or NULL if the -// path is empty. -//////////////////////////////////////////////////////////////////// -INLINE Node *NodePath:: -node() const { - nassertr(_error_type == ET_ok, (Node *)NULL); - if (is_empty()) { - return (Node *)NULL; - } - return _head->get_node(); -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::arc -// Access: Public -// Description: Returns the bottom arc of the path, or NULL if the -// path is empty or is a singleton. -//////////////////////////////////////////////////////////////////// -INLINE NodeRelation *NodePath:: -arc() const { - nassertr(_error_type == ET_ok, (NodeRelation *)NULL); - if (!has_arcs()) { - // A singleton or empty list. - return (NodeRelation *)NULL; - } - return _head->get_arc(); -} - //////////////////////////////////////////////////////////////////// // Function: NodePath::get_num_children // Access: Public @@ -449,7 +387,11 @@ attach_new_node(const string &name, int sort) const { //////////////////////////////////////////////////////////////////// INLINE void NodePath:: output(ostream &out) const { - out << as_string(0); + if (_error_type == ET_ok && is_empty()) { + out << "**empty**"; + } else { + out << as_string(0); + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/sgmanip/nodePath.cxx b/panda/src/sgmanip/nodePath.cxx index 309d946db1..8bb0f8f446 100644 --- a/panda/src/sgmanip/nodePath.cxx +++ b/panda/src/sgmanip/nodePath.cxx @@ -150,16 +150,6 @@ extend_by(NodeRelation *darc) { return false; } - if (darc->get_type() != get_graph_type() && - darc->get_type() != NodeRelation::get_stashed_type()) { - if (sgmanip_cat.is_debug()) { - sgmanip_cat.debug() - << "Cannot extend " << *this << " by arc " << *darc - << "; wrong graph type.\n"; - } - return false; - } - _head = new ArcComponent(darc, _head); return true; @@ -418,95 +408,6 @@ find_singular_transform() const { return NodePath::not_found(); } -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_node -// Access: Public -// Description: Returns the nth node of the path, where 0 is the -// bottom node and get_num_nodes() - 1 is the top node. -// This requires iterating through the path. -//////////////////////////////////////////////////////////////////// -Node *NodePath:: -get_node(int index) const { - nassertr(index >= 0 && index < get_num_nodes(), NULL); - - ArcComponent *comp = _head; - while (index > 0) { - // If this assertion fails, the index was out of range. - nassertr(comp != (ArcComponent *)NULL, NULL); - comp = comp->get_next(); - index--; - } - - // If this assertion fails, the index was out of range. - nassertr(comp != (ArcComponent *)NULL, NULL); - return comp->get_node(); -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_num_nodes -// Access: Public -// Description: Returns the number of nodes in the path. This is -// always one more than the number of arcs (except for -// a completely empty path). -//////////////////////////////////////////////////////////////////// -int NodePath:: -get_num_nodes() const { - int num = 0; - ArcComponent *comp = _head; - while (comp != (ArcComponent *)NULL) { - num++; - comp = comp->get_next(); - } - return num; -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_arc -// Access: Public -// Description: Returns the nth arc of the path, where 0 is the arc -// above the bottom node node and get_num_arcs() - 1 is -// the arc below the top node. This requires iterating -// through the path. -//////////////////////////////////////////////////////////////////// -NodeRelation *NodePath:: -get_arc(int index) const { - nassertr(index >= 0 && index < get_num_arcs(), NULL); - - ArcComponent *comp = _head; - while (index > 0) { - // If this assertion fails, the index was out of range. - nassertr(comp != (ArcComponent *)NULL, NULL); - comp = comp->get_next(); - index--; - } - - // If either assertion fails, the index was out of range. - nassertr(comp != (ArcComponent *)NULL, NULL); - nassertr(comp->has_arc(), NULL); - return comp->get_arc(); -} - -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_top_node -// Access: Public -// Description: Returns the top node of the path, or NULL if the path -// is empty. This requires iterating through the path. -//////////////////////////////////////////////////////////////////// -Node *NodePath:: -get_top_node() const { - if (is_empty()) { - return (Node *)NULL; - } - - ArcComponent *comp = _head; - while (!comp->is_top_node()) { - comp = comp->get_next(); - nassertr(comp != (ArcComponent *)NULL, NULL); - } - - return comp->get_node(); -} - //////////////////////////////////////////////////////////////////// // Function: NodePath::share_with // Access: Public diff --git a/panda/src/sgmanip/nodePath.h b/panda/src/sgmanip/nodePath.h index 634a1a58e8..3103051187 100644 --- a/panda/src/sgmanip/nodePath.h +++ b/panda/src/sgmanip/nodePath.h @@ -148,6 +148,7 @@ PUBLISHED: INLINE NodePath(TypeHandle graph_type = RenderRelation::get_class_type()); INLINE NodePath(Node *top_node, TypeHandle graph_type = RenderRelation::get_class_type()); + INLINE NodePath(NodeRelation *arc); INLINE NodePath(const ArcChain &chain, TypeHandle graph_type); INLINE NodePath(const NodePath ©); @@ -183,19 +184,9 @@ PUBLISHED: // Methods to query a NodePath's contents. - INLINE bool is_empty() const; INLINE ErrorType get_error_type() const; - INLINE bool is_singleton() const; - INLINE bool has_arcs() const; - int get_num_nodes() const; - Node *get_node(int index) const; - INLINE int get_num_arcs() const; - NodeRelation *get_arc(int index) const; - - Node *get_top_node() const; - INLINE Node *node() const; - INLINE NodeRelation *arc() const; + // Most of these methods are inherited from ArcChain. // Methods to manage the disconnected NodePaths that can result if diff --git a/panda/src/sgraphutil/frustumCullTraverser.I b/panda/src/sgraphutil/frustumCullTraverser.I index 769f1f23a4..c3b3e692ea 100644 --- a/panda/src/sgraphutil/frustumCullTraverser.I +++ b/panda/src/sgraphutil/frustumCullTraverser.I @@ -74,7 +74,7 @@ FrustumCullTraverser(ArcChain &arc_chain, Node *root, } } - bool needs_top_node = !_arc_chain.has_node(); + bool needs_top_node = _arc_chain.is_empty(); if (needs_top_node) { // If the ArcChain supplied in is initially empty, put the root // node in it. diff --git a/panda/src/tform/Sources.pp b/panda/src/tform/Sources.pp index 91a9e9ff38..cc2157c7c0 100644 --- a/panda/src/tform/Sources.pp +++ b/panda/src/tform/Sources.pp @@ -10,15 +10,17 @@ #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define SOURCES \ - buttonThrower.h config_tform.h dataValve.I dataValve.h \ - driveInterface.I driveInterface.h mouseWatcher.I \ - mouseWatcher.h mouseWatcherRegion.I mouseWatcherRegion.h \ - planarSlider.h trackball.h transform2sg.h + buttonThrower.h config_tform.h dataValve.I dataValve.h \ + driveInterface.I driveInterface.h mouseWatcher.I \ + mouseWatcher.h mouseWatcherGroup.h \ + mouseWatcherRegion.I mouseWatcherRegion.h \ + planarSlider.h trackball.h transform2sg.h #define INCLUDED_SOURCES \ - buttonThrower.cxx config_tform.cxx dataValve.cxx \ - driveInterface.cxx mouseWatcher.cxx mouseWatcherRegion.cxx \ - planarSlider.cxx trackball.cxx transform2sg.cxx + buttonThrower.cxx config_tform.cxx dataValve.cxx \ + driveInterface.cxx mouseWatcher.cxx mouseWatcherGroup.cxx \ + mouseWatcherRegion.cxx \ + planarSlider.cxx trackball.cxx transform2sg.cxx #define INSTALL_HEADERS \ buttonThrower.h dataValve.I dataValve.h \ diff --git a/panda/src/tform/config_tform.cxx b/panda/src/tform/config_tform.cxx index 7a1cffa777..e0be47a881 100644 --- a/panda/src/tform/config_tform.cxx +++ b/panda/src/tform/config_tform.cxx @@ -22,6 +22,7 @@ #include "driveInterface.h" #include "buttonThrower.h" #include "mouseWatcher.h" +#include "mouseWatcherGroup.h" #include "mouseWatcherRegion.h" #include "planarSlider.h" #include "trackball.h" @@ -49,6 +50,7 @@ ConfigureFn(config_tform) { DriveInterface::init_type(); ButtonThrower::init_type(); MouseWatcher::init_type(); + MouseWatcherGroup::init_type(); MouseWatcherRegion::init_type(); PlanarSlider::init_type(); Trackball::init_type(); diff --git a/panda/src/tform/mouseWatcher.I b/panda/src/tform/mouseWatcher.I index 295fc2239b..47c3105df8 100644 --- a/panda/src/tform/mouseWatcher.I +++ b/panda/src/tform/mouseWatcher.I @@ -296,7 +296,7 @@ set_extra_handler(EventHandler *eh) { // can be registered with a mouseWatcher so that events // can be dealt with much sooner. //////////////////////////////////////////////////////////////////// -INLINE EventHandler* MouseWatcher:: +INLINE EventHandler *MouseWatcher:: get_extra_handler(void) const { return _eh; } diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index 314d7a6eaa..1636a59519 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -59,34 +59,10 @@ MouseWatcher:: ~MouseWatcher() { } -//////////////////////////////////////////////////////////////////// -// Function: MouseWatcher::add_region -// Access: Published -// Description: Adds the indicated region to the set of regions that -// are to be watched. Returns true if it was -// successfully added, or false if it was already on the -// list. -//////////////////////////////////////////////////////////////////// -bool MouseWatcher:: -add_region(MouseWatcherRegion *region) { - return _regions.insert(region).second; -} - -//////////////////////////////////////////////////////////////////// -// Function: MouseWatcher::has_region -// Access: Published -// Description: Returns true if the indicated region has already been -// added to the MouseWatcher, false otherwise. -//////////////////////////////////////////////////////////////////// -bool MouseWatcher:: -has_region(MouseWatcherRegion *region) const { - return _regions.count(region) != 0; -} - //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::remove_region // Access: Published -// Description: Removes the indicated region from the Watcher. +// Description: Removes the indicated region from the group. // Returns true if it was successfully removed, or false // if it wasn't there in the first place. //////////////////////////////////////////////////////////////////// @@ -98,27 +74,7 @@ remove_region(MouseWatcherRegion *region) { if (region == _button_down_region) { _button_down_region = (MouseWatcherRegion *)NULL; } - return _regions.erase(region) != 0; -} - -//////////////////////////////////////////////////////////////////// -// Function: MouseWatcher::find_region -// Access: Published -// Description: Returns a pointer to the first region found with the -// indicated name. If multiple regions share the same -// name, the one that is returned is indeterminate. -//////////////////////////////////////////////////////////////////// -MouseWatcherRegion *MouseWatcher:: -find_region(const string &name) const { - Regions::const_iterator ri; - for (ri = _regions.begin(); ri != _regions.end(); ++ri) { - MouseWatcherRegion *region = (*ri); - if (region->get_name() == name) { - return region; - } - } - - return (MouseWatcherRegion *)NULL; + return MouseWatcherGroup::remove_region(region); } //////////////////////////////////////////////////////////////////// @@ -151,6 +107,27 @@ get_over_region(const LPoint2f &pos) const { } } + // Also check all of our sub-groups. + Groups::const_iterator gi; + for (gi = _groups.begin(); gi != _groups.end(); ++gi) { + MouseWatcherGroup *group = (*gi); + for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { + MouseWatcherRegion *region = (*ri); + const LVecBase4f &frame = region->get_frame(); + + if (region->get_active() && + pos[0] >= frame[0] && pos[0] <= frame[1] && + pos[1] >= frame[2] && pos[1] <= frame[3]) { + + // We're over this region. Is it preferred to the other one? + if (over_region == (MouseWatcherRegion *)NULL || + *region < *over_region) { + over_region = region; + } + } + } + } + return over_region; } @@ -180,6 +157,59 @@ write(ostream &out, int indent_level) const { MouseWatcherRegion *region = (*ri); region->write(out, indent_level + 2); } + + if (!_groups.empty()) { + Groups::const_iterator gi; + for (gi = _groups.begin(); gi != _groups.end(); ++gi) { + MouseWatcherGroup *group = (*gi); + indent(out, indent_level + 2) + << "Subgroup:\n"; + for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { + MouseWatcherRegion *region = (*ri); + region->write(out, indent_level + 4); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::add_group +// Access: Public +// Description: Adds the indicated group of regions to the set of +// regions the MouseWatcher will monitor each frame. +// +// Since the MouseWatcher itself inherits from +// MouseWatcherGroup, this operation is normally not +// necessary--you can simply add the Regions you care +// about one at a time. Adding a complete group is +// useful when you may want to explicitly remove the +// regions as a group later. +// +// Returns true if the group was successfully added, or +// false if it was already on the list. +//////////////////////////////////////////////////////////////////// +bool MouseWatcher:: +add_group(MouseWatcherGroup *group) { + return _groups.insert(group).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::remove_group +// Access: Public +// Description: Removes the indicated group from the set of extra +// groups associated with the MouseWatcher. Returns +// true if successful, or false if the group was already +// removed or was never added via add_group(). +//////////////////////////////////////////////////////////////////// +bool MouseWatcher:: +remove_group(MouseWatcherGroup *group) { + if (group->has_region(_current_region)) { + _current_region = (MouseWatcherRegion *)NULL; + } + if (group->has_region(_button_down_region)) { + _button_down_region = (MouseWatcherRegion *)NULL; + } + return _groups.erase(group) != 0; } //////////////////////////////////////////////////////////////////// @@ -198,10 +228,12 @@ set_current_region(MouseWatcherRegion *region) { #endif if (region != _current_region) { if (_current_region != (MouseWatcherRegion *)NULL) { + _current_region->exit(); throw_event_pattern(_leave_pattern, _current_region); } _current_region = region; if (_current_region != (MouseWatcherRegion *)NULL) { + _current_region->enter(); throw_event_pattern(_enter_pattern, _current_region); } } @@ -219,9 +251,11 @@ throw_event_pattern(const string &pattern, const MouseWatcherRegion *region, if (pattern.empty()) { return; } +#ifndef NDEBUG if (region != (MouseWatcherRegion *)NULL) { region->test_ref_count_integrity(); } +#endif string event; for (size_t p = 0; p < pattern.size(); ++p) { @@ -323,13 +357,9 @@ transmit_data(NodeAttributes &data) { // There is some danger of losing button-up events here. If // more than one button goes down together, we won't detect // both of the button-up events properly. - - // We should probably throw a different button_up event based - // on whether the _current_region is NULL or not, so the - // calling code can differentiate between button_up within the - // starting region, and button_up outside the region. - // Presently, changing this will break the GUI code. if (_button_down_region != (MouseWatcherRegion *)NULL) { + bool is_within = (_current_region == _button_down_region); + _button_down_region->button_up(be._button, is_within); throw_event_pattern(_button_up_pattern, _button_down_region, be._button.get_name()); } @@ -343,6 +373,7 @@ transmit_data(NodeAttributes &data) { } _button_down = true; if (_button_down_region != (MouseWatcherRegion *)NULL) { + _button_down_region->button_down(be._button); throw_event_pattern(_button_down_pattern, _button_down_region, be._button.get_name()); } diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index a4f135ab69..401d125170 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -22,6 +22,7 @@ #include #include "mouseWatcherRegion.h" +#include "mouseWatcherGroup.h" #include #include @@ -52,15 +53,12 @@ // scene graph. It will move the geometry around // according to the mouse's known position. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA MouseWatcher : public DataNode { +class EXPCL_PANDA MouseWatcher : public DataNode, public MouseWatcherGroup { PUBLISHED: MouseWatcher(const string &name = ""); ~MouseWatcher(); - bool add_region(MouseWatcherRegion *region); - bool has_region(MouseWatcherRegion *region) const; bool remove_region(MouseWatcherRegion *region); - MouseWatcherRegion *find_region(const string &name) const; INLINE bool has_mouse() const; INLINE const LPoint2f &get_mouse() const; @@ -93,26 +91,29 @@ PUBLISHED: INLINE void clear_geometry(); INLINE void set_extra_handler(EventHandler *eh); - INLINE EventHandler* get_extra_handler(void) const; + INLINE EventHandler *get_extra_handler(void) const; public: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; + bool add_group(MouseWatcherGroup *group); + bool remove_group(MouseWatcherGroup *group); + private: void set_current_region(MouseWatcherRegion *region); void throw_event_pattern(const string &pattern, const MouseWatcherRegion *region, const string &button_name = string()); - typedef pset< PT(MouseWatcherRegion) > Regions; - Regions _regions; + typedef pset< PT(MouseWatcherGroup) > Groups; + Groups _groups; bool _has_mouse; LPoint2f _mouse; - MouseWatcherRegion *_current_region; - MouseWatcherRegion *_button_down_region; + PT(MouseWatcherRegion) _current_region; + PT(MouseWatcherRegion) _button_down_region; bool _button_down; string _button_down_pattern; diff --git a/panda/src/tform/mouseWatcherGroup.cxx b/panda/src/tform/mouseWatcherGroup.cxx new file mode 100644 index 0000000000..bd935a25a2 --- /dev/null +++ b/panda/src/tform/mouseWatcherGroup.cxx @@ -0,0 +1,87 @@ +// Filename: mouseWatcherGroup.cxx +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "mouseWatcherGroup.h" + + +TypeHandle MouseWatcherGroup::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::add_region +// Access: Published +// Description: Adds the indicated region to the set of regions in +// the group. Returns true if it was successfully +// added, or false if it was already on the list. +//////////////////////////////////////////////////////////////////// +bool MouseWatcherGroup:: +add_region(MouseWatcherRegion *region) { + return _regions.insert(region).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::has_region +// Access: Published +// Description: Returns true if the indicated region has already been +// added to the MouseWatcherGroup, false otherwise. +//////////////////////////////////////////////////////////////////// +bool MouseWatcherGroup:: +has_region(MouseWatcherRegion *region) const { + return _regions.count(region) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::remove_region +// Access: Published +// Description: Removes the indicated region from the group. +// Returns true if it was successfully removed, or false +// if it wasn't there in the first place. +//////////////////////////////////////////////////////////////////// +bool MouseWatcherGroup:: +remove_region(MouseWatcherRegion *region) { + return _regions.erase(region) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::find_region +// Access: Published +// Description: Returns a pointer to the first region found with the +// indicated name. If multiple regions share the same +// name, the one that is returned is indeterminate. +//////////////////////////////////////////////////////////////////// +MouseWatcherRegion *MouseWatcherGroup:: +find_region(const string &name) const { + Regions::const_iterator ri; + for (ri = _regions.begin(); ri != _regions.end(); ++ri) { + MouseWatcherRegion *region = (*ri); + if (region->get_name() == name) { + return region; + } + } + + return (MouseWatcherRegion *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::clear_regions +// Access: Published +// Description: Removes all the regions from the group. +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +clear_regions() { + _regions.clear(); +} diff --git a/panda/src/tform/mouseWatcherGroup.h b/panda/src/tform/mouseWatcherGroup.h new file mode 100644 index 0000000000..5bf66ed435 --- /dev/null +++ b/panda/src/tform/mouseWatcherGroup.h @@ -0,0 +1,60 @@ +// Filename: mouseWatcherGroup.h +// Created by: drose (02Jul01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOUSEWATCHERGROUP_H +#define MOUSEWATCHERGROUP_H + +#include "pandabase.h" +#include "mouseWatcherRegion.h" + +#include "pointerTo.h" +#include "referenceCount.h" +#include "pset.h" + +//////////////////////////////////////////////////////////////////// +// Class : MouseWatcherGroup +// Description : This represents a collection of MouseWatcherRegions +// that may be managed as a group. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MouseWatcherGroup : virtual public ReferenceCount { +PUBLISHED: + bool add_region(MouseWatcherRegion *region); + bool has_region(MouseWatcherRegion *region) const; + bool remove_region(MouseWatcherRegion *region); + MouseWatcherRegion *find_region(const string &name) const; + void clear_regions(); + +protected: + typedef pset< PT(MouseWatcherRegion) > Regions; + Regions _regions; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "MouseWatcherGroup"); + } + +private: + static TypeHandle _type_handle; + + friend class MouseWatcher; +}; + +#endif diff --git a/panda/src/tform/mouseWatcherRegion.cxx b/panda/src/tform/mouseWatcherRegion.cxx index a086f79052..2823620106 100644 --- a/panda/src/tform/mouseWatcherRegion.cxx +++ b/panda/src/tform/mouseWatcherRegion.cxx @@ -43,3 +43,47 @@ write(ostream &out, int indent_level) const { indent(out, indent_level) << get_name() << " lrbt = " << _frame << "\n"; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::enter +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse enters the region. +//////////////////////////////////////////////////////////////////// +void MouseWatcherRegion:: +enter() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::exit +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// mouse exits the region. +//////////////////////////////////////////////////////////////////// +void MouseWatcherRegion:: +exit() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::button_down +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button is depressed while the mouse +// is within the region. +//////////////////////////////////////////////////////////////////// +void MouseWatcherRegion:: +button_down(ButtonHandle) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::button_up +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button previously depressed with +// button_down() is release. The bool is_within flag is +// true if the button was released while the mouse was +// still within the region, or false if it was released +// outside the region. +//////////////////////////////////////////////////////////////////// +void MouseWatcherRegion:: +button_up(ButtonHandle, bool) { +} diff --git a/panda/src/tform/mouseWatcherRegion.h b/panda/src/tform/mouseWatcherRegion.h index 53301c8633..72c7a963c0 100644 --- a/panda/src/tform/mouseWatcherRegion.h +++ b/panda/src/tform/mouseWatcherRegion.h @@ -24,6 +24,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////// // Class : MouseWatcherRegion @@ -56,6 +57,11 @@ PUBLISHED: public: INLINE bool operator < (const MouseWatcherRegion &other) const; + virtual void enter(); + virtual void exit(); + virtual void button_down(ButtonHandle button); + virtual void button_up(ButtonHandle button, bool is_within); + private: LVecBase4f _frame; float _area; diff --git a/panda/src/tform/tform_composite1.cxx b/panda/src/tform/tform_composite1.cxx index 2a183463bb..ba608cb7b5 100644 --- a/panda/src/tform/tform_composite1.cxx +++ b/panda/src/tform/tform_composite1.cxx @@ -3,4 +3,5 @@ #include "config_tform.cxx" #include "dataValve.cxx" #include "driveInterface.cxx" +#include "planarSlider.cxx" diff --git a/panda/src/tform/tform_composite2.cxx b/panda/src/tform/tform_composite2.cxx index 49ef40e55b..e867533eb1 100644 --- a/panda/src/tform/tform_composite2.cxx +++ b/panda/src/tform/tform_composite2.cxx @@ -1,7 +1,7 @@ #include "mouseWatcher.cxx" +#include "mouseWatcherGroup.cxx" #include "mouseWatcherRegion.cxx" -#include "planarSlider.cxx" #include "trackball.cxx" #include "transform2sg.cxx"