MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
spinner.cpp
1 // © Copyright 2010 - 2014 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 #ifndef _uispinner_cpp
41 #define _uispinner_cpp
42 
43 #include "UI/spinner.h"
44 #include "UI/button.h"
45 #include "UI/screen.h"
46 #include "UI/uimanager.h"
47 
48 #include "Input/inputmanager.h"
49 #include "Input/mouse.h"
50 #include "stringtool.h"
51 #include "entresol.h"
52 #include <sstream>
53 
54 namespace Mezzanine
55 {
56  namespace UI
57  {
58  /*Spinner::Spinner(const String& name, const Rect& RendRect, const UI::SpinnerStyle& SStyle, const Real& GlyphHeight, Screen* parent)
59  : Widget(name,parent),
60  IncrementOffset(Vector2(0,0)),
61  DecrementOffset(Vector2(0,0)),
62  Value(0),
63  MinValue(0),
64  MaxValue(0),
65  DecimalPlaces(0),
66  DecimalDisplay(false),
67  Editable(false),
68  IncrementLock(true),
69  DecrementLock(true),
70  Increment(NULL),
71  Decrement(NULL),
72  ValueDisplay(NULL)
73  {
74  Type = Widget::W_Spinner;
75  SpinLayout = SStyle;
76  if(RendRect.Relative)
77  {
78  RelPosition = RendRect.Position;
79  RelSize = RendRect.Size;
80  }else{
81  RelPosition = RendRect.Position / ParentScreen->GetViewportDimensions();
82  RelSize = RendRect.Size / ParentScreen->GetViewportDimensions();
83  }
84 
85  if(UI::Spn_Separate == SpinLayout)
86  {
87  if(RendRect.Size.X > RendRect.Size.Y * 2)
88  {
89  if(RendRect.Relative)
90  {
91  const Vector2& WinDim = ParentScreen->GetViewportDimensions();
92  Vector2 APos = RendRect.Position * WinDim;
93  Vector2 ASize = RendRect.Size * WinDim;
94  CalculateOffsets(ASize);
95  CreateHorizontalSpinner(APos,ASize,GlyphHeight);
96  }else{
97  CalculateOffsets(RendRect.Size);
98  CreateHorizontalSpinner(RendRect.Position,RendRect.Size,GlyphHeight);
99  }
100  }
101  else if(RendRect.Size.Y > RendRect.Size.X * 2)
102  {
103  if(RendRect.Relative)
104  {
105  const Vector2& WinDim = ParentScreen->GetViewportDimensions();
106  Vector2 APos = RendRect.Position * WinDim;
107  Vector2 ASize = RendRect.Size * WinDim;
108  CalculateOffsets(ASize);
109  CreateVerticalSpinner(APos,ASize,GlyphHeight);
110  }else{
111  CalculateOffsets(RendRect.Size);
112  CreateVerticalSpinner(RendRect.Position,RendRect.Size,GlyphHeight);
113  }
114  }
115  else
116  {
117  MEZZ_EXCEPTION(Exception::PARAMETERS_EXCEPTION,"Spinner dimensions incompatible with this widget.");
118  }
119  }
120  else if(UI::Spn_Together_Left == SpinLayout || UI::Spn_Together_Right == SpinLayout)
121  {
122  if(RendRect.Relative)
123  {
124  const Vector2& WinDim = ParentScreen->GetViewportDimensions();
125  Vector2 APos = RendRect.Position * WinDim;
126  Vector2 ASize = RendRect.Size * WinDim;
127  CalculateOffsets(ASize);
128  CreateBoxSpinner(APos,ASize,GlyphHeight);
129  }else{
130  CalculateOffsets(RendRect.Size);
131  CreateBoxSpinner(RendRect.Position,RendRect.Size,GlyphHeight);
132  }
133  }
134  CaptureData = new InputCaptureData();
135  CaptureData->AddInputRange(Input::KEY_1,Input::KEY_0);
136  CaptureData->AddInputRange(Input::KEY_KP_1,Input::KEY_KP_PERIOD);
137  CaptureData->AddInput(Input::KEY_PERIOD);
138  CaptureData->AddInput(Input::KEY_RETURN);
139  CaptureData->AddInput(Input::KEY_KP_ENTER);
140  }
141 
142  Spinner::~Spinner()
143  {
144  ParentScreen->DestroyWidget(Increment);
145  ParentScreen->DestroyWidget(Decrement);
146  ParentScreen->DestroyBasicRenderable(ValueDisplay);
147  delete CaptureData;
148  }
149 
150  void Spinner::CreateHorizontalSpinner(const Vector2& Position, const Vector2& Size, const Real& GlyphHeight)
151  {
152  const Vector2& WinDim = ParentScreen->GetViewportDimensions();
153  Whole ActHeight = (Whole)(GlyphHeight * WinDim.Y);
154  std::pair<Whole,Real> GlyphInfo = Manager->SuggestGlyphIndex(ActHeight,ParentScreen->GetPrimaryAtlas());
155  Vector2 IncPos = Position + IncrementOffset;
156  Vector2 DecPos = Position + DecrementOffset;
157  Vector2 ValPos = Position + ValueDisplayOffset;
158  Increment = ParentScreen->CreateButton(Name+"Inc",Rect(IncPos,Vector2(Size.Y,Size.Y),false));
159  Decrement = ParentScreen->CreateButton(Name+"Dec",Rect(DecPos,Vector2(Size.Y,Size.Y),false));
160  ValueDisplay = ParentScreen->CreateCaption(Name+"Dis",Rect(ValPos,Vector2(Size.X - (Size.Y * 2),Size.Y),false),GlyphInfo.first,GetValueAsText());
161  AddSubRenderable(0,Increment);
162  AddSubRenderable(1,Decrement);
163  AddSubRenderable(2,ValueDisplay);
164  if(1 != GlyphInfo.second)
165  ValueDisplay->SetTextScale(GlyphInfo.second);
166  }
167 
168  void Spinner::CreateVerticalSpinner(const Vector2& Position, const Vector2& Size, const Real& GlyphHeight)
169  {
170  const Vector2& WinDim = ParentScreen->GetViewportDimensions();
171  Whole ActHeight = (Whole)(GlyphHeight * WinDim.Y);
172  std::pair<Whole,Real> GlyphInfo = Manager->SuggestGlyphIndex(ActHeight,ParentScreen->GetPrimaryAtlas());
173  Vector2 IncPos = Position + IncrementOffset;
174  Vector2 DecPos = Position + DecrementOffset;
175  Vector2 ValPos = Position + ValueDisplayOffset;
176  Increment = ParentScreen->CreateButton(Name+"Inc",Rect(IncPos,Vector2(Size.X,Size.X),false));
177  Decrement = ParentScreen->CreateButton(Name+"Dec",Rect(DecPos,Vector2(Size.X,Size.X),false));
178  ValueDisplay = ParentScreen->CreateCaption(Name+"Dis",Rect(ValPos,Vector2(Size.X,Size.Y - (Size.X * 2)),false),GlyphInfo.first,GetValueAsText());
179  AddSubRenderable(0,Increment);
180  AddSubRenderable(1,Decrement);
181  AddSubRenderable(2,ValueDisplay);
182  if(1 != GlyphInfo.second)
183  ValueDisplay->SetTextScale(GlyphInfo.second);
184  }
185 
186  void Spinner::CreateBoxSpinner(const Vector2& Position, const Vector2& Size, const Real& GlyphHeight)
187  {
188  const Vector2& WinDim = ParentScreen->GetViewportDimensions();
189  Whole ActHeight = (Whole)(GlyphHeight * WinDim.Y);
190  std::pair<Whole,Real> GlyphInfo = Manager->SuggestGlyphIndex(ActHeight,ParentScreen->GetPrimaryAtlas());
191  Vector2 IncPos = Position + IncrementOffset;
192  Vector2 DecPos = Position + DecrementOffset;
193  Vector2 ValPos = Position + ValueDisplayOffset;
194  Increment = ParentScreen->CreateButton(Name+"Inc",Rect(IncPos,Vector2(Size.Y * 0.5,Size.Y * 0.5),false));
195  Decrement = ParentScreen->CreateButton(Name+"Dec",Rect(DecPos,Vector2(Size.Y * 0.5,Size.Y * 0.5),false));
196  ValueDisplay = ParentScreen->CreateCaption(Name+"Dis",Rect(ValPos,Vector2(Size.X - (Size.Y * 0.5),Size.Y),false),GlyphInfo.first,GetValueAsText());
197  AddSubRenderable(0,Increment);
198  AddSubRenderable(1,Decrement);
199  AddSubRenderable(2,ValueDisplay);
200  if(1 != GlyphInfo.second)
201  ValueDisplay->SetTextScale(GlyphInfo.second);
202  }
203 
204  void Spinner::CalculateOffsets(const Vector2& Size)
205  {
206  switch (SpinLayout)
207  {
208  case UI::Spn_Separate:
209  {
210  if(Size.X > Size.Y * 2)
211  {
212  IncrementOffset = Vector2(Size.X - Size.Y,0);
213  DecrementOffset = Vector2(0,0);
214  ValueDisplayOffset = Vector2(Size.Y,0);
215  }
216  else if(Size.Y > Size.X * 2)
217  {
218  IncrementOffset = Vector2(0,0);
219  DecrementOffset = Vector2(0,Size.Y - Size.X);
220  ValueDisplayOffset = Vector2(0,Size.X);
221  }
222  break;
223  }
224  case UI::Spn_Together_Left:
225  {
226  IncrementOffset = Vector2(0,0);
227  DecrementOffset = Vector2(0,Size.Y * 0.5);
228  ValueDisplayOffset = Vector2(Size.Y * 0.5,0);
229  break;
230  }
231  case UI::Spn_Together_Right:
232  {
233  IncrementOffset = Vector2(Size.X - (Size.Y * 0.5),0);
234  DecrementOffset = Vector2(Size.X - (Size.Y * 0.5),Size.Y * 0.5);
235  ValueDisplayOffset = Vector2(0,0);
236  break;
237  }
238  }
239  }
240 
241  void Spinner::SetLocation(const Vector2& Position)
242  {
243  Increment->SetActualPosition(Position + IncrementOffset);
244  Decrement->SetActualPosition(Position + DecrementOffset);
245  ValueDisplay->SetActualPosition(Position + ValueDisplayOffset);
246  }
247 
248  void Spinner::SetArea(const Vector2& Size)
249  {
250  switch (SpinLayout)
251  {
252  case UI::Spn_Separate:
253  {
254  if(Size.X > Size.Y * 2)
255  {
256  Increment->SetActualSize(Vector2(Size.Y,Size.Y));
257  Decrement->SetActualSize(Vector2(Size.Y,Size.Y));
258  ValueDisplay->SetActualSize(Vector2(Size.X - (Size.Y * 2),Size.Y));
259  }
260  else if(Size.Y > Size.X * 2)
261  {
262  Increment->SetActualSize(Vector2(Size.X,Size.X));
263  Decrement->SetActualSize(Vector2(Size.X,Size.X));
264  ValueDisplay->SetActualSize(Vector2(Size.X,Size.Y - (Size.X * 2)));
265  }
266  break;
267  }
268  case UI::Spn_Together_Left:
269  case UI::Spn_Together_Right:
270  {
271  Increment->SetActualSize(Vector2(Size.Y * 0.5,Size.Y * 0.5));
272  Decrement->SetActualSize(Vector2(Size.Y * 0.5,Size.Y * 0.5));
273  ValueDisplay->SetActualSize(Vector2(Size.X - (Size.Y * 0.5),Size.Y));
274  break;
275  }
276  }
277  }
278 
279  void Spinner::CheckValueLimits()
280  {
281  if(MinValue != 0 || MaxValue != 0)
282  {
283  if(Value < MinValue) Value = MinValue;
284  if(Value > MaxValue) Value = MaxValue;
285  }
286  }
287 
288  String Spinner::GetValueAsText()
289  {
290  std::stringstream converter;
291  String AsText;
292  converter << Value;
293  converter >> AsText;
294  if(DecimalDisplay)
295  {
296  String Deci(".");
297  if(DecimalPlaces + 1 > AsText.size()) AsText.insert(0,(DecimalPlaces+1) - AsText.size(),'0');
298  AsText.insert(AsText.size() - DecimalPlaces,Deci);
299  }
300  return AsText;
301  }
302 
303  void Spinner::UpdateImpl(bool Force)
304  {
305  ProcessCapturedInputs();
306  if( HoveredSubWidget && (Widget::W_Button == HoveredSubWidget->GetType()) )
307  {
308  Input::ButtonState State = InputManager::GetSingletonPtr()->GetSystemMouse()->GetButtonState(1);
309  if(Input::BUTTON_PRESSING == State)
310  {
311  if(HoveredSubWidget==Increment)
312  IncrementLock = false;
313  else if(HoveredSubWidget==Decrement)
314  DecrementLock = false;
315  }
316  else if(Input::BUTTON_LIFTING && (!IncrementLock || !DecrementLock))
317  {
318  if(HoveredSubWidget==Increment && !IncrementLock)
319  {
320  Value++;
321  CheckValueLimits();
322  ValueDisplay->SetText(GetValueAsText());
323  IncrementLock = true;
324  }
325  else if(HoveredSubWidget==Decrement && !DecrementLock)
326  {
327  Value--;
328  CheckValueLimits();
329  ValueDisplay->SetText(GetValueAsText());
330  DecrementLock = true;
331  }
332  }
333  }
334  }
335 
336  void Spinner::SetVisibleImpl(bool visible)
337  {
338  Increment->SetVisible(visible);
339  Decrement->SetVisible(visible);
340  ValueDisplay->SetVisible(visible);
341  }
342 
343  bool Spinner::CheckMouseHoverImpl()
344  {
345  if(Increment->CheckMouseHover())
346  {
347  HoveredSubWidget = Increment;
348  return true;
349  }
350  if(Decrement->CheckMouseHover())
351  {
352  HoveredSubWidget = Decrement;
353  return true;
354  }
355  if(ValueDisplay->CheckMouseHover())
356  {
357  HoveredSubWidget = NULL;
358  return true;
359  }
360  return false;
361  }
362 
363  void Spinner::ProcessCapturedInputs()
364  {
365  if(!Editable)
366  return;
367  if(0 == CaptureData->GetNumCapturedInputs())
368  return;
369  std::vector<Input::InputCode>* CCodes = CaptureData->GetCapturedInputs();
370  bool EnterPressed = false;
371  for( Whole X = 0 ; X < CCodes->size() ; X++ )
372  {
373  Input::InputCode Current = CCodes->at(X);
374  if(Input::KEY_RETURN == Current || Input::KEY_KP_ENTER == Current)
375  {
376  EnterPressed = true;
377  continue;
378  }else{
379  EditCache+=StringTools::ConvertToString(Current,false);
380  }
381  }
382  if(EnterPressed)
383  {
384  std::stringstream TempStream;
385  if(DecimalDisplay)
386  EditCache.erase(EditCache.find("."),1);
387  TempStream << EditCache;
388  TempStream >> Value;
389  CheckValueLimits();
390  ValueDisplay->SetText(GetValueAsText());
391  EditCache.clear();
392  }
393  }
394 
395  void Spinner::SetSpinnerValue(const int& ValueToSet)
396  {
397  Value = ValueToSet;
398  CheckValueLimits();
399  ValueDisplay->SetText(GetValueAsText());
400  }
401 
402  int Spinner::GetSpinnerValue()
403  {
404  return Value;
405  }
406 
407  void Spinner::SetValueLimits(const int& Lower, const int& Upper)
408  {
409  MinValue = Lower;
410  MaxValue = Upper;
411  }
412 
413  void Spinner::SetEditable(bool Edit)
414  {
415  Editable = Edit;
416  }
417 
418  bool Spinner::GetEditable()
419  {
420  return Editable;
421  }
422 
423  void Spinner::EnableDecimalDisplay(bool Enable, const Whole& Places)
424  {
425  DecimalDisplay = Enable;
426  DecimalPlaces = Places;
427  }
428 
429  void Spinner::SetPosition(const Vector2& Position)
430  {
431  RelPosition = Position;
432  Vector2 Adjusted = Position * ParentScreen->GetViewportDimensions();
433  SetLocation(Adjusted);
434  }
435 
436  void Spinner::SetActualPosition(const Vector2& Position)
437  {
438  RelPosition = Position / ParentScreen->GetViewportDimensions();
439  SetLocation(Position);
440  }
441 
442  void Spinner::SetSize(const Vector2& Size)
443  {
444  RelSize = Size;
445  Vector2 Adjusted = Size * ParentScreen->GetViewportDimensions();
446  CalculateOffsets(Adjusted);
447  SetArea(Adjusted);
448  SetLocation(GetActualPosition());
449  }
450 
451  void Spinner::SetActualSize(const Vector2& Size)
452  {
453  RelSize = Size / ParentScreen->GetViewportDimensions();
454  CalculateOffsets(Size);
455  SetArea(Size);
456  SetLocation(GetActualPosition());
457  }
458 
459  void Spinner::UpdateDimensions()
460  {
461  WidgetResult Result = ViewportUpdateTool::UpdateWidget(this);
462  RelPosition = Result.first / ViewportUpdateTool::GetNewSize();
463  RelSize = Result.second / ViewportUpdateTool::GetNewSize();
464  Increment->UpdateDimensions();
465  Decrement->UpdateDimensions();
466  ValueDisplay->UpdateDimensions();
467  CalculateOffsets(Result.second);
468  SetPosition(RelPosition);
469  }
470 
471  Button* Spinner::GetIncrement()
472  {
473  return Increment;
474  }
475 
476  Button* Spinner::GetDecrement()
477  {
478  return Decrement;
479  }
480 
481  Caption* Spinner::GetValueDisplay()
482  {
483  return ValueDisplay;
484  }
485 
486  ///////////////////////////////////////////////////////////////////////////////
487  // PageProvider Methods
488 
489  Real Spinner::GetMaxXPages() const
490  { return this->MaxXPages; }
491 
492  Real Spinner::GetMaxYPages() const
493  { return this->MaxYPages; }
494 
495  Real Spinner::GetCurrentXPage() const
496  {
497  Real PageValue = static_cast<Real>( this->Target->GetSpinnerValue() );
498  if( this->PageOrder == UI::OP_Horizontal_Vertical ) {
499  return MathTool::Ceil( PageValue / this->MaxXPages );
500  }else if( this->PageOrder == UI::OP_Vertical_Horizontal ) {
501  return MathTool::Fmod( PageValue, this->MaxYPages );
502  }
503  return 1;
504  }
505 
506  Real Spinner::GetCurrentYPage() const
507  {
508  Real PageValue = static_cast<Real>( this->Target->GetSpinnerValue() );
509  if( this->PageOrder == UI::OP_Horizontal_Vertical ) {
510  return MathTool::Ceil( PageValue / this->MaxXPages );
511  }else if( this->PageOrder == UI::OP_Vertical_Horizontal ) {
512  return MathTool::Fmod( PageValue, this->MaxYPages );
513  }
514  return 1;
515  }
516 
517  void Spinner::SetOrdering(const UI::OrderingPriority Order)
518  { this->PageOrder = Order; }
519 
520  UI::OrderingPriority Spinner::GetOrdering() const
521  { return this->PageOrder; }
522 
523  ///////////////////////////////////////////////////////////////////////////////
524  // Serialization
525 
526  void Spinner::ProtoSerializePageData(XML::Node& SelfRoot) const
527  { }
528 
529  void Spinner::ProtoDeSerializePageData(const XML::Node& SelfRoot)
530  { }
531 
532  ///////////////////////////////////////////////////////////////////////////////
533  // Internal Methods
534 
535  void Spinner::_SetContainer(PagedContainer* ToUpdate)
536  { this->Container = ToUpdate; }
537 
538  void Spinner::_NotifyContainerUpdated()
539  { this->Target->SetValueLimits(1,this->MaxXPages * this->MaxYPages); }//*/
540  }//UI
541 }//Mezzanine
542 
543 #endif