2017-10-16 02:39:22 +02:00
/*
GEN - NODE - LOCATION - QUERY . hpp - pose UI - coordinate location queries against a GenNode structure
Copyright ( C ) Lumiera . org
2017 , Hermann Vosseler < Ichthyostega @ web . de >
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of
the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/** @file gen-node-location-query.hpp
* * Implementation of the gui : : interact : : LocationQuery interface to work on a GenNode tree .
* * The interface allows to pose queries against a concrete structure to verify and reshape some
* * [ UI Coordinate specification ] ( \ ref UICoord ) ; basically it offers methods to navigate within a
* * tree - like structure . While in the actual implementation , such a query interface would be backed
* * by navigating real UI structures , the implementation given here instead uses a generic tree structure
* * given as ` Record < GenNode > ` .
* *
2017-10-23 02:16:57 +02:00
* * # Representing UI structure as GenNode tree
* *
* * While basically the interface LocationQuery abstracts and reduces the structure of an UI into
* * just some hierarchically arranged and nested IDs , we should note some specific twists how a GenNode
* * tree is used here to represent the structure elements as defined through [ UI coordinates ] ( \ ref UICoord ) :
* * - we use the special _type_ attribute to represent the _perspective_ within each window ;
* * deliberately , we ' ll use this twisted structure here to highlight the fact that the
* * backing structure need not be homogeneous ; rather , it may require explicit branching
* * - we use the _attributes_ within the GenNode " object " representation , since these are _named_
* * nested elements , and the whole notion of an UI coordinate path is based on named child components
* * - relying upon the [ object builder notation ] ( \ ref Record : : Mutator ) , it is possible to define a whole
* * structure as nested inline tree ; this leads to a somewhat confusing notation , where the names of
* * the child nodes are spelled of at the closing bracket of each construct .
* * - since GenNodeLocationQuery is conceived for writing test and verification code , there is a
* * special convention to set the ` currentWindow ` to be the last one in list - - in a real UI
* * this would not of course not be a configurable property of the LocationQuery , and rather
* * just reflect the transient window state and return the currently activated window
* *
2017-10-16 02:39:22 +02:00
* * @ todo WIP 10 / 2017 started in the effort of shaping the LoactionQuery interface , and used
* * to support writing unit tests , to verify the UiCoordResolver . It remains to be seen
* * if this implementation can be used beyond this limited purpose
* *
* * @ see UICoordResolver_test
* * @ see ui - coord - resolver . hpp
* * @ see navigator . hpp
*/
# ifndef GUI_INTERACT_GEN_NODE_LOCATION_QUERY_H
# define GUI_INTERACT_GEN_NODE_LOCATION_QUERY_H
# include "lib/error.hpp"
//#include "lib/symbol.hpp"
# include "gui/interact/ui-coord-resolver.hpp"
# include "lib/diff/gen-node.hpp"
2017-10-23 04:05:44 +02:00
# include "lib/format-string.hpp"
2017-10-23 02:16:57 +02:00
# include "lib/itertools.hpp"
# include "lib/util.hpp"
2017-10-16 02:39:22 +02:00
//#include <boost/noncopyable.hpp>
//#include <string>
//#include <vector>
//#include <utility>
//#include <memory>
namespace gui {
namespace interact {
namespace error = lumiera : : error ;
// using std::unique_ptr;
// using std::string;
// using lib::Literal;
// using lib::Symbol;
using lib : : diff : : Rec ;
2017-10-23 04:05:44 +02:00
using util : : _Fmt ;
2017-10-16 02:39:22 +02:00
// using util::unConst;
// using util::isnil;
// using util::min;
/**
2017-10-21 23:37:04 +02:00
* Test / Diagnostics : implementation of the LocationQuery - API
* based on a abstract topological structure given as Record < GenNode > ( " GenNode tree " ) .
* @ remark intended for verifying path resolution and navigation through unit tests
2017-10-16 02:39:22 +02:00
* @ todo initial draft as of 10 / 2017
*/
class GenNodeLocationQuery
: public LocationQuery
{
2017-10-23 02:16:57 +02:00
Rec const & tree_ ;
2017-10-16 02:39:22 +02:00
public :
GenNodeLocationQuery ( Rec const & backingStructure )
2017-10-23 02:16:57 +02:00
: tree_ ( backingStructure )
{ }
2017-10-16 02:39:22 +02:00
/* === LocationQuery interface === */
2017-10-21 23:37:04 +02:00
/** resolve Anchor against GenNode tree */
virtual Literal
determineAnchor ( UICoord const & path )
{
2017-10-23 02:16:57 +02:00
if ( isnil ( tree_ ) or not path . isPresent ( UIC_WINDOW ) )
return Symbol : : BOTTOM ;
if ( UIC_FIRST_WINDOW = = path . getWindow ( ) )
return getFirstWindow ( ) ;
if ( UIC_CURRENT_WINDOW = = path . getWindow ( ) )
return getCurrentWindow ( ) ;
2017-10-23 04:05:44 +02:00
if ( not tree_ . hasAttribute ( string { path . getWindow ( ) } ) ) /////////////////////////////////////TICKET #1113 : unnecessary repackaging of a Literal into string when GenNode ID is based on Literal
return Symbol : : BOTTOM ;
2017-10-23 02:16:57 +02:00
return path . getWindow ( ) ;
2017-10-21 23:37:04 +02:00
}
2017-10-23 03:17:18 +02:00
/** evaluate to what extent a UIcoord spec matches the structure given as GenNode tree */
2017-10-21 23:37:04 +02:00
virtual size_t
determineCoverage ( UICoord const & path )
{
2017-10-23 03:17:18 +02:00
size_t depth = 0 ;
2017-10-23 04:05:44 +02:00
drillDown ( tree_ , path , path . size ( ) , depth ) ;
2017-10-23 03:17:18 +02:00
return depth ;
2017-10-21 23:37:04 +02:00
}
2017-10-23 03:17:18 +02:00
/** get the sequence of child IDs at a designated position in the backing GenNode tree */
2017-10-21 23:37:04 +02:00
virtual ChildIter
getChildren ( UICoord const & path , size_t pos )
{
2017-10-23 04:05:44 +02:00
size_t depth = 0 ;
Rec const & node = drillDown ( tree_ , path , pos , depth ) ;
if ( pos ! = depth - 1 )
throw error : : State ( _Fmt { " unable to drill down to depth %d: "
" element %s at pos %d in path %s is in "
" contradiction to actual UI structure " }
% pos
% ( depth < path . size ( ) ? path [ depth ] : Symbol : : BOTTOM )
% depth
% path
) ;
return childSequence ( node ) ;
2017-10-21 23:37:04 +02:00
}
2017-10-16 02:39:22 +02:00
private :
2017-10-23 02:16:57 +02:00
Literal
getFirstWindow ( )
{
return Symbol { * tree_ . keys ( ) } ; //////////////////////////////////////////////TICKET #1113 : warning use of Symbol table becomes obsolete when EntryID relies on Literal
}
Literal
getCurrentWindow ( )
{
return Symbol { lib : : pull_last ( tree_ . keys ( ) ) } ; //////////////////////////////////////////////TICKET #1113 : warning use of Symbol table becomes obsolete when EntryID relies on Literal
}
2017-10-23 03:17:18 +02:00
2017-10-23 04:05:44 +02:00
static ChildIter
childSequence ( Rec const & node )
{
auto internedString = [ ] ( string const & id ) - > Literal
{
return Symbol { id } ;
} ;
///////////////////////////////////////////////////////////////TICKET #1113 : could just use lib::wrapIter when GenNode exposes Literal as ID
return lib : : transform ( node . keys ( ) , internedString ) ;
}
2017-10-23 03:17:18 +02:00
static Rec const &
drillDown ( Rec const & tree , UICoord const & path , size_t maxDepth , size_t & depth )
{
if ( depth < maxDepth and path . isPresent ( depth ) )
{
const char * pathElm = path [ depth ] ;
if ( tree . hasAttribute ( pathElm ) )
return drillDown ( tree . get ( pathElm ) . data . get < Rec > ( ) , path , maxDepth , + + depth ) ;
}
return tree ;
}
2017-10-16 02:39:22 +02:00
} ;
} } // namespace gui::interact
# endif /*GUI_INTERACT_GEN_NODE_LOCATION_QUERY_H*/