1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module w4d.widget.mdi.window;
8 import w4d.event;
9 
10 /// An enum of rectangle sides.
11 enum Side
12 {
13     None   = 0b0000,
14     Left   = 0b0001,
15     Top    = 0b0010,
16     Right  = 0b0100,
17     Bottom = 0b1000,
18 }
19 
20 /// A handler that handles closing MdiClient.
21 alias MdiClientCloseHandler = EventHandler!( bool );
22 
23 /// A template that declares methods related to operating Window.
24 template WindowOperations ()
25 {
26     protected uint _draggingSides;
27 
28     protected void enableSideDragging ( vec2 pos )
29     {
30         const lt = style.translate +
31             style.box.borderInsideLeftTop;
32         const ltsz = vec2( style.box.paddings.left.calced,
33                 style.box.paddings.top.calced );
34 
35         const rb = lt + ltsz + style.box.clientSize;
36         const rbsz = vec2( style.box.paddings.right.calced,
37                 style.box.paddings.bottom.calced );
38 
39         _draggingSides = Side.None;
40         if ( pos.x > lt.x && pos.x < lt.x+ltsz.x ) {
41             _draggingSides |= Side.Left;
42         }
43         if ( pos.y > lt.y && pos.y < lt.y+ltsz.y ) {
44             _draggingSides |= Side.Top;
45         }
46         if ( pos.x > rb.x && pos.x < rb.x+rbsz.x ) {
47             _draggingSides |= Side.Right;
48         }
49         if ( pos.y > rb.y && pos.y < rb.y+rbsz.y ) {
50             _draggingSides |= Side.Bottom;
51         }
52     }
53 
54     protected void resizeWithDragging ( vec2 cur )
55     {
56         const sides = _draggingSides;
57         auto   size = _size, pos = _pos;
58 
59         if ( sides & Side.Left ) {
60             size.x += pos.x-cur.x;
61             pos.x   = cur.x;
62         }
63         if ( sides & Side.Top ) {
64             size.y += pos.y-cur.y;
65             pos.y   = cur.y;
66         }
67         if ( sides & Side.Right ) {
68             size.x = cur.x - pos.x;
69         }
70         if ( sides & Side.Bottom ) {
71             size.y = cur.y - pos.y;
72         }
73         move  ( pos );
74         resize( size );
75     }
76 
77 
78     protected vec2 _minSize;
79     @property minSize () { return _minSize; }
80 
81     protected vec2 _maxSize;
82     @property maxSize () { return _maxSize; }
83 
84     void limitSize ( vec2 min, vec2 max )
85     {
86         _minSize = min;
87         _maxSize = max;
88         resize( size );
89     }
90 
91 
92     protected vec2 _pos;
93     @property vec2 pos () { return _pos; }
94 
95     protected vec2 _size;
96     @property vec2 size () { return _size; }
97 
98     void move ( vec2 pos )
99     {
100         _pos = pos;
101         requestLayout();
102     }
103     void resize ( vec2 size )
104     {
105         _size = size;
106         requestLayout();
107     }
108 
109     MdiClientCloseHandler onClose; // Return true to abort closing.
110     void close ()
111     {
112         enforce( _host, "Host is not defined yet." );
113         if ( !onClose.call() ) {
114             _host.removeClient( this );
115         }
116     }
117 
118 
119     override @property const(Cursor) cursor ()
120     {
121         return
122             _draggingSides & Side.Left  ? Cursor.HResize:
123             _draggingSides & Side.Right ? Cursor.HResize:
124             _draggingSides & Side.Top   ? Cursor.VResize:
125             _draggingSides & Side.Bottom? Cursor.VResize:
126             super.cursor;
127     }
128 
129     override bool handleMouseMove ( vec2 cur )
130     {
131         if ( super.handleMouseMove( cur ) ) return true;
132 
133         if ( isTracked ) {
134             cur -= _host.style.clientLeftTop;
135             resizeWithDragging( cur );
136             return true;
137         }
138 
139         enableSideDragging( cur );
140         return true;
141     }
142 
143     override bool handleMouseButton ( MouseButton btn, bool status, vec2 pos )
144     {
145         enforce( _host, "MdiHost is null." );
146         _host.focusClient( this );
147 
148         if ( super.handleMouseButton(btn,status,pos) ) return true;
149         return false;
150     }
151 }