Position_InputNode.cpp
Go to the documentation of this file.
1 #include "Position_InputNode.hpp"
2 #include "../Defs.hpp"
3 
4 namespace iv
5 {
6 
7 Position_InputNode::Position_InputNode( Instance * inst, Tester * tester, bool keeps_press_in_offspace ) :
8  InputNode( inst ),
9  InputEvent( inst ),
10  FrameUpdateClient( inst ),
11  cm( inst, this, "Position_InputNode", ClientMarker::Status() ),
12  attr_hover( &this->cm, this, false ),
13  tester( tester ),
14  keeps_press_in_offspace( keeps_press_in_offspace ),
15  ibq( inst ),
16  iq( inst ),
17  _input_id(),
18  _fallthrough( false ),
19  _active( std::nullopt ),
20  _durated( std::nullopt ),
21  _watched(),
22  _last( std::nullopt )
23 {
24  this->cm.inherits( this->InputNode::cm, this->InputEvent::cm, this->FrameUpdateClient::cm );
25  this->cm.owns( this->ibq.cm );
26 
28 
29  this->setup_frame_update();
30 }
31 
33 {
34 }
35 
37 {
38  static iv::TableId DebugTable = TableId::create( "Position_InputNode" );
39 
40  auto row = view->Table( DebugTable ).Row( this );
41  (void)row;
42 
43  row.Column( "input_id", this->input_id() );
44  row.Column( "fallthrough_enabled", this->fallthrough_enabled() );
45  row.Column( "keeps_press_in_offspace", this->keeps_press_in_offspace );
46  row.Column( "active", this->_active );
47  row.Column( "last", this->_last );
48 }
49 
51 {
52  this->_input_id = val;
53  this->input_treeRefresh();
54 }
55 
57 {
58  return this->_input_id;
59 }
60 
62 {
63  this->_fallthrough = val;
64  this->input_treeRefresh();
65 }
66 
68 {
69  return this->_fallthrough;
70 }
71 
72 void Position_InputNode::setup_frame_update()
73 {
74  if( this->_watched.size() )
75  this->frame_update_resume();
76  else
77  this->frame_update_pause();
78 }
79 
80 void Position_InputNode::frame_update()
81 {
82  for( auto & p_devkey : this->_watched )
83  {
84  // test hit
85  int2 input_position = this->iq.input_position( p_devkey.first.first, p_devkey.first.second );
86  bool hit = this->tester->position_test( input_position );
87 
88  // change?
89  if( hit != p_devkey.second )
90  {
91  this->input_treeRefresh();
92  break;
93  }
94  }
95 }
96 
98 {
99  if( !this->_last.has_value() )
100  {
101  Input::Key bound_key = Input::Key::None;
102  int bound_device_id = -1;
103 
104  this->ibq.ForeachBinding(
105  this->_input_id,
106  [ & ]( Input::Key key, int device_id )
107  {
108  bound_key = key;
109  bound_device_id = device_id;
110  }
111  );
112 
113  if( bound_key == Input::Key::None )
114  return int2( -1, -1 );
115 
116  return this->iq.input_position( bound_key, bound_device_id );
117  }
118 
119  Input::DeviceKey key = this->_last.value();
120  return this->iq.input_position( key.first, key.second );
121 }
122 
123 
124 bool Position_InputNode::input_trigger_process( InputRoot * root, Input::DeviceKey key )
125 {
126  if( !this->ibq.IsBound( this->_input_id, key ) )
127  return true;
128 
129  int2 pos = this->iq.input_position( key.first, key.second );
130  bool hit = this->tester->position_test( pos );
131  if( !hit )
132  return true;
133 
134  this->ie_trigger();
135  return false;
136 }
137 
138 void Position_InputNode::input_process_hover( InputRoot * root, Input::DeviceKey key, bool & press, bool & real, bool & offspace )
139 {
140  if( !this->ibq.IsHoverBound( this->_input_id, key ) )
141  return;
142 
143  //
144  int2 pos = this->iq.input_position( key.first, key.second );
145  bool hit = this->tester->position_test( pos );
146 
147  // watched
148  if( press )
149  this->_watched[ key ] = hit;
150  else
151  this->_watched.erase( key );
152 
153  this->setup_frame_update();
154 
155  // change hover
156  bool new_hover = press && hit && ( !offspace || ( this->keeps_press_in_offspace && this->_active.has_value() ) );
157  //cout << "PositionInputEvent (" << this << ") hover -> " << new_hover << endl;
158  this->Attribute_Set( &this->attr_hover, new_hover );
159 
160  if( hit )
161  offspace = true;
162 
163  // hover is offspaced if we have active thing pressed in offspace (and keeps_press_in_offspace is true)
164  //if( this->keeps_press_in_offspace && this->_active.has_value() ) // PROBLEM - we need to reserve the hover
165  // offspace = true;
166 }
167 
168 void Position_InputNode::reserve_hover_keys( InputRoot * root, bool reserve )
169 {
170  // reserve hover keys
171  this->ibq.ForeachHoverBinding(
172  this->_input_id,
173  [ root, reserve ]( Input::Key key, int device_id )
174  {
175  root->reserve_key( Input::DeviceKey( key, device_id ), reserve );
176  }
177  );
178 }
179 
180 void Position_InputNode::input_process( InputRoot * root, Input::DeviceKey key, bool & press, bool & real, bool & offspace )
181 {
182  // hover input
183  this->input_process_hover( root, key, press, real, offspace );
184 
185  //
186  int2 pos = this->iq.input_position( key.first, key.second );
187  bool hit = this->tester->position_test( pos );
188 
189  if( !this->ibq.IsBound( this->_input_id, key ) )
190  {
191  this->cm.log( SRC_INFO, Defs::Log::Input, "Refuse input (not bound)." );
192 
193  // Each position input event will block all input and binding is only to know which should be accepted (fallthrough works only on keys that are bound to attached inputId and only if enabled) .
194  if( hit )
195  offspace = true;
196  return;
197  }
198 
199  //cout << "PositionInputEvent::input_process key="<<ivorium::StringIO_Write( key.first, this->object() )<<" hit="<<hit<<" press="<<press<<" real="<<real<<" offspace="<<offspace<<" queue_refresh="<<queue_refresh << endl;
200 
201  // watched
202  if( press )
203  this->_watched[ key ] = hit;
204  else
205  this->_watched.erase( key );
206  this->setup_frame_update();
207 
208  // duration
209  if( press && hit && !offspace )
210  {
211  if( ( !this->_durated.has_value() || this->_durated == key ) )
212  {
213  this->cm.log( SRC_INFO, Defs::Log::Input, "Accept input / begin Duration." );
214  this->_durated = key;
215  this->ie_start_duration();
216  }
217  else
218  {
219  this->cm.log( SRC_INFO, Defs::Log::Input, "Reject input / Duration is already active with different key." );
220  }
221  }
222  else
223  {
224  if( this->_durated == key )
225  {
226  this->cm.log( SRC_INFO, Defs::Log::Input, "Accept input / end Duration -> queue refresh." );
227 
228  this->_durated = std::nullopt;
229  this->input_treeRefresh();
230  this->ie_stop_duration();
231  }
232  else
233  {
234  this->cm.log( SRC_INFO, Defs::Log::Input, "Reject input / Duration is active with different key." );
235  }
236  }
237 
238  // activation
239  if( !this->_active.has_value() && press && real && hit && !offspace )
240  { // clicked on this
241  offspace = true;
242  this->_active = key;
243  this->_last = key;
244 
245  if( this->keeps_press_in_offspace )
246  {
247  // reserve key
248  root->reserve_key( key, true );
249  this->reserve_hover_keys( root, true );
250  }
251 
252  this->cm.log( SRC_INFO, Defs::Log::Input, "Accept input / Press Activation -> input type changed to offspace." );
253 
254  this->ie_start_activation();
255  }
256  else if( this->_active == key && press && ( !hit || offspace ) )
257  { // moved to offspace
258  if( this->keeps_press_in_offspace )
259  {
260  offspace = true;
261  }
262  else
263  {
264  // release it
265  if( this->_active )
266  {
267  this->input_treeRefresh();
268  this->cm.log( SRC_INFO, Defs::Log::Input, "Queue refresh (activation moved to offspace)." );
269  }
270  this->_active = std::nullopt;
271  //this->reserve_hover_keys( root, false );
272  this->ie_stop_activation( false );
273  }
274  }
275  else if( this->_active == key && !press )
276  { // released
277  this->input_treeRefresh();
278  this->cm.log( SRC_INFO, Defs::Log::Input, "Accept input / Release Activation (real: ", real ,") -> queue refresh." );
279 
280  this->_active = std::nullopt;
281  if( this->keeps_press_in_offspace )
282  this->reserve_hover_keys( root, false );
283  this->ie_stop_activation( real );
284  }
285 
286  if( !this->_fallthrough )
287  {
288  if( hit )
289  {
290  offspace = true;
291  this->cm.log( SRC_INFO, Defs::Log::Input, "Block input (change it to offspace because fallthrough is not enabled)." );
292  }
293  }
294 }
295 
297 {
298  this->cm.log( SRC_INFO, Defs::Log::Input, "Activation interrupted from outside code." );
299  this->_active = std::nullopt;
300  this->ie_stop_activation( false );
301  this->input_treeRefresh();
302 }
303 
304 }