1 // Written in the D programming language.
2 
3 /**
4 This module declares basic data types for usage in dlangui library.
5 
6 Contains reference counting support, point and rect structures, character glyph structure, misc utility functions.
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:   Boost License 1.0
40 Authors:   Vadim Lopatin, coolreader.org@gmail.com
41 */
42 module dlangui.core.types;
43 
44 public import dlangui.core.config;
45 
46 import std.algorithm;
47 
48 /// 2D point
49 struct Point {
50     int x;
51     int y;
52 
53     Point opBinary(string op)(Point v) if (op == "+") {
54         return Point(x + v.x, y + v.y);
55     }
56     Point opBinary(string op)(Point v) if (op == "-") {
57         return Point(x - v.x, y - v.y);
58     }
59     int opCmp(ref const Point b) const {
60         if (x == b.x) return y - b.y;
61         return x - b.x;
62     }
63 }
64 
65 /** 2D rectangle
66     Note: Rect(0,0,20,10) size is 20x10 -- right and bottom sides are non-inclusive -- if you draw such rect, rightmost drawn pixel will be x=19 and bottom pixel y=9
67 */
68 struct Rect {
69     /// x coordinate of top left corner
70     int left;
71     /// y coordinate of top left corner
72     int top;
73     /// x coordinate of bottom right corner (non-inclusive)
74     int right;
75     /// y coordinate of bottom right corner (non-inclusive)
76     int bottom;
77     /// returns average of left, right
78     @property int middlex() { return (left + right) / 2; }
79     /// returns average of top, bottom
80     @property int middley() { return (top + bottom) / 2; }
81     /// returns middle point
82     @property Point middle() { return Point(middlex, middley); }
83 
84     /// returns top left point of rectangle
85     @property Point topLeft() { return Point(left, top); }
86     /// returns bottom right point of rectangle
87     @property Point bottomRight() { return Point(right, bottom); }
88 
89     /// returns size (width, height) in Point
90     @property Point size() { return Point(right - left, bottom - top); }
91 
92     /// add offset to horizontal and vertical coordinates
93     void offset(int dx, int dy) {
94         left += dx;
95         right += dx;
96         top += dy;
97         bottom += dy;
98     }
99     /// expand rectangle dimensions
100     void expand(int dx, int dy) {
101         left -= dx;
102         right += dx;
103         top -= dy;
104         bottom += dy;
105     }
106     /// shrink rectangle dimensions
107     void shrink(int dx, int dy) {
108         left += dx;
109         right -= dx;
110         top += dy;
111         bottom -= dy;
112     }
113     /// for all fields, sets this.field to rc.field if rc.field > this.field
114     void setMax(Rect rc) {
115         if (left < rc.left)
116             left = rc.left;
117         if (right < rc.right)
118             right = rc.right;
119         if (top < rc.top)
120             top = rc.top;
121         if (bottom < rc.bottom)
122             bottom = rc.bottom;
123     }
124     /// returns width of rectangle (right - left)
125     @property int width() { return right - left; }
126     /// returns height of rectangle (bottom - top)
127     @property int height() { return bottom - top; }
128     /// constructs rectangle using left, top, right, bottom coordinates
129     this(int x0, int y0, int x1, int y1) {
130         left = x0;
131         top = y0;
132         right = x1;
133         bottom = y1;
134     }
135     /// constructs rectangle using two points - (left, top), (right, bottom) coordinates
136     this(Point pt0, Point pt1) {
137         left = pt0.x;
138         top = pt0.y;
139         right = pt1.x;
140         bottom = pt1.y;
141     }
142     /// returns true if rectangle is empty (right <= left || bottom <= top)
143     @property bool empty() {
144         return right <= left || bottom <= top;
145     }
146     /// translate rectangle coordinates by (x,y) - add deltax to x coordinates, and deltay to y coordinates
147     alias moveBy = offset;
148     /// moves this rect to fit rc bounds, retaining the same size
149     void moveToFit(ref Rect rc) {
150         if (right > rc.right)
151             moveBy(rc.right - right, 0);
152         if (bottom > rc.bottom)
153             moveBy(0, rc.bottom - bottom);
154         if (left < rc.left)
155             moveBy(rc.left - left, 0);
156         if (top < rc.top)
157             moveBy(0, rc.top - top);
158 
159     }
160     /// updates this rect to intersection with rc, returns true if result is non empty
161     bool intersect(Rect rc) {
162         if (left < rc.left)
163             left = rc.left;
164         if (top < rc.top)
165             top = rc.top;
166         if (right > rc.right)
167             right = rc.right;
168         if (bottom > rc.bottom)
169             bottom = rc.bottom;
170         return right > left && bottom > top;
171     }
172     /// returns true if this rect has nonempty intersection with rc
173     bool intersects(Rect rc) {
174         if (rc.left >= right || rc.top >= bottom || rc.right <= left || rc.bottom <= top)
175             return false;
176         return true;
177     }
178     /// returns true if point is inside of this rectangle
179     bool isPointInside(Point pt) {
180         return pt.x >= left && pt.x < right && pt.y >= top && pt.y < bottom;
181     }
182     /// returns true if point is inside of this rectangle
183     bool isPointInside(int x, int y) {
184         return x >= left && x < right && y >= top && y < bottom;
185     }
186     /// this rectangle is completely inside rc
187     bool isInsideOf(Rect rc) {
188         return left >= rc.left && right <= rc.right && top >= rc.top && bottom <= rc.bottom;
189     }
190 
191     bool opEquals(Rect rc) const {
192         return left == rc.left && right == rc.right && top == rc.top && bottom == rc.bottom;
193     }
194 }
195 
196 /// constant acting as "rectangle not set" value
197 immutable Rect RECT_VALUE_IS_NOT_SET = Rect(int.min, int.min, int.min, int.min);
198 
199 /// widget state bit flags
200 enum State : uint {
201     /// state not specified / normal
202     Normal = 4 | 256, // Normal is Enabled
203     /// pressed (e.g. clicked by mouse)
204     Pressed = 1,
205     /// widget has focus
206     Focused = 2,
207     /// widget can process mouse and key events
208     Enabled = 4,
209     /// mouse pointer is over this widget
210     Hovered = 8, // mouse pointer is over control, buttons not pressed
211     /// widget is selected
212     Selected = 16,
213     /// widget can be checked
214     Checkable = 32,
215     /// widget is checked
216     Checked = 64,
217     /// widget is activated
218     Activated = 128,
219     /// window is focused
220     WindowFocused = 256,
221     /// widget is default control for form (should be focused when window gains focus first time)
222     Default = 512, // widget is default for form (e.g. default button will be focused on show)
223     /// widget has been focused by keyboard navigation
224     KeyboardFocused = 1024,
225     /// return state of parent instead of widget's state when requested
226     Parent = 0x10000, // use parent's state
227 }
228 
229 
230 // Layout size constants
231 /// layout option, to occupy all available place
232 enum int FILL_PARENT = 0x4000_0000;
233 /// layout option, for size based on content
234 enum int WRAP_CONTENT = 0x2000_0000;
235 /// use as widget.layout() param to avoid applying of parent size
236 enum int SIZE_UNSPECIFIED = 0x6000_0000;
237 
238 /// use in styles to specify size in points (1/72 inch)
239 enum int SIZE_IN_POINTS_FLAG = 0x1000_0000;
240 /// (RESERVED) use in styles to specify size in percents * 100 (e.g. 0 == 0%, 10000 == 100%, 100 = 1%)
241 enum int SIZE_IN_PERCENTS_FLAG = 0x0800_0000;
242 
243 
244 /// convert custom size to pixels (sz can be either pixels, or points if SIZE_IN_POINTS_FLAG bit set)
245 int toPixels(int sz) {
246     if (sz > 0 && (sz & SIZE_IN_POINTS_FLAG) != 0) {
247         return pointsToPixels(sz ^ SIZE_IN_POINTS_FLAG);
248     }
249     return sz;
250 }
251 
252 /// convert custom size Point to pixels (sz can be either pixels, or points if SIZE_IN_POINTS_FLAG bit set)
253 Point toPixels(const Point sz) {
254     return Point(toPixels(sz.x), toPixels(sz.y));
255 }
256 
257 /// convert custom size Rect to pixels (sz can be either pixels, or points if SIZE_IN_POINTS_FLAG bit set)
258 Rect toPixels(const Rect sz) {
259     return Rect(toPixels(sz.left), toPixels(sz.top), toPixels(sz.right), toPixels(sz.bottom));
260 }
261 
262 /// make size value with SIZE_IN_POINTS_FLAG set
263 int makePointSize(int pt) {
264     return pt | SIZE_IN_POINTS_FLAG;
265 }
266 
267 /// make size value with SIZE_IN_PERCENTS_FLAG set
268 int makePercentSize(int percent) {
269     return (percent * 100) | SIZE_IN_PERCENTS_FLAG;
270 }
271 
272 /// make size value with SIZE_IN_PERCENTS_FLAG set
273 int makePercentSize(double percent) {
274     return cast(int)(percent * 100) | SIZE_IN_PERCENTS_FLAG;
275 }
276 
277 /// returns true for WRAP_CONTENT, WRAP_CONTENT, SIZE_UNSPECIFIED
278 bool isSpecialSize(int sz) {
279     // don't forget to update if more special constants added
280     return (sz & (WRAP_CONTENT | FILL_PARENT | SIZE_UNSPECIFIED)) != 0;
281 }
282 
283 /// returns true if size has SIZE_IN_PERCENTS_FLAG bit set
284 bool isPercentSize(int size) {
285     return (size & SIZE_IN_PERCENTS_FLAG) != 0;
286 }
287 
288 /// if size has SIZE_IN_PERCENTS_FLAG bit set, returns percent of baseSize, otherwise returns size unchanged
289 int fromPercentSize(int size, int baseSize) {
290     if (isPercentSize(size))
291         return cast(int)(cast(long)(size & ~SIZE_IN_PERCENTS_FLAG) * baseSize / 10000);
292     return size;
293 }
294 
295 /// screen dots per inch
296 private __gshared int PRIVATE_SCREEN_DPI = 96;
297 
298 @property int SCREEN_DPI() {
299     return PRIVATE_SCREEN_DPI;
300 }
301 
302 @property void SCREEN_DPI(int dpi) {
303     static if (BACKEND_CONSOLE) {
304         PRIVATE_SCREEN_DPI = dpi;
305     } else {
306         if (dpi >= 72 && dpi <= 500) {
307             if (PRIVATE_SCREEN_DPI != dpi) {
308                 // changed DPI
309                 PRIVATE_SCREEN_DPI = dpi;
310             }
311         }
312     }
313 }
314 
315 /// one point is 1/72 of inch
316 enum POINTS_PER_INCH = 72;
317 
318 /// convert length in points (1/72in units) to pixels according to SCREEN_DPI
319 int pointsToPixels(int pt) {
320     return pt * SCREEN_DPI / POINTS_PER_INCH;
321 }
322 
323 /// rectangle coordinates in points (1/72in units) to pixels according to SCREEN_DPI
324 Rect pointsToPixels(Rect rc) {
325     return Rect(rc.left.pointsToPixels, rc.top.pointsToPixels, rc.right.pointsToPixels, rc.bottom.pointsToPixels);
326 }
327 
328 /// convert points (1/72in units) to pixels according to SCREEN_DPI
329 int pixelsToPoints(int px) {
330     return px * POINTS_PER_INCH / SCREEN_DPI;
331 }
332 
333 /// Subpixel rendering mode for fonts (aka ClearType)
334 enum SubpixelRenderingMode : ubyte {
335     /// no sub
336     None,
337     /// subpixel rendering on, subpixel order on device: B,G,R
338     BGR,
339     /// subpixel rendering on, subpixel order on device: R,G,B
340     RGB,
341 }
342 
343 /**
344     Character glyph.
345 
346     Holder for glyph metrics as well as image.
347 */
348 align(1)
349 struct Glyph
350 {
351     /// 0: width of glyph black box
352     ushort   blackBoxX;
353 
354     @property ushort correctedBlackBoxX() { return subpixelMode ? blackBoxX / 3 : blackBoxX; }
355 
356     /// 2: height of glyph black box
357     ubyte   blackBoxY;
358     /// 3: X origin for glyph
359     byte    originX;
360     /// 4: Y origin for glyph
361     byte    originY;
362 
363     /// 5: full width of glyph
364     ubyte   width;
365     /// 6: subpixel rendering mode - if !=SubpixelRenderingMode.None, glyph data contains 3 bytes per pixel instead of 1
366     SubpixelRenderingMode subpixelMode;
367     /// 7: usage flag, to handle cleanup of unused glyphs
368     ubyte   lastUsage;
369     static if (ENABLE_OPENGL) {
370         /// 8: unique id of glyph (for drawing in hardware accelerated scenes)
371         uint    id;
372     }
373 
374     ///< 12: glyph data, arbitrary size (blackBoxX * blackBoxY)
375     ubyte[] glyph;
376 }
377 
378 /**
379     Base class for reference counted objects, maintains reference counter inplace.
380 
381     If some class is not inherited from RefCountedObject, additional object will be required to hold counters.
382 */
383 class RefCountedObject {
384     /// count of references to this object from Ref
385     protected int _refCount;
386     /// returns current value of reference counter
387     @property int refCount() const { return _refCount; }
388     /// increments reference counter
389     void addRef() {
390         _refCount++;
391     }
392     /// decrement reference counter, destroy object if no more references left
393     void releaseRef() {
394         if (--_refCount == 0)
395             destroy(this);
396     }
397     ~this() {}
398 }
399 
400 /**
401     Reference counting support.
402 
403     Implemented for case when T is RefCountedObject.
404     Similar to shared_ptr in C++.
405     Allows to share object, destroying it when no more references left.
406 
407     Useful for automatic destroy of objects.
408 */
409 struct Ref(T) { // if (T is RefCountedObject)
410     private T _data;
411     alias get this;
412     /// returns true if object is not assigned
413     @property bool isNull() const { return _data is null; }
414     /// returns counter of references
415     @property int refCount() const { return _data !is null ? _data.refCount : 0; }
416     /// init from T
417     this(T data) {
418         _data = data;
419         if (_data !is null)
420             _data.addRef();
421     }
422     /// after blit
423     this(this) {
424         if (_data !is null)
425             _data.addRef();
426     }
427     /// assign from another refcount by reference
428     ref Ref opAssign(ref Ref data) {
429         if (data._data == _data)
430             return this;
431         if (_data !is null)
432             _data.releaseRef();
433         _data = data._data;
434         if (_data !is null)
435             _data.addRef();
436         return this;
437     }
438     /// assign from another refcount by value
439     ref Ref opAssign(Ref data) {
440         if (data._data == _data)
441             return this;
442         if (_data !is null)
443             _data.releaseRef();
444         _data = data._data;
445         if (_data !is null)
446             _data.addRef();
447         return this;
448     }
449     /// assign object
450     ref Ref opAssign(T data) {
451         if (data == _data)
452             return this;
453         if (_data !is null)
454             _data.releaseRef();
455         _data = data;
456         if (_data !is null)
457             _data.addRef();
458         return this;
459     }
460     /// clears reference
461     void clear() {
462         if (_data !is null) {
463             _data.releaseRef();
464             _data = null;
465         }
466     }
467     /// returns object reference (null if not assigned)
468     @property T get() {
469         return _data;
470     }
471     /// returns const reference from const object
472     @property const(T) get() const {
473         return _data;
474     }
475     /// decreases counter, and destroys object if no more references left
476     ~this() {
477         if (_data !is null)
478             _data.releaseRef();
479     }
480 }
481 
482 
483 //================================================================================
484 // some utility functions
485 
486 /** conversion from wchar z-string */
487 wstring fromWStringz(const(wchar[]) s) {
488     if (s is null)
489         return null;
490     int i = 0;
491     while(s[i])
492         i++;
493     return cast(wstring)(s[0..i].dup);
494 }
495 
496 /** conversion from wchar z-string */
497 wstring fromWStringz(const(wchar) * s) {
498     if (s is null)
499         return null;
500     int i = 0;
501     while(s[i])
502         i++;
503     return cast(wstring)(s[0..i].dup);
504 }
505 
506 /** Deprecated: use std.uni.toUpper instead.
507     Uppercase unicode character.
508 */
509 deprecated dchar dcharToUpper(dchar ch) {
510     static import std.uni;
511     return std.uni.toUpper(ch);
512 }
513 
514 /// decodes hex digit (0..9, a..f, A..F), returns uint.max if invalid
515 uint parseHexDigit(T)(T ch) pure nothrow {
516     if (ch >= '0' && ch <= '9')
517         return ch - '0';
518     else if (ch >= 'a' && ch <= 'f')
519         return ch - 'a' + 10;
520     else if (ch >= 'A' && ch <= 'F')
521         return ch - 'A' + 10;
522     return uint.max;
523 }
524 
525 /// replacement of deprecated std.utf.toUTF8
526 string toUTF8(dstring str) {
527     import std.utf : encode, codeLength, byUTF;
528     char[] buf;
529     buf.length = codeLength!char(str);
530     int pos = 0;
531     foreach(ch; str.byUTF!char) {
532         buf.ptr[pos++] = ch;
533     }
534     return cast(string)buf;
535 }