1 // Written in the D programming language.
2 
3 /**
4 DLANGUI library.
5 
6 This module declares basic data types for usage in dlangui library.
7 
8 Synopsis:
9 
10 ----
11 import dlangui.core.types;
12 
13 // points 
14 Point p(5, 10);
15 
16 // rectangles
17 Rect r(5, 13, 120, 200);
18 writeln(r);
19 
20 // reference counted objects, useful for RAII / resource management.
21 class Foo : RefCountedObject {
22 	int[] resource;
23 	~this() {
24 		writeln("freeing Foo resources");
25 	}
26 }
27 {
28 	Ref!Foo ref1;
29 	{
30 		Ref!Foo fooRef = new RefCountedObject();
31 		ref1 = fooRef;
32 	}
33 	// RAII: will destroy object when no more references
34 }
35 
36 ----
37 
38 Copyright: Vadim Lopatin, 2014
39 License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
40 Authors:   $(WEB coolreader.org, Vadim Lopatin)
41 */
42 module dlangui.core.types;
43 
44 import std.algorithm;
45 
46 struct Point {
47     int x;
48     int y;
49     this(int x0, int y0) {
50         x = x0;
51         y = y0;
52     }
53 }
54 
55 struct Rect {
56     int left;
57     int top;
58     int right;
59     int bottom;
60     @property int middlex() { return (left + right) / 2; }
61     @property int middley() { return (top + bottom) / 2; }
62     void offset(int dx, int dy) {
63         left += dx;
64         right += dx;
65         top += dy;
66         bottom += dy;
67     }
68     /// for all fields, sets this.field to rc.field if rc.field > this.field
69     void setMax(Rect rc) {
70         if (left < rc.left)
71             left = rc.left;
72         if (right < rc.right)
73             right = rc.right;
74         if (top < rc.top)
75             top = rc.top;
76         if (bottom < rc.bottom)
77             bottom = rc.bottom;
78     }
79     @property int width() { return right - left; }
80     @property int height() { return bottom - top; }
81     this(int x0, int y0, int x1, int y1) {
82         left = x0;
83         top = y0;
84         right = x1;
85         bottom = y1;
86     }
87     @property bool empty() {
88         return right <= left || bottom <= top;
89     }
90     void moveBy(int deltax, int deltay) {
91         left += deltax;
92         right += deltax;
93         top += deltay;
94         bottom += deltay;
95     }
96     /// moves this rect to fit rc bounds, retaining the same size
97     void moveToFit(ref Rect rc) {
98         if (right > rc.right)
99             moveBy(rc.right - right, 0);
100         if (bottom > rc.bottom)
101             moveBy(0, rc.bottom - bottom);
102         if (left < rc.left)
103             moveBy(rc.left - left, 0);
104         if (top < rc.top)
105             moveBy(0, rc.top - top);
106 
107     }
108     /// updates this rect to intersection with rc, returns true if result is non empty
109     bool intersect(Rect rc) {
110         if (left < rc.left)
111             left = rc.left;
112         if (top < rc.top)
113             top = rc.top;
114         if (right > rc.right)
115             right = rc.right;
116         if (bottom > rc.bottom)
117             bottom = rc.bottom;
118         return right > left && bottom > top;
119     }
120     /// returns true if this rect has nonempty intersection with rc
121     bool intersects(Rect rc) {
122         if (rc.left >= right || rc.top >= bottom || rc.right <= left || rc.bottom <= top)
123             return false;
124         return true;
125     }
126     /// returns true if point is inside of this rectangle
127     bool isPointInside(Point pt) {
128         return pt.x >= left && pt.x < right && pt.y >= top && pt.y < bottom;
129     }
130     /// returns true if point is inside of this rectangle
131     bool isPointInside(int x, int y) {
132         return x >= left && x < right && y >= top && y < bottom;
133     }
134     /// this rectangle is completely inside rc
135     bool isInsideOf(Rect rc) {
136         return left >= rc.left && right <= rc.right && top >= rc.top && bottom <= rc.bottom;
137     }
138 }
139 
140 /// character glyph
141 align(1)
142 struct Glyph
143 {
144     version (USE_OPENGL) {
145         ///< 0: unique id of glyph (for drawing in hardware accelerated scenes)
146         uint    id;
147     }
148     ///< 4: width of glyph black box
149     ubyte   blackBoxX;
150     ///< 5: height of glyph black box
151     ubyte   blackBoxY;
152     ///< 6: X origin for glyph
153     byte    originX;
154     ///< 7: Y origin for glyph
155     byte    originY;
156     ///< 8: bytes in glyph array
157     ushort  glyphIndex;
158     ///< 10: full width of glyph
159     ubyte   width;
160     ///< 11: usage flag, to handle cleanup of unused glyphs
161 	ubyte   lastUsage;
162     ///< 12: glyph data, arbitrary size
163     ubyte[] glyph;       
164 }
165 
166 /// base class for reference counted objects, maintains reference counter inplace.
167 class RefCountedObject {
168     protected int _refCount;
169     @property int refCount() const { return _refCount; }
170     void addRef() { 
171 		_refCount++; 
172 	}
173     void releaseRef() { 
174 		if (--_refCount == 0) 
175 			destroy(this); 
176 	}
177     ~this() {}
178 }
179 
180 struct Ref(T) { // if (T is RefCountedObject)
181     private T _data;
182     alias get this;
183     @property bool isNull() const { return _data is null; }
184     @property int refCount() const { return _data !is null ? _data.refCount : 0; }
185     this(T data) {
186         _data = data;
187         if (_data !is null)
188             _data.addRef();
189     }
190 	this(this) {
191 		if (_data !is null)
192             _data.addRef();
193 	}
194     ref Ref opAssign(ref Ref data) {
195         if (data._data == _data)
196             return this;
197         if (_data !is null)
198             _data.releaseRef();
199         _data = data._data;
200         if (_data !is null)
201             _data.addRef();
202 		return this;
203     }
204     ref Ref opAssign(Ref data) {
205         if (data._data == _data)
206             return this;
207         if (_data !is null)
208             _data.releaseRef();
209         _data = data._data;
210         if (_data !is null)
211             _data.addRef();
212 		return this;
213     }
214     ref Ref opAssign(T data) {
215         if (data == _data)
216             return this;
217         if (_data !is null)
218             _data.releaseRef();
219         _data = data;
220         if (_data !is null)
221             _data.addRef();
222 		return this;
223     }
224     void clear() { 
225         if (_data !is null) {
226             _data.releaseRef();
227             _data = null;
228         }
229     }
230 	@property T get() {
231 		return _data;
232 	}
233 	@property const(T) get() const {
234 		return _data;
235 	}
236     ~this() {
237         if (_data !is null)
238             _data.releaseRef();
239     }
240 }
241 
242 
243 // some utility functions
244 string fromStringz(const(char[]) s) {
245     if (s is null)
246         return null;
247 	int i = 0;
248 	while(s[i])
249 		i++;
250 	return cast(string)(s[0..i].dup);
251 }
252 
253 string fromStringz(const(char*) s) {
254     if (s is null)
255         return null;
256 	int i = 0;
257 	while(s[i])
258 		i++;
259 	return cast(string)(s[0..i].dup);
260 }
261 
262 wstring fromWStringz(const(wchar[]) s) {
263     if (s is null)
264         return null;
265 	int i = 0;
266 	while(s[i])
267 		i++;
268 	return cast(wstring)(s[0..i].dup);
269 }
270 
271 
272 /// widget state flags - bits
273 enum State : uint {
274     /// state not specified / normal
275     Normal = 4, // Normal is Enabled
276     Pressed = 1,
277     Focused = 2,
278     Enabled = 4,
279     Hovered = 8, // mouse pointer is over control, buttons not pressed
280     Selected = 16,
281     Checkable = 32,
282     Checked = 64,
283     Activated = 128,
284     WindowFocused = 256,
285     Default = 512, // widget is default for form (e.g. default button will be focused on show)
286     Parent = 0x10000, // use parent's state
287 }
288 
289 
290 
291 version (Windows) {
292     immutable char PATH_DELIMITER = '\\';
293 } else {
294     immutable char PATH_DELIMITER = '/';
295 }
296 
297 /// returns true if char ch is / or \ slash
298 bool isPathDelimiter(char ch) {
299     return ch == '/' || ch == '\\';
300 }
301 
302 /// returns current executable path only, including last path delimiter
303 @property string exePath() {
304     import std.file;
305     string path = thisExePath();
306     int lastSlash = 0;
307     for (int i = 0; i < path.length; i++)
308         if (path[i] == PATH_DELIMITER)
309             lastSlash = i;
310     return path[0 .. lastSlash + 1];
311 }
312 
313 /// converts path delimiters to standard for platform inplace in buffer(e.g. / to \ on windows, \ to / on posix), returns buf
314 char[] convertPathDelimiters(char[] buf) {
315     foreach(ref ch; buf) {
316         version (Windows) {
317             if (ch == '/')
318                 ch = '\\';
319         } else {
320             if (ch == '\\')
321                 ch = '/';
322         }
323     }
324     return buf;
325 }
326 
327 /// converts path delimiters to standard for platform (e.g. / to \ on windows, \ to / on posix)
328 string convertPathDelimiters(string src) {
329     char[] buf = src.dup;
330     return cast(string)convertPathDelimiters(buf);
331 }
332 
333 /// appends file path parts with proper delimiters e.g. appendPath("/home/user", ".myapp", "config") => "/home/user/.myapp/config"
334 string appendPath(string[] pathItems ...) {
335     char[] buf;
336     foreach (s; pathItems) {
337         if (buf.length && !isPathDelimiter(buf[$-1]))
338             buf ~= PATH_DELIMITER;
339         buf ~= s;
340     }
341     return convertPathDelimiters(buf).dup;
342 }
343 
344 /// appends file path parts with proper delimiters (as well converts delimiters inside path to system) to buffer e.g. appendPath("/home/user", ".myapp", "config") => "/home/user/.myapp/config"
345 char[] appendPath(char[] buf, string[] pathItems ...) {
346     foreach (s; pathItems) {
347         if (buf.length && !isPathDelimiter(buf[$-1]))
348             buf ~= PATH_DELIMITER;
349         buf ~= s;
350     }
351     return convertPathDelimiters(buf);
352 }
353