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