Lumiera  0.pre.03
»edit your freedom«
ui-coord.hpp
Go to the documentation of this file.
1 /*
2  UI-COORD.hpp - generic topological location addressing scheme within the UI
3 
4  Copyright (C)
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
14 
73 #ifndef STAGE_INTERACT_UI_COORD_H
74 #define STAGE_INTERACT_UI_COORD_H
75 
76 #include "lib/error.hpp"
77 #include "lib/symbol.hpp"
78 #include "lib/path-array.hpp"
79 #include "lib/nocopy.hpp"
80 #include "lib/util.hpp"
81 
82 #include <cstring>
83 #include <string>
84 #include <vector>
85 #include <utility>
86 
87 
88 namespace stage {
89 namespace interact {
90 
91  namespace error = lumiera::error;
92 
93  using std::string;
94  using lib::Literal;
95  using lib::Symbol;
96  using util::unConst;
97  using util::isnil;
98  using util::min;
99 
100  enum {
101  UIC_INLINE_SIZE = 8
102  };
103 
104  /* === predefined DSL symbols === */
105 
106  enum UIPathElm
107  {
108  UIC_WINDOW,
109  UIC_PERSP,
110  UIC_PANEL,
111  UIC_VIEW,
112  UIC_TAB,
113  UIC_PATH
114  };
115 
116 
117  extern const Symbol UIC_CURRENT_WINDOW;
118  extern const Symbol UIC_FIRST_WINDOW;
119  extern const Symbol UIC_ELIDED;
120 
121 
122 
129  class UICoord
130  : public lib::PathArray<UIC_INLINE_SIZE>
131  {
132 
133  public:
143  template<typename...ARGS>
144  explicit
145  UICoord (ARGS&& ...args) : PathArray(std::forward<ARGS> (args)...) { }
146 
147  UICoord (UICoord&&) = default;
148  UICoord (UICoord const&) = default;
149  UICoord (UICoord& o) : UICoord((UICoord const&)o) { }
150 
151  UICoord& operator= (UICoord const&) = default;
152  UICoord& operator= (UICoord &&) = default;
153 
154 
155 
156  /* === Builder API === */
157 
158  class Builder;
159 
161  UICoord (Builder&& builder);
162 
164  static Builder firstWindow();
165 
167  static Builder currentWindow();
168 
170  static Builder window (Literal windowID);
171 
172  //----- convenience shortcuts to start a copy-builder....
173  Builder persp (Literal perspectiveID) const;
174  Builder panel (Literal panelID)const;
175  Builder view (Literal viewID) const;
176  Builder tab (Literal tabID) const;
177  Builder tab (uint tabIdx) const;
178  Builder noTab () const;
179 
180  //----- convenience shortcuts to start mutation on a copy...
181  Builder path (Literal pathDefinition) const;
182  Builder append (Literal elmID) const;
183  Builder prepend (Literal elmID) const;
184  Builder rebuild() const;
185 
186 
187 
188  /* === named component access === */
189 
190  Literal getWindow() const { return accesComponent (UIC_WINDOW);}
191  Literal getPersp() const { return accesComponent (UIC_PERSP); }
192  Literal getPanel() const { return accesComponent (UIC_PANEL); }
193  Literal getView() const { return accesComponent (UIC_VIEW); }
194  Literal getTab() const { return accesComponent (UIC_TAB); }
195 
196 
197 
198  /* === query functions === */
199 
205  bool
206  isIncomplete() const
207  {
208  return not empty()
209  and isnil (getWindow());
210  }
211 
212  bool
213  isComplete() const
214  {
215  return not empty()
216  and not isnil (getWindow());
217  }
218 
219 
224  bool
225  isExplicit() const
226  {
227  return isComplete()
228  and not util::contains (*this, Symbol::ANY);
229  }
230 
231 
232  bool
233  isPresent (size_t idx) const
234  {
235  Literal* elm = unConst(this)->getPosition(idx);
236  return not isnil(elm)
237  and *elm != Symbol::ANY;
238  }
239 
240 
241  bool
242  isWildcard (size_t idx) const
243  {
244  Literal* elm = unConst(this)->getPosition(idx);
245  return elm
246  and *elm == Symbol::ANY;
247  }
248 
249 
261  bool
262  isExtendedBelow (UICoord const& parent) const
263  {
264  size_t subSiz = this->size(),
265  parSiz = parent.size(),
266  idx = 0;
267 
268  if (parSiz >= subSiz)
269  return false;
270 
271  while (idx < parSiz
272  and ( (*this)[idx]== parent[idx]
273  or Symbol::ANY == parent[idx]
274  or isnil (parent[idx])))
275  ++idx;
276 
277  ENSURE (idx < subSiz);
278  return idx == parSiz;
279  } // meaning: this goes further down
280 
281 
282 
283  /* === String representation === */
284 
285  explicit
286  operator string() const
287  {
288  if (isnil (*this))
289  return "UI:?";
290 
291  string component = getComp();
292  string path = getPath();
293 
294  if (isnil (component))
295  return "UI:?/" + path;
296 
297  if (isnil (path))
298  return "UI:" + component;
299  else
300  return "UI:" + component + "/" + path;
301  }
302 
303  string
304  getComp() const
305  {
306  if (empty()) return "";
307 
308  size_t end = min (size(), UIC_PATH);
309  size_t pos = indexOf (*begin());
310 
311  if (pos >= end)
312  return ""; // empty or path information only
313 
314  string buff;
315  buff.reserve(80);
316 
317  if (0 < pos) // incomplete UI-Coordinates (not anchored)
318  buff += "?";
319 
320  for ( ; pos<end; ++pos )
321  switch (pos) {
322  case UIC_WINDOW:
323  buff += getWindow();
324  break;
325  case UIC_PERSP:
326  buff += "["+getPersp()+"]";
327  break;
328  case UIC_PANEL:
329  buff += "-"+getPanel();
330  break;
331  case UIC_VIEW:
332  buff += "."+getView();
333  break;
334  case UIC_TAB:
335  if (UIC_ELIDED != getTab())
336  buff += "."+getTab();
337  break;
338  default:
339  NOTREACHED ("component index numbering broken");
340  }
341  return buff;
342  }
343 
344  string
345  getPath() const
346  {
347  size_t siz = size();
348  if (siz <= UIC_PATH)
349  return ""; // no path information
350 
351  string buff; // heuristic pre-allocation
352  buff.reserve (10 * (siz - UIC_PATH));
353 
354  iterator elm = pathSeq();
355  if (isnil (*elm))
356  { // irregular case : only a path fragment
357  elm = this->begin();
358  buff += "?/";
359  }
360 
361  for ( ; elm; ++elm )
362  buff += *elm + "/";
363 
364  // chop off last delimiter
365  size_t len = buff.length();
366  ASSERT (len >= 1);
367  buff.resize(len-1);
368  return buff;
369  }
370 
372  iterator
373  pathSeq() const
374  {
375  return size()<= UIC_PATH? end()
376  : iterator{this, unConst(this)->getPosition(UIC_PATH)};
377  }
378 
379 
380  private:
382  friend class Builder;
383 
384  size_t
385  findStartIdx() const
386  {
387  REQUIRE (not empty());
388  return indexOf (*begin());
389  }
390 
391  Literal
392  accesComponent (UIPathElm idx) const
393  {
394  Literal* elm = unConst(this)->getPosition(idx);
395  return elm? *elm : Symbol::EMPTY;
396  }
397 
398  void
399  setComponent (size_t idx, Literal newContent)
400  {
401  Literal* storage = expandPosition (idx);
402  setContent (storage, newContent);
403  }
404 
405 
413  void
414  setTailSequence (size_t idx, Literal newContent)
415  {
416  std::vector<Literal> elms;
417  if (not isnil (newContent))
418  {
419  if (not std::strchr (newContent, '/'))
420  {
421  // single element: just place it as-is
422  // and remove any further content behind
423  elms.emplace_back (newContent);
424  }
425  else
426  { // it is actually a sequence of elements,
427  // which need to be split first, and then
428  // interned into the global symbol table
429  string sequence{newContent};
430  size_t pos = 0;
431  size_t last = 0;
432  while (string::npos != (last = sequence.find ('/', pos)))
433  {
434  elms.emplace_back (Symbol{sequence.substr(pos, last - pos)});
435  pos = last + 1; // delimiter stripped
436  }
437  sequence = sequence.substr(pos);
438  if (not isnil (sequence))
439  elms.emplace_back (Symbol{sequence});
440  } }
441 
442  setTailSequence (idx, elms);
443  }
444 
453  void
454  setTailSequence (size_t idx, std::vector<Literal>& pathElms)
455  {
456  size_t cnt = pathElms.size();
457  expandPosition (idx + cnt); // preallocate
458  for (size_t i=0 ; i < cnt; ++i)
459  setContent (expandPosition(idx + i), pathElms[i]);
460  size_t end = size();
461  for (size_t i = idx+cnt; i<end; ++i)
462  setContent (expandPosition(i), nullptr);
463  }
464 
465 
466  public: /* ===== relational operators : equality and partial order ===== */
467 
468  friend bool
469  operator== (UICoord const& l, UICoord const& r)
470  {
471  return static_cast<PathArray const&> (l) == static_cast<PathArray const&> (r);
472  }
473 
474  friend bool
475  operator< (UICoord const& l, UICoord const& r)
476  {
477  return l.isExtendedBelow (r);
478  }
479 
480  friend bool operator> (UICoord const& l, UICoord const& r) { return (r < l); }
481  friend bool operator<= (UICoord const& l, UICoord const& r) { return (l < r) or (l == r); }
482  friend bool operator>= (UICoord const& l, UICoord const& r) { return (r < l) or (l == r); }
483  friend bool operator!= (UICoord const& l, UICoord const& r) { return not (l == r); }
484  };
485 
486 
487 
488 
489 
490  /* === Builder API === */
491 
492  class LocationClause;
493 
496  {
497  protected:
498  UICoord uic_;
499 
500  template<typename...ARGS>
501  explicit
502  Builder (ARGS&& ...args) : uic_{std::forward<ARGS> (args)...} { }
503  Builder (UICoord && anonRef) : uic_{std::move(anonRef)} { }
504  Builder (UICoord const& base) : uic_{base} { }
505 
507  friend class UICoord;
508 
509  public:
511  Builder (Builder &&) = default;
512 
513  size_t size() const { return uic_.size(); }
514  bool empty() const { return uic_.empty();}
515 
518  UICoord const&
520  {
521  return uic_;
522  }
523 
524 
525  /* == Builder functions == */
526 
529  Builder&&
530  window (Literal windowID)
531  {
532  uic_.setComponent (UIC_WINDOW, windowID);
533  return std::move (*this);
534  }
535 
537  Builder&&
538  persp (Literal perspectiveID)
539  {
540  uic_.setComponent (UIC_PERSP, perspectiveID);
541  return std::move (*this);
542  }
543 
545  Builder&&
546  panel (Literal panelID)
547  {
548  uic_.setComponent (UIC_PANEL, panelID);
549  return std::move (*this);
550  }
551 
553  Builder&&
554  view (Literal viewID)
555  {
556  uic_.setComponent (UIC_VIEW, viewID);
557  return std::move (*this);
558  }
559 
561  Builder&&
562  tab (Literal tabID)
563  {
564  uic_.setComponent (UIC_TAB, tabID);
565  return std::move (*this);
566  }
567 
569  Builder&&
570  tab (uint tabIdx)
571  {
572  uic_.setComponent (UIC_TAB, Symbol{"#"+util::toString (tabIdx)});
573  return std::move (*this);
574  }
575 
578  Builder&&
580  {
581  uic_.setComponent (UIC_TAB, UIC_ELIDED);
582  return std::move (*this);
583  }
584 
585 
590  Builder&&
592  {
593  if (not isnil(elm))
594  uic_.setTailSequence (uic_.size(), elm);
595  return std::move (*this);
596  }
597 
599  Builder&&
600  prepend (Literal elmID)
601  {
602  if (not uic_.isIncomplete())
603  throw error::Logic ("Attempt to prepend "+elmID
604  +" to the complete rooted path "+string(uic_));
605 
606  uic_.setComponent (uic_.findStartIdx() - 1, elmID);
607  return std::move (*this);
608  }
609 
615  Builder&&
616  path (Literal pathDef)
617  {
618  uic_.setTailSequence (UIC_PATH, pathDef);
619  return std::move (*this);
620  }
621 
623  Builder&&
624  truncateTo (size_t depth)
625  {
626  uic_.truncateTo (depth);
627  return std::move (*this);
628  }
629 
631  operator LocationClause();
632  LocationClause create();
633 
634  protected:
635  Builder&&
636  normalise()
637  {
638  uic_.normalise();
639  return std::move (*this);
640  }
641 
642  Builder&&
643  overwrite (size_t depth, Literal newSpec)
644  {
645  Literal* storage = uic_.expandPosition (depth);
646  uic_.setContent (storage, newSpec);
647  return std::move (*this);
648  }
649  };
650 
651 
652 
657  inline
659  : UICoord{std::move (builder.uic_)}
660  {
661  PathArray::normalise();
662  }
663 
664 
668  inline UICoord::Builder
670  {
671  return window (UIC_CURRENT_WINDOW);
672  }
673 
674  inline UICoord::Builder
676  {
677  return window (UIC_FIRST_WINDOW);
678  }
679 
681  inline UICoord::Builder
683  {
684  return Builder{windowID};
685  }
686 
687 
694  inline UICoord::Builder
695  UICoord::persp (Literal perspectiveID) const
696  {
697  return Builder(*this).persp (perspectiveID);
698  }
699 
700  inline UICoord::Builder
701  UICoord::panel (Literal panelID) const
702  {
703  return Builder(*this).panel (panelID);
704  }
705 
706  inline UICoord::Builder
707  UICoord::view (Literal viewID) const
708  {
709  return Builder(*this).view (viewID);
710  }
711 
712  inline UICoord::Builder
713  UICoord::tab (Literal tabID) const
714  {
715  return Builder(*this).tab (tabID);
716  }
717 
718  inline UICoord::Builder
719  UICoord::tab (uint tabIdx) const
720  {
721  return Builder(*this).tab (tabIdx);
722  }
723 
724  inline UICoord::Builder
725  UICoord::noTab () const
726  {
727  return Builder(*this).noTab();
728  }
729 
735  inline UICoord::Builder
736  UICoord::path (Literal pathDefinition) const
737  {
738  return Builder(*this).path (pathDefinition);
739  }
740 
741  inline UICoord::Builder
742  UICoord::append (Literal elmID) const
743  {
744  return Builder(*this).append (elmID);
745  }
746 
747  inline UICoord::Builder
748  UICoord::prepend (Literal elmID) const
749  {
750  return Builder(*this).prepend (elmID);
751  }
752 
753  inline UICoord::Builder
754  UICoord::rebuild () const
755  {
756  return Builder(*this);
757  }
758 
759 
760 
761 }}// namespace stage::interact
762 #endif /*STAGE_INTERACT_UI_COORD_H*/
bool isExplicit() const
Definition: ui-coord.hpp:225
Literal * expandPosition(size_t idx)
Definition: path-array.hpp:447
Builder && truncateTo(size_t depth)
possibly shorten this path specification to a limited depth
Definition: ui-coord.hpp:624
Describe a location within the UI through structural/topological coordinates.
Definition: ui-coord.hpp:129
Builder && path(Literal pathDef)
augment UI coordinates to define a complete local path
Definition: ui-coord.hpp:616
Builder persp(Literal perspectiveID) const
Definition: ui-coord.hpp:695
void setTailSequence(size_t idx, std::vector< Literal > &pathElms)
replace the existing path information with the given elements
Definition: ui-coord.hpp:454
Builder && window(Literal windowID)
change UI coordinate spec to define it to be rooted within the given window
Definition: ui-coord.hpp:530
UICoord(ARGS &&...args)
UI-Coordinates can be created explicitly by specifying a sequence of Literal tokens, which will be used to initialise and then normalise the underlying PathArray.
Definition: ui-coord.hpp:145
Builder path(Literal pathDefinition) const
convenience builder function so set a full path definition
Definition: ui-coord.hpp:736
Builder && persp(Literal perspectiveID)
augment UI coordinates to mandate a specific perspective to be active within the window ...
Definition: ui-coord.hpp:538
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
STL namespace.
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:49
Builder && append(Literal elm)
augment UI coordinates by appending a further component at the end.
Definition: ui-coord.hpp:591
const Symbol UIC_ELIDED
indicate that a component is elided or irrelevant here
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Builder && tab(Literal tabID)
augment UI coordinates to indicate a specific tab within the view"
Definition: ui-coord.hpp:562
Abstraction for path-like topological coordinates.
Definition: path-array.hpp:225
Token or Atom with distinct identity.
Definition: symbol.hpp:117
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A single location specification to be matched and fulfilled.
Builder && panel(Literal panelID)
augment UI coordinates to indicate a specific view to be used
Definition: ui-coord.hpp:546
Marker types to indicate a literal string and a Symbol.
void normalise()
establish the contract of PathArray
Definition: path-array.hpp:492
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Builder && noTab()
augment UI coordinates to indicate that no tab specification is necessary
Definition: ui-coord.hpp:579
const Symbol UIC_CURRENT_WINDOW
window spec to refer to the current window
iterator pathSeq() const
iterative access to the path sequence section
Definition: ui-coord.hpp:373
PathArray(IndexSeq< prefix... >, IndexSeq< rest... >, ARGS &&...args)
Definition: path-array.hpp:245
bool isExtendedBelow(UICoord const &parent) const
Check if this coordinate spec can be seen as an extension of the given parent coordinates and thus re...
Definition: ui-coord.hpp:262
Lumiera error handling (C++ interface).
Builder && view(Literal viewID)
augment UI coordinates to indicate a specific view to be used
Definition: ui-coord.hpp:554
static Builder window(Literal windowID)
Builder: start definition of UI-Coordinates rooted in given window.
Definition: ui-coord.hpp:682
bool isIncomplete() const
Definition: ui-coord.hpp:206
void setContent(Literal *pos, const char *val)
Definition: path-array.hpp:460
Builder && prepend(Literal elmID)
augment partially defined UI coordinates by extending them towards the root
Definition: ui-coord.hpp:600
Foundation abstraction to implement path-like component sequences.
const Symbol UIC_FIRST_WINDOW
window spec to refer to the first window of the application
size_t indexOf(Literal const &content) const
reverse lookup of actual path content
Definition: path-array.hpp:331
void setTailSequence(size_t idx, Literal newContent)
replace / overwrite existing content starting at given index.
Definition: ui-coord.hpp:414
static Builder firstWindow()
Builder: start definition of UI-Coordinates rooted in the firstWindow
Definition: ui-coord.hpp:675
Adapter for building an implementation of the »Lumiera Forward Iterator« concept. ...
static Builder currentWindow()
Builder: start definition of UI-Coordinates rooted in the currentWindow
Definition: ui-coord.hpp:669
Builder && tab(uint tabIdx)
augment UI coordinates to indicate a tab specified by index number
Definition: ui-coord.hpp:570