Lumiera  0.pre.03
»edit your freedom«
ui-coord-resolver.hpp
Go to the documentation of this file.
1 /*
2  UI-COORD-RESOLVER.hpp - resolve UI coordinate spec against actual window topology
3 
4  Copyright (C) Lumiera.org
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
115 #ifndef STAGE_INTERACT_UI_COORD_RESOLVER_H
116 #define STAGE_INTERACT_UI_COORD_RESOLVER_H
117 
118 #include "lib/error.hpp"
119 #include "lib/symbol.hpp"
120 #include "lib/format-string.hpp"
122 #include "lib/iter-explorer.hpp"
123 #include "lib/iter-source.hpp"
124 #include "lib/depend.hpp"
125 #include "lib/util.hpp"
126 
127 #include <utility>
128 #include <memory>
129 
130 
131 namespace stage {
132 namespace interact {
133 
134  namespace error = lumiera::error;
135 
136  using std::unique_ptr;
137  using util::unConst;
138  using lib::Literal;
139  using lib::Symbol;
140 
141 
142 
143 
150  : public lib::IterSource<Literal>
151  {
152  public:
153  virtual ~TreeStructureNavigator();
154 
167  virtual TreeStructureNavigator* expandChildren() const =0;
168 
169 
177  static auto
179  {
180  return lib::explore (source)
181  .expand([](TreeStructureNavigator& parent){ return parent.expandChildren(); });
182  }
183  };
184 
185 
186 
187 
188 
201  {
202  public:
203  virtual ~LocationQuery();
204 
207 
208 
209  using ChildIter = decltype (TreeStructureNavigator::buildIterator(0));
210 
211 
223  virtual Literal determineAnchor (UICoord const& path) =0;
224 
225 
235  virtual size_t determineCoverage (UICoord const& path) =0;
236 
248  virtual ChildIter getChildren (UICoord const& path, size_t pos) =0;
249  };
250 
251 
252 
253 
254 
255 
286  : public UICoord::Builder
287  {
288 
289  struct Resolution
290  {
291  const char* anchor = nullptr;
292  size_t depth = 0;
293  unique_ptr<UICoord> covfefe{};
294  bool isResolved = false;
295  };
296 
297  LocationQuery& query_;
298  Resolution res_;
299 
300  public:
301  UICoordResolver (UICoord const& uic, LocationQuery& queryAPI)
302  : Builder{uic}
303  , query_{queryAPI}
304  , res_{}
305  {
306  attempt_trivialResolution();
307  }
308 
309  UICoordResolver (UICoord && uic, LocationQuery& queryAPI)
310  : Builder{std::move(uic)}
311  , query_{queryAPI}
312  , res_{}
313  {
314  attempt_trivialResolution();
315  }
316 
317 
318  /* === query functions === */
319 
323  bool
324  isAnchored() const
325  {
326  return res_.anchor
327  and res_.anchor != Symbol::BOTTOM;
328  }
329 
337  bool
338  canAnchor() const
339  {
340  return isAnchored()
341  or (res_.isResolved and res_.covfefe)
342  or unConst(this)->pathResolution()
343  or isAnchored(); // resolution failed, but computed at least an anchor
344  }
345 
352  bool
354  {
355  return res_.isResolved
356  and res_.depth > 0;
357  }
358 
363  bool
364  isCovered() const
365  {
366  return res_.isResolved
367  and res_.depth == this->uic_.size();
368  }
369 
371  bool
373  {
374  return isCovered();
375  }
376 
377 
387  bool
388  canCover() const
389  {
390  return isCovered() // either explicit coverage known
391  or (res_.isResolved and res_.covfefe) // or previous matching run found (partial) solution
392  or unConst(this)->pathResolution() // perform matching run now to find total coverage
393  or (res_.covfefe); // or at least partial coverage was found
394  }
395 
396 
397 
398  /* === mutation functions === */
399 
410  {
411  if (isCoveredPartially() and not res_.covfefe)
412  {
413  ASSERT (res_.anchor); // depth > 0 implies anchorage
414  window (res_.anchor); // thus make this anchor explicit
415  truncateTo (res_.depth);
416  }
417  else if (canCover())
418  {
419  ASSERT (res_.isResolved);
420  REQUIRE (res_.covfefe);
421  res_.depth = res_.covfefe->size();
422  this->uic_ = std::move (*res_.covfefe);
423  res_.covfefe.reset();
424  }
425  else
426  {
427  ASSERT (res_.isResolved);
428  REQUIRE (res_.depth == 0);
429  REQUIRE (not res_.covfefe);
430  truncateTo (0);
431  }
432  ENSURE (isCovered());
433  return std::move (*this);
434  }
435 
436 
445  {
446  if (isCoveredPartially() and not res_.covfefe)
447  {
448  ASSERT (res_.anchor);
449  window (res_.anchor); // just ensure the anchor info is explicit,
450  } // the rest is already in place and explicit
451  else if (canCover())
452  {
453  ASSERT (res_.isResolved);
454  REQUIRE (res_.covfefe);
455  REQUIRE (uic_.size() >= res_.covfefe->size());
456  res_.depth = res_.covfefe->size();
457  // possibly overwrite placeholders by explicitly resolved info...
458  for (size_t pos = 0; pos < res_.depth; ++pos )
459  overwrite (pos, (*res_.covfefe)[pos]);
460  res_.covfefe.reset();
461  }
462  else
463  {
464  ASSERT (res_.isResolved);
465  REQUIRE (res_.depth == 0);
466  REQUIRE (not res_.covfefe);
467  truncateTo (0);
468  }
469  ENSURE (empty() or (isCoveredPartially() and uic_.isExplicit()));
470  return std::move (*this); // no wildcards remain
471  }
472 
473 
485  {
486  if (canAnchor())
487  {
488  window (res_.anchor);
489  normalise();
490  }
491  return std::move (*this);
492  }
493 
494 
495 
502  extend (Literal pathExtension)
503  {
504  if (not isCovered())
505  cover();
506  ENSURE (isCovered());
507  append (pathExtension);
508  res_.depth = query_.determineCoverage (this->uic_); // coverage may grow
509  return std::move (*this);
510  }
511 
519  extend (UICoord const& partialExtensionSpec)
520  {
521  if (not canCover())
522  uic_ = partialExtensionSpec;
523  else
524  {
525  REQUIRE (res_.isResolved);
526  size_t coverable = res_.covfefe? res_.covfefe->size() : res_.depth;
527  auto newContent = partialExtensionSpec.begin();
528  size_t extensionPos = newContent? partialExtensionSpec.indexOf(*newContent) : coverable;
529  if (coverable > extensionPos)
530  throw error::Invalid (util::_Fmt{"Attempt to extend covered path %s with %s "
531  "would overwrite positions %d to %d (incl)"}
532  % (res_.covfefe? *res_.covfefe : UICoord{uic_.rebuild().truncateTo(res_.depth)})
533  % partialExtensionSpec
534  % extensionPos
535  % (coverable-1));
536  cover();
537  for ( ; newContent; ++newContent, ++extensionPos )
538  overwrite (extensionPos, *newContent);
539  normalise();
540  }
541  res_ = Resolution{}; // start over with pristine resolution state
542  attempt_trivialResolution();
543  canCover();
544  return std::move (*this);
545  }
546 
547 
559  {
560  if (pos < uic_.size() and not uic_.isPresent(pos))
561  overwrite (pos, UIC_ELIDED);
562  return std::move (*this);
563  }
564 
565 
566 
568  operator string() const { return string(this->uic_); }
569  size_t coverDepth() const { return res_.depth; }
570 
571 
572 
573  private:
581  void
583  {
584  res_.anchor = query_.determineAnchor (this->uic_);
585  if (not uic_.isExplicit()) return;
586  res_.depth = query_.determineCoverage(this->uic_);
587  if (util::contains (this->uic_, UIC_ELIDED)) return; // existentially quantified
588  res_.isResolved = true;
589  }
590 
595  bool pathResolution();
596  };
597 
598 
599 
600 }}// namespace stage::interact
601 #endif /*STAGE_INTERACT_UI_COORD_RESOLVER_H*/
Builder && truncateTo(size_t depth)
possibly shorten this path specification to a limited depth
Definition: ui-coord.hpp:633
Describe a location within the UI through structural/topological coordinates.
Definition: ui-coord.hpp:138
UICoordResolver && cover()
mutate the path to get it totally covered
virtual TreeStructureNavigator * expandChildren() const =0
expand into exploration of child elements at "current position".
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
bool canAnchor() const
determine if a mutation is possible to anchor the path explicitly
UICoordResolver && existentiallyQuantify(size_t pos)
mutate to turn a wildcard into existentially quantified. This means to assume (or require) that an el...
bool isAnchored() const
is this path explicitly anchored at an existing window?
virtual Literal determineAnchor(UICoord const &path)=0
make the real anchor point explicit.
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:75
Front-end for printf-style string template interpolation.
A front-end for using printf-style formatting.
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:289
const Symbol UIC_ELIDED
indicate that a component is elided or irrelevant here
UICoordResolver && extend(UICoord const &partialExtensionSpec)
mutate the path and extend it with components at fixed positions
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:196
Token or Atom with distinct identity.
Definition: symbol.hpp:116
Iteration source interface to abstract a data source, which then can be accessed through IterAdapter ...
Definition: iter-source.hpp:88
bool isCovered() const
this path is completely covered by the currently existing UI structure;
Marker types to indicate a literal string and a Symbol.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:46
iterator begin() const
Definition: path-array.hpp:406
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
UICoordResolver && anchor()
mutate the window part of the path such as to make the anchorage explicit, if possible ...
A topological addressing scheme to designate structural locations within the UI.
Interface to locate and move within a tree shaped structure.
Query and mutate UICoord specifications in relation to actual UI topology.
bool isCoveredPartially() const
is this path at least partially covered? A covered path describes an access path through widgets actu...
bool canCover() const
determine if a mutation is possible to get the path (partially) covered.
Singleton services and Dependency Injection.
Interface to discover a backing structure for the purpose of path navigation and resolution.
Lumiera error handling (C++ interface).
static auto buildIterator(TreeStructureNavigator *source)
build a Lumiera Forward Iterator as front-end and managing Handle for a TreeStructureNavigator or sub...
static lib::Depend< LocationQuery > service
access point to global LocationQuery service implementation
void attempt_trivialResolution()
establish a trivial anchorage and coverage, if possible.
UICoordResolver && extend(Literal pathExtension)
mutate the path to extend it while keeping it partially covered
virtual ~TreeStructureNavigator()
this is an interface
bool isCoveredTotally() const
synonymous to isCovered()
Building tree expanding and backtracking evaluations within hierarchical scopes.
Extension module to build an opaque data source, accessible as Lumiera Forward Iterator.
size_t indexOf(Literal const &content) const
reverse lookup of actual path content
Definition: path-array.hpp:357
UICoordResolver && coverPartially()
mutate the path by resolving all wildcards to achieve partial coverage
virtual size_t determineCoverage(UICoord const &path)=0
evaluate to what extent a UIcoord spec matches the actual UI