import arsd.minigui; import arsd.textlayouter; import arsd.core; import std.string; class MyTheme : VisualTheme!MyTheme { mixin DefaultDarkTheme; override Color selectionBackgroundColor() { return Color(0, 120, 255); } override Color selectionForegroundColor() { return Color(245, 245, 245); } } class AnimatedTextEdit : CustomTextEdit { this(Widget parent) { super(parent); colorMap = [ "error": Color(255, 0, 0), "warn": Color(255, 255, 0), "info": Color(0, 255, 255), "success": Color(0, 255, 0), "TODO": Color(255, 0, 255) ]; } static class Style : CustomTextEdit.Style { override OperatingSystemFont font() { return new OperatingSystemFont("monospace", 24); } override WidgetBackground background() { return WidgetBackground(Color(16, 16, 16)); } override Color foregroundColor() { return Color(245, 245, 245); } override FrameStyle borderStyle() { return FrameStyle.none; } } mixin OverrideStyle!Style; Color[string] colorMap; string searching; override TextDisplayHelper textDisplayHelperFactory( TextLayouter textLayout, ScrollMessageWidget smw ) { smw.verticalScrollBar.hide(); smw.horizontalScrollBar.hide(); return new MyTextDisplayHelper(this, textLayout, smw); } static class MyTextDisplayHelper : TextDisplayHelper { AnimatedTextEdit owner; this(AnimatedTextEdit owner, TextLayouter textLayout, ScrollMessageWidget smw) { this.owner = owner; super(textLayout, smw); } float camX = 0; float camY = 0; override void drawTextSegment( MyTextStyle ignoredGenericStyle, WidgetPainter painter, Point upperLeft, scope const(char)[] text ) { auto t = text.idup; Color col = getComputedStyle().foregroundColor; foreach (word, c; owner.colorMap) { if (t.indexOf(word) != -1) { col = c; break; } } if (owner.searching.length && t.indexOf(owner.searching) != -1) { painter.fillColor = Color(40, 40, 40); painter.outlineColor = Color(80, 80, 80); painter.drawRectangle(upperLeft, painter.textSize(text)); } painter.outlineColor = col; painter.drawText(upperLeft, text); } override Rectangle paintContent( WidgetPainter painter, const Rectangle bounds ) { TextLayouter.CaretInformation ci; TextStyle ts; l.getDrawableText(delegate bool(txt, style, info, carets...) { ts = style; foreach (caret; carets) ci = caret; return true; }); float targetX = this.width / 2 - ci.boundingBox.center.x; float targetY = this.height / 2 - ci.boundingBox.center.y; float smoothing = 0.15; camX += (targetX - camX) * smoothing; camY += (targetY - camY) * smoothing; auto baseX = painter.originX; auto baseY = painter.originY; painter.originX = baseX + cast(int)camX; painter.originY = baseY + cast(int)camY; auto result = super.paintContent(painter, bounds); auto cs = getComputedStyle(); auto fg = cs.foregroundColor; auto caretBox = ci.boundingBox; auto block = Rectangle( Point( (caretBox.upperLeft + bounds.upperLeft - smw.position()).x, (caretBox.upperLeft + bounds.upperLeft - smw.position()).y - 1 ), Size( cast(int)ts.font().averageWidth(), cast(int)ts.font().height + 2 ) ); painter.fillColor = Color(0, 0, 0, 0); painter.outlineColor = fg; painter.drawRectangle(block); painter.notifyCursorPosition( block.left, block.top, block.width, block.height ); return result; } } } Timer timer; void main() { auto window = new Window; auto ate = new AnimatedTextEdit(window); ate.content = ""; WidgetPainter.visualTheme = new MyTheme; timer = new Timer(16, { ate.redraw(); }); window.loop(); }