1 module nanogui.experimental.utils; 2 3 import nanogui.common : NanoContext; 4 import nanogui.common : Vector2f, Vector2i; 5 6 public import auxil.model; 7 public import auxil.traits; 8 9 private string enumToString(E)(E e) @nogc @safe nothrow 10 if (is(E == enum)) 11 { 12 import std.traits : Unqual; 13 14 // using `if` instead of `final switch` is simple 15 // workaround of duplicated enum members 16 static foreach(v; __traits(allMembers, E)) 17 mixin("if (e == E." ~ v ~ ") return `" ~ v ~ "`;"); 18 return "Unrepresentable by " ~ Unqual!E.stringof ~ " value"; 19 } 20 21 void indent(ref NanoContext ctx) 22 { 23 ctx.position.x += 20; 24 ctx.size.x -= 20; 25 } 26 27 void unindent(ref NanoContext ctx) 28 { 29 ctx.position.x -= 20; 30 ctx.size.x += 20; 31 } 32 33 bool isPointInRect(Vector2f topleft, Vector2f size, Vector2f point) 34 { 35 return point.x >= topleft.x && point.x < topleft.x+size.x && 36 point.y >= topleft.y && point.y < topleft.y+size.y; 37 } 38 39 bool isPointInRect(Vector2i topleft, Vector2i size, Vector2i point) 40 { 41 return point.x >= topleft.x && point.x < topleft.x+size.x && 42 point.y >= topleft.y && point.y < topleft.y+size.y; 43 } 44 45 auto drawItem(T)(ref NanoContext ctx, float height, T item) 46 { 47 import std.traits : isSomeString; 48 49 static if (isSomeString!T) 50 return drawString(ctx, height, item); 51 else 52 return drawPodType(ctx, height, item); 53 } 54 55 private auto drawString(Char)(ref NanoContext ctx, float height, const(Char)[] str) 56 { 57 import nanogui.common : textAlign, text, roundedRect, fill, beginPath, linearGradient, fillPaint, fillColor; 58 59 // it is possible if the item is placed out of visible area 60 if (ctx.size[ctx.orientation] <= 0) 61 return false; 62 63 bool inside; 64 if (isPointInRect(ctx.position, ctx.size, ctx.mouse)) 65 { 66 const gradTop = ctx.theme.mButtonGradientTopFocused; 67 const gradBot = ctx.theme.mButtonGradientBotFocused; 68 69 ctx.beginPath; 70 ctx.roundedRect( 71 ctx.position.x, ctx.position.y, 72 ctx.size.x, ctx.size.y, 73 ctx.theme.mButtonCornerRadius - 1 74 ); 75 76 const bg = ctx.linearGradient( 77 ctx.position.x, ctx.position.y, 78 ctx.position.x, ctx.position.y + height, 79 gradTop, gradBot 80 ); 81 82 ctx.fillPaint(bg); 83 ctx.fill; 84 ctx.fillColor(ctx.theme.mTextColor); 85 inside = true; 86 } 87 ctx.textAlign(ctx.algn); 88 ctx.text(ctx.position.x, ctx.position.y, str); 89 ctx.position.y += height; 90 91 return inside; 92 } 93 94 private auto drawPodType(T)(ref NanoContext ctx, float height, T item) 95 { 96 import std.format : sformat; 97 import std.traits : isIntegral, isFloatingPoint, isBoolean, isSomeString, 98 isPointer, isSomeChar; 99 100 enum textBufferSize = 512; 101 char[textBufferSize] buffer; 102 size_t l; 103 104 // format specifier depends on type 105 static if (is(T == enum)) 106 { 107 const s = item.enumToString; 108 l += sformat(buffer[l..$], "%s", s).length; 109 } 110 else static if (isIntegral!T) 111 l += sformat(buffer[l..$], "%d", item).length; 112 else static if (isFloatingPoint!T) 113 l += sformat(buffer[l..$], "%f", item).length; 114 else static if (isBoolean!T) 115 l += sformat(buffer[l..$], item ? "true" : "false").length; 116 else static if (isSomeString!T || isPointer!T) 117 l += sformat(buffer[l..$], "%s", item).length; 118 else static if (isSomeChar!T) 119 l += sformat(buffer[l..$], "%s", item).length; 120 else 121 static assert(0, T.stringof); 122 123 buffer[l < $ ? l : $-1] = '\0'; 124 125 return ctx.drawString(height, buffer[0..l]); 126 } 127 128 mixin template DependencyProperty(T, alias string name) 129 { 130 import std.string : capitalize; 131 132 protected 133 { 134 mixin("T m" ~ name.capitalize ~ ";"); 135 } 136 public 137 { 138 import std.format : format; 139 mixin (format(q{ 140 final T %1$s() const { return m%2$s; } 141 final void %1$s(T value) 142 { 143 if (value == m%2$s) return; 144 m%2$s = value; 145 invalidate(); 146 } 147 }, name, name.capitalize)); 148 } 149 }