1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module w4d.layout.base;
8 import w4d.layout.placer.base,
9        w4d.layout.exception,
10        w4d.style.widget;
11 import gl3n.linalg;
12 import std.algorithm;
13 
14 enum LayoutState
15 {
16     None,
17     Dirty,
18     Clean,
19 }
20 
21 /// A baseclass of Layout object.
22 /// Layout object decides children position to place
23 /// and calculates all styles.
24 abstract class Layout
25 {
26     protected Placer _placer;
27 
28     /// Placer of the layout.
29     inout @property inout(Placer) placer () { return _placer; }
30 
31     protected Layoutable _owner;
32     /// Owner of the layout.
33     inout @property owner () { return _owner; }
34 
35     protected LayoutState _status;
36 
37     protected vec2 _beforeBasePos;
38     protected vec2 _beforeParentSize;
39 
40     protected @property children ()
41     {
42         return _owner.childLayoutables;
43     }
44     protected @property style ()
45     {
46         return _owner.style;
47     }
48 
49     ///
50     this ( Placer placer, Layoutable owner )
51     {
52         enforce( owner, "Owner is null." );
53         _placer = placer;
54         _owner  = owner;
55 
56         _status           = LayoutState.None;
57         _beforeBasePos    = vec2(0,0);
58         _beforeParentSize = vec2(-1,-1);
59     }
60 
61     /// Moves the owner and the children.
62     void shift ( vec2 size )
63     {
64         if ( size == vec2(0,0) ) return;
65 
66         _beforeBasePos += size;
67         style.shift( size );
68         children.each!( x => x.shift( size ) );
69     }
70 
71     protected bool updateDirtyState ()
72     {
73         auto dirty = false;
74         foreach ( child; children ) {
75             if ( child.layoutObject.updateDirtyState() ) {
76                 dirty = true;
77             }
78         }
79         if ( !dirty ) {
80             dirty = _owner.checkNeedLayout( false );
81         }
82         _status = dirty? LayoutState.Dirty: LayoutState.Clean;
83         return dirty;
84     }
85 
86     /// Just moves the owner and the children to pos
87     /// if no need to layout completely.
88     /// Returns: Whether the owner is placed easily.
89     protected bool placeEasily ( vec2 basepos, vec2 parentSize )
90     {
91         scope(exit) {
92             _status           = LayoutState.None;
93             _beforeBasePos    = basepos;
94             _beforeParentSize = parentSize;
95         }
96         if ( _status == LayoutState.None ) {
97             updateDirtyState();
98         }
99 
100         auto dirty = (_status != LayoutState.Clean);
101         if ( _beforeParentSize != parentSize || dirty ) {
102             return false;
103         }
104 
105         shift( basepos - _beforeBasePos );
106         return true;
107     }
108 
109     /// Calculates styles and decodes position of children.
110     void place ( vec2, vec2 );
111 }
112 
113 /// An interface of layoutable objects.
114 interface Layoutable : PlacerOwner
115 {
116     /// Layout object.
117     inout @property inout(Layout) layoutObject ();
118 
119     /// Child layoutables.
120     @property Layoutable[] childLayoutables ();
121 
122     /// Wanted size.
123     @property vec2 wantedSize ();
124 
125     /// Checks if need to layout completely.
126     /// Set the first argument true to check recursively.
127     bool checkNeedLayout ( bool );
128 
129     /// Moves the owner and the children.
130     void shift ( vec2 );
131 }