Sciter: F:/hsmile5/sdk/include/behaviors/behavior_tabs.cpp Source File

Sciter  3.3.2.5
Sciter API
behavior_tabs.cpp
Go to the documentation of this file.
1 #include "stdafx.h"
2 #include "sciter-x.h"
3 #include "sciter-x-behavior.h"
4 
5 namespace sciter
6 {
7 /*
8 
9 BEHAVIOR: tabs
10  goal: Implementation of the tabs: stacked panels switchable by tabs in tab strip
11 COMMENTS:
12  <div style="behavior:tabs">
13  <ul> <!-- our tab strip, can be placed on any side of tab container. -->
14  <li panel="panel-id1" selected >tab1 caption</li>
15  <li panel="panel-id2">tab2 caption</li>
16  </ul>
17  <div name="panel-id1" > first panel content </div>
18  <div name="panel-id2"> second panel content </div>
19  </div>
20 SAMPLE:
21  See: samples/behaviors/tabs.htm
22 */
23 
24  struct tabs: public event_handler, behavior_factory
25 {
26  // ctor
27   tabs(): event_handler(),
28  behavior_factory("tabs")
29  {}
30 
31   virtual bool subscription( HELEMENT he, UINT& event_groups )
32  {
33  event_groups = HANDLE_MOUSE |
34  HANDLE_KEY |
35  HANDLE_FOCUS |
36  HANDLE_BEHAVIOR_EVENT |
37  HANDLE_TIMER;
38  return true;
39  }
40 
41  // the only behavior_factory method:
42   virtual event_handler* create(HELEMENT he) { return this; }
43 
44   virtual void attached (HELEMENT he )
45  {
46  dom::element tabs_el = he; //:root below matches the element we use to start lookup from.
47  dom::element tab_el = tabs_el.find_first(":root>.strip>[panel][selected]"); // initialy selected
48 
49  json::string pname = tab_el.get_attribute("panel");
50  // find panel we need to show by default
51  dom::element panel_el = tabs_el.find_first(":root>[name=\"%s\"],:root>[id=\"%s\"]", aux::w2a(pname).c_str(), aux::w2a(pname).c_str());
52  if( !panel_el.is_valid())
53  {
54  assert(false); // what a ...!, panel="somename" without matching name="somename"
55  return;
56  }
57 
58  dom::element tab_strip_el = tab_el.parent();
59  tab_strip_el.set_state(STATE_CURRENT,0); // :current - make tab strip as current element inside focusable.
60  tab_el.set_state(STATE_CURRENT,0); // :current - current tab is, well, current.
61  panel_el.set_state(STATE_EXPANDED,0); // :expanded - current panel is expanded.
62 
63  //tabs_el.start_timer(1000, (void*) 12345);
64 
65  }
66 
67  /*virtual bool on_timer (HELEMENT he, UINT_PTR timerId )
68  {
69  ::MessageBeep(MB_ICONASTERISK);
70  return true;
71  }*/
72 
73   virtual bool on_mouse(HELEMENT he, HELEMENT target, UINT event_type, POINT pt, UINT mouseButtons, UINT keyboardStates )
74  {
75  if( event_type != MOUSE_DOWN && event_type != MOUSE_DCLICK /*&& event_type != DROP*/)
76  return false;
77 
78  dom::element tabs_el = he; // our tabs container
79  dom::element tab_el = target_tab( target, he );
80 
81  if(!tab_el.is_valid())
82  return false;
83 
84  //el here is a <element panel="panel-id1">tab1 caption</element>
85  //and we've got here MOUSE_DOWN somewhere on it.
86 
87  return select_tab( tabs_el, tab_el );
88  }
89 
90   static inline bool is_in_focus(const dom::element& el)
91  {
92  return el.test(":focus") || el.find_nearest_parent(":focus");
93  }
94 
95 #ifdef WINDOWS
96  virtual bool on_key(HELEMENT he, HELEMENT target, UINT event_type, UINT code, UINT keyboardStates )
97  {
98  if(event_type != KEY_DOWN)
99  return FALSE; // we are handling only KEY_DOWN here
100 
101  dom::element tabs_el = he; // our tabs container
102  dom::element tab_el = tabs_el.find_first(":root>.strip>[panel]:current"); // currently selected
103 
104  switch( code )
105  {
106  case VK_TAB: if( keyboardStates & CONTROL_KEY_PRESSED )
107  return select_tab( tabs_el, tab_el,
108  keyboardStates & SHIFT_KEY_PRESSED? -1:1 );
109  break;
110  case VK_LEFT: return is_in_focus(tab_el)? select_tab( tabs_el, tab_el, -1 ):FALSE;
111  case VK_RIGHT: return is_in_focus(tab_el)? select_tab( tabs_el, tab_el, 1 ):FALSE;
112  case VK_HOME: return is_in_focus(tab_el)? select_tab( tabs_el, tab_el, -2 ):FALSE;
113  case VK_END: return is_in_focus(tab_el)? select_tab( tabs_el, tab_el, 2 ):FALSE;
114  }
115  return FALSE;
116  }
117 #endif
118 
119   virtual bool on_event (HELEMENT he, HELEMENT target, BEHAVIOR_EVENTS type, UINT_PTR reason )
120  {
121  if( type == ACTIVATE_CHILD )
122  {
123  dom::element newtab = target_tab(target, he);
124  if(!newtab.is_valid())
125  {
126  assert(false); // target is not a tab here.
127  return TRUE;
128  }
129  dom::element tabs = he;
130  select_tab(tabs,newtab);
131  return TRUE;
132  }
133  return FALSE;
134  }
135 
136  // select
137   bool select_tab( dom::element& tabs_el, dom::element& tab_el )
138  {
139  if(tab_el.get_state(STATE_CURRENT))
140  // already selected, nothing to do...
141  return true; // but we've handled it.
142 
143  //find currently selected element (tab and panel) and remove "selected" from them
144  dom::element prev_panel_el = tabs_el.find_first(":root>:not(.strip):expanded");
145  dom::element prev_tab_el = tabs_el.find_first(":root>.strip>[panel]:current");
146 
147  // find new tab and panel
148  json::string pname = tab_el.get_attribute("panel");
149  dom::element panel_el = tabs_el.find_first(":root>[name=\"%s\"],:root>[id=\"%s\"]", aux::w2a(pname).c_str(), aux::w2a(pname).c_str());
150 
151  if( !panel_el.is_valid() || !tab_el.is_valid() )
152  {
153  assert(false); // panel="somename" without matching name="somename"
154  return true;
155  }
156 
157  if( prev_panel_el.is_valid() )
158  {
159  prev_panel_el.set_attribute("selected", 0); // remove selected attribute - just in case somone is using attribute selectors
160  prev_panel_el.set_state(STATE_COLLAPSED,0); // set collapsed in case of someone use it for styling
161  }
162  if( prev_tab_el.is_valid() )
163  {
164  prev_tab_el.set_attribute("selected", 0); // remove selected attribute
165  prev_tab_el.set_state(0,STATE_CURRENT); // reset also state flag, :current
166  }
167 
168  panel_el.set_attribute("selected", WSTR("")); // set selected attribute (empty)
169  panel_el.set_state(STATE_EXPANDED,0); // expand it
170 
171  tab_el.set_attribute("selected", WSTR("")); // set selected attribute (empty)
172  tab_el.set_state(STATE_CURRENT,0); // set also state flag, :current
173 
174  // notify all parties involved
175  if(prev_tab_el.is_valid())
176  prev_tab_el.post_event(ELEMENT_COLLAPSED,0, prev_tab_el); // source here is old collapsed tab itself
177  tab_el.post_event(ELEMENT_EXPANDED,0, tab_el); // source here is new expanded tab itself
178  // NOTE #1: these event will bubble from panel elements up to the root so panel itself, tabs ctl, its parent, etc.
179  // will receive these notifications. Handle them if you need to change UI dependent from current tab.
180  // NOTE #2: while handling this event in:
181  // virtual bool on_event (HELEMENT he, HELEMENT target, BEHAVIOR_EVENTS type, UINT reason ),
182  // HELEMENT target is the panel element being collapsed/expanded
183 
184  return true;
185  }
186 
187  // select next/prev/first/last tab
188   bool select_tab( dom::element& tabs_el, dom::element& tab_el, int direction )
189  {
190 
191  // find new tab
192 
193  dom::element new_tab_el(0);
194 
195  int rdirection = direction;
196 
197  switch( direction )
198  {
199  case -2: new_tab_el = tab_el.first_sibling(); rdirection = +1; break;
200  case -1: new_tab_el = tab_el.prev_sibling(); break;
201  case +1: new_tab_el = tab_el.next_sibling(); break;
202  case +2: new_tab_el = tab_el.last_sibling(); rdirection = -1; break;
203  default: assert(false); return false;
204  }
205 
206  if( !new_tab_el.is_valid() || new_tab_el.get_attribute("panel").length() == 0 ) //is not a tab element
207  return FALSE;
208 
209  if( !new_tab_el.enabled() || !new_tab_el.visible() ) // if it is either disabled or not visible - try next
210  return select_tab( tabs_el, new_tab_el, rdirection );
211 
212  return select_tab( tabs_el, new_tab_el );
213  }
214 
215   dom::element target_tab(HELEMENT he, HELEMENT h_tabs_container)
216  {
217  if( he == h_tabs_container )
218  return 0;
219 
220  dom::element el = he;
221  json::string panel_name = el.get_attribute("panel");
222  if(panel_name.length())
223  return el; // here we are!
224 
225  return target_tab( el.parent(), h_tabs_container );
226  }
227 
228 
229 };
230 
231 // instantiating and attaching it to the global list
232  tabs tabs_instance;
233 }
Behaiviors support (a.k.a windowless scriptable controls)
bool test(const char *selector,...) const
HELEMENT prev_sibling() const
bool select_tab(dom::element &tabs_el, dom::element &tab_el, int direction)
void set_state(unsigned int bitsToSet, unsigned int bitsToClear=0, bool update=true)
BEHAVIOR_EVENTS
void set_attribute(const char *name, const WCHAR *value)
virtual event_handler * create(HELEMENT he)
bool is_valid() const
virtual void attached(HELEMENT he)
tabs tabs_instance
HELEMENT first_sibling() const
HELEMENT last_sibling() const
virtual bool on_mouse(HELEMENT he, HELEMENT target, UINT event_type, POINT pt, UINT mouseButtons, UINT keyboardStates)
static bool is_in_focus(const dom::element &el)
unsigned int get_state() const
bool select_tab(dom::element &tabs_el, dom::element &tab_el)
HELEMENT find_first(const char *selector,...) const
virtual bool on_event(HELEMENT he, HELEMENT target, BEHAVIOR_EVENTS type, UINT_PTR reason)
HELEMENT find_nearest_parent(const char *selector,...) const
dom::element target_tab(HELEMENT he, HELEMENT h_tabs_container)
virtual bool subscription(HELEMENT he, UINT &event_groups)
void * HELEMENT
Definition: sciter-x-dom.h:40
std::basic_string< WCHAR > string
Definition: value.hpp:42
HELEMENT parent() const
sciter::string get_attribute(unsigned int n) const
HELEMENT next_sibling() const
void post_event(unsigned int event_code, unsigned int reason=0, HELEMENT heSource=0)

AltStyle によって変換されたページ (->オリジナル) /