1 /** DGui project file.
2 
3 Copyright: Trogu Antonio Davide 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Trogu Antonio Davide
8 */
9 
10 module dguihub.layout.splitpanel;
11 
12 import dguihub.core.events.event;
13 import dguihub.core.events.eventargs;
14 import dguihub.layout.layoutcontrol;
15 import dguihub.layout.panel;
16 
17 enum SplitOrientation {
18    vertical = 1,
19    horizontal = 2,
20 }
21 
22 class SplitPanel : LayoutControl {
23    private enum int splitterSize = 8;
24 
25    private SplitOrientation _splitOrientation = SplitOrientation.vertical;
26    private bool _downing = false;
27    private int _splitPos = 0;
28    private Panel _panel1;
29    private Panel _panel2;
30 
31    public this() {
32       this._panel1 = new Panel();
33       this._panel1.parent = this;
34 
35       this._panel2 = new Panel();
36       this._panel2.parent = this;
37    }
38 
39    @property public void splitPosition(int sp) {
40       this._splitPos = sp;
41 
42       if (this.created) {
43          this.updateLayout();
44       }
45    }
46 
47    @property public Panel panel1() {
48       return this._panel1;
49    }
50 
51    @property public Panel panel2() {
52       return this._panel2;
53    }
54 
55    @property SplitOrientation splitOrientation() {
56       return this._splitOrientation;
57    }
58 
59    @property void splitOrientation(SplitOrientation so) {
60       this._splitOrientation = so;
61    }
62 
63    public override void updateLayout() {
64       scope ResizeManager rm = new ResizeManager(2); //Fixed Panel
65 
66       bool changed = false;
67 
68       switch (this._splitOrientation) {
69       case SplitOrientation.vertical: {
70             if (this._splitPos >= 0 && (this._splitPos + splitterSize) < this.width) {
71                rm.setSize(this._panel1, this._splitPos, this.height);
72                rm.resizeControl(this._panel2, this._splitPos + splitterSize, 0,
73                      this.width - (this._splitPos + splitterSize), this.height);
74                changed = true;
75             }
76          }
77          break;
78 
79       default: // SplitOrientation.horizontal
80       {
81             if (this._splitPos >= 0 && (this._splitPos + splitterSize) < this.height) {
82                rm.setSize(this._panel1, this.width, this._splitPos);
83                rm.resizeControl(this._panel2, 0, this._splitPos + splitterSize,
84                      this.width, this.height - (this._splitPos + splitterSize));
85                changed = true;
86             }
87          }
88          break;
89       }
90 
91       if (changed) {
92          this.invalidate();
93       }
94    }
95 
96    protected override void onMouseKeyDown(MouseEventArgs e) {
97       if (e.keys == MouseKeys.left) {
98          this._downing = true;
99          SetCapture(this._handle);
100       }
101 
102       super.onMouseKeyDown(e);
103    }
104 
105    protected override void onMouseKeyUp(MouseEventArgs e) {
106       if (this._downing) {
107          this._downing = false;
108          ReleaseCapture();
109       }
110 
111       super.onMouseKeyUp(e);
112    }
113 
114    protected override void onMouseMove(MouseEventArgs e) {
115       if (this._downing) {
116          Point pt = Cursor.position;
117          convertPoint(pt, null, this);
118 
119          switch (this._splitOrientation) {
120          case SplitOrientation.vertical:
121             this._splitPos = pt.x;
122             break;
123 
124          default: // SplitOrientation.horizontal
125             this._splitPos = pt.y;
126             break;
127          }
128 
129          this.updateLayout();
130       }
131 
132       super.onMouseMove(e);
133    }
134 
135    protected override void createControlParams(ref CreateControlParams ccp) {
136       ccp.className = WC_DSPLITPANEL;
137 
138       switch (this._splitOrientation) {
139       case SplitOrientation.vertical:
140          ccp.defaultCursor = SystemCursors.sizeWE;
141          break;
142 
143       default: // SplitOrientation.horizontal
144          ccp.defaultCursor = SystemCursors.sizeNS;
145          break;
146       }
147 
148       if (!this._splitPos) {
149          switch (this._splitOrientation) {
150          case SplitOrientation.vertical:
151             this._splitPos = this.width / 3;
152             break;
153 
154          default: // SplitOrientation.horizontal
155             this._splitPos = this.height - (this.height / 3);
156             break;
157          }
158       }
159 
160       super.createControlParams(ccp);
161    }
162 
163    protected override void onDGuiMessage(ref Message m) {
164       switch (m.msg) {
165       case DGUI_ADDCHILDCONTROL: {
166             Control c = winCast!(Control)(m.wParam);
167 
168             if (c is this._panel1 || c is this._panel2) {
169                super.onDGuiMessage(m);
170             } else {
171                throwException!(DGuiException)("SplitPanel doesn't accept child controls");
172             }
173          }
174          break;
175 
176       default:
177          super.onDGuiMessage(m);
178          break;
179       }
180    }
181 
182    protected override void onHandleCreated(EventArgs e) {
183       switch (this._splitOrientation) {
184       case SplitOrientation.vertical:
185          this.cursor = SystemCursors.sizeWE;
186          break;
187 
188       default: // SplitOrientation.horizontal
189          this.cursor = SystemCursors.sizeNS;
190          break;
191       }
192 
193       super.onHandleCreated(e);
194    }
195 
196    protected override void onPaint(PaintEventArgs e) {
197       Canvas c = e.canvas;
198       Rect cr = e.clipRectangle;
199       int mid = this._splitPos + (splitterSize / 2);
200       scope Pen dp = new Pen(SystemColors.color3DDarkShadow, 2, PenStyle.dot);
201       scope Pen lp = new Pen(SystemColors.colorButtonFace, 2, PenStyle.dot);
202 
203       switch (this._splitOrientation) {
204       case SplitOrientation.vertical: {
205             c.drawEdge(Rect(this._splitPos, cr.top, splitterSize, cr.bottom),
206                   EdgeType.raised, EdgeMode.left | EdgeMode.right);
207 
208             for (int p = (this.height / 2) - 15, i = 0; i < 8; i++, p += 5) {
209                c.drawLine(dp, mid, p, mid, p + 1);
210                c.drawLine(lp, mid - 1, p - 1, mid - 1, p);
211             }
212          }
213          break;
214 
215       default: // SplitOrientation.horizontal
216       {
217             c.drawEdge(Rect(cr.left, this._splitPos, cr.right, splitterSize),
218                   EdgeType.raised, EdgeMode.top | EdgeMode.bottom);
219 
220             for (int p = (this.width / 2) - 15, i = 0; i < 8; i++, p += 5) {
221                c.drawLine(dp, p, mid, p + 1, mid);
222                c.drawLine(lp, p - 1, mid + 1, p - 1, mid);
223             }
224          }
225          break;
226       }
227 
228       super.onPaint(e);
229    }
230 
231    protected override void wndProc(ref Message m) {
232       if (m.msg == WM_WINDOWPOSCHANGING) {
233          WINDOWPOS* pWndPos = cast(WINDOWPOS*)m.lParam;
234 
235          if (!(pWndPos.flags & SWP_NOSIZE)) {
236             switch (this._splitOrientation) {
237             case SplitOrientation.vertical: {
238                   if (this.width) // Avoid division by 0
239                      this._splitPos = MulDiv(pWndPos.cx, this._splitPos, this.width);
240                }
241                break;
242 
243             default: // SplitOrientation.horizontal
244             {
245                   if (this.height) // Avoid division by 0
246                      this._splitPos = MulDiv(pWndPos.cy, this._splitPos, this.height);
247                }
248                break;
249             }
250          }
251       }
252 
253       super.wndProc(m);
254    }
255 }