diff --git a/examples/example1/example1.dproj b/examples/example1/example1.dproj index 43366f65..27c4112f 100644 --- a/examples/example1/example1.dproj +++ b/examples/example1/example1.dproj @@ -23,6 +23,11 @@ ../../src + ../../../DerelictUtil/source + ../../../DerelictFI/source + ../../../DerelictFT/source + ../../../DerelictSDL2/source + ../../../DerelictGL3/source @@ -36,6 +41,12 @@ Executable true 0 + + + USE_SDL + USE_OPENGL + + bin\Release diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 30cddd82..f93c79ff 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -43,6 +43,15 @@ uint blendARGB(uint dst, uint src, uint alpha) { return (r << 16) | (g << 8) | b; } +/// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque) +uint blendAlpha(uint a1, uint a2) { + if (!a1) + return a2; + if (!a2) + return a1; + return (((a1 ^ 0xFF) * (a2 ^ 0xFF)) >> 8) ^ 0xFF; +} + ubyte rgbToGray(uint color) { uint srcr = (color >> 16) & 0xFF; uint srcg = (color >> 8) & 0xFF; @@ -127,6 +136,33 @@ version (USE_OPENGL) { class DrawBuf : RefCountedObject { protected Rect _clipRect; protected NinePatch * _ninePatch; + protected uint _alpha; + + /// get current alpha setting (to be applied to all drawing operations) + @property uint alpha() { return _alpha; } + /// set new alpha setting (to be applied to all drawing operations) + @property void alpha(uint alpha) { + _alpha = alpha; + if (_alpha > 0xFF) + _alpha = 0xFF; + } + + /// apply additional transparency to current drawbuf alpha value + void addAlpha(uint alpha) { + _alpha = blendAlpha(_alpha, alpha); + } + + /// applies current drawbuf alpha to argb color value + uint applyAlpha(uint argb) { + if (!_alpha) + return argb; // no drawbuf alpha + uint a1 = (argb >> 24) & 0xFF; + if (a1 == 0xFF) + return argb; // fully transparent + uint a2 = _alpha & 0xFF; + uint a = blendAlpha(a1, a2); + return (argb & 0xFFFFFF) | (a << 24); + } version (USE_OPENGL) { protected uint _id; @@ -291,7 +327,7 @@ class DrawBuf : RefCountedObject { return !rc.empty() && !rc2.empty(); } /// reserved for hardware-accelerated drawing - begins drawing batch - void beforeDrawing() { } + void beforeDrawing() { _alpha = 0; } /// reserved for hardware-accelerated drawing - ends drawing batch void afterDrawing() { } /// returns buffer bits per pixel @@ -372,13 +408,18 @@ alias DrawBufRef = Ref!DrawBuf; struct ClipRectSaver { private DrawBuf _buf; private Rect _oldClipRect; - this(DrawBuf buf, ref Rect newClipRect) { + private uint _oldAlpha; + this(DrawBuf buf, ref Rect newClipRect, uint newAlpha = 0) { _buf = buf; _oldClipRect = buf.clipRect; + _oldAlpha = buf.alpha; buf.intersectClipRect(newClipRect); + if (newAlpha) + buf.addAlpha(newAlpha); } ~this() { _buf.clipRect = _oldClipRect; + _buf.alpha = _oldAlpha; } } @@ -407,7 +448,7 @@ class ColorDrawBufBase : DrawBuf { uint * dstrow = scanLine(dstrect.top + yy) + dstrect.left; for (int i = 0; i < dx; i++) { uint pixel = srcrow[i]; - uint alpha = pixel >> 24; + uint alpha = blendAlpha(_alpha, pixel >> 24); if (!alpha) dstrow[i] = pixel; else if (alpha < 255) { @@ -447,8 +488,8 @@ class ColorDrawBufBase : DrawBuf { for (int x = 0; x < dx; x++) { uint srcpixel = srcrow[xmap[x]]; uint dstpixel = dstrow[x]; - uint alpha = (srcpixel >> 24) & 255; - if (!alpha) + uint alpha = blendAlpha(_alpha, srcpixel >> 24); + if (!alpha) dstrow[x] = srcpixel; else if (alpha < 255) { // apply blending @@ -537,6 +578,7 @@ class ColorDrawBufBase : DrawBuf { int srcdx = glyph.blackBoxX; int srcdy = glyph.blackBoxY; bool clipping = !_clipRect.empty(); + color = applyAlpha(color); for (int yy = 0; yy < srcdy; yy++) { int liney = y + yy; if (clipping && (liney < _clipRect.top || liney >= _clipRect.bottom)) diff --git a/src/dlangui/graphics/ftfonts.d b/src/dlangui/graphics/ftfonts.d index 55fc7d49..442b4a61 100644 --- a/src/dlangui/graphics/ftfonts.d +++ b/src/dlangui/graphics/ftfonts.d @@ -350,7 +350,7 @@ class FreeTypeFont : Font { // Log.d("ft _glyphCache.find took ", duration / 10, " ns"); if (found !is null) return found; - Log.v("Glyph ", ch, " is not found in cache, getting from font"); + //Log.v("Glyph ", ch, " is not found in cache, getting from font"); FT_UInt index; FreeTypeFontFile file; if (!findGlyph(ch, 0, index, file)) { diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d index d3becf7c..c5c991fa 100644 --- a/src/dlangui/graphics/gldrawbuf.d +++ b/src/dlangui/graphics/gldrawbuf.d @@ -53,6 +53,7 @@ class GLDrawBuf : DrawBuf { /// reserved for hardware-accelerated drawing - begins drawing batch override void beforeDrawing() { + _alpha = 0; if (_scene !is null) { _scene.reset(); } @@ -77,12 +78,12 @@ class GLDrawBuf : DrawBuf { /// fill the whole buffer with solid color (no clipping applied) override void fill(uint color) { assert(_scene !is null); - _scene.add(new SolidRectSceneItem(Rect(0, 0, _dx, _dy), color)); + _scene.add(new SolidRectSceneItem(Rect(0, 0, _dx, _dy), applyAlpha(color))); } /// fill rectangle with solid color (clipping is applied) override void fillRect(Rect rc, uint color) { assert(_scene !is null); - _scene.add(new SolidRectSceneItem(rc, color)); + _scene.add(new SolidRectSceneItem(rc, applyAlpha(color))); } /// draw 8bit alpha image - usually font glyph using specified color (clipping is applied) override void drawGlyph(int x, int y, Glyph * glyph, uint color) { @@ -93,7 +94,7 @@ class GLDrawBuf : DrawBuf { if (applyClipping(dstrect, srcrect)) { if (!glGlyphCache.get(glyph.id)) glGlyphCache.put(glyph); - _scene.add(new GlyphSceneItem(glyph.id, dstrect, srcrect, color, null)); + _scene.add(new GlyphSceneItem(glyph.id, dstrect, srcrect, applyAlpha(color), null)); } } /// draw source buffer rectangle contents to destination buffer @@ -104,7 +105,7 @@ class GLDrawBuf : DrawBuf { if (applyClipping(dstrect, srcrect)) { if (!glImageCache.get(src.id)) glImageCache.put(src); - _scene.add(new TextureSceneItem(src.id, dstrect, srcrect, 0xFFFFFF, 0, null, 0)); + _scene.add(new TextureSceneItem(src.id, dstrect, srcrect, applyAlpha(0xFFFFFF), 0, null, 0)); } } /// draw source buffer rectangle contents to destination buffer rectangle applying rescaling @@ -114,7 +115,7 @@ class GLDrawBuf : DrawBuf { if (applyClipping(dstrect, srcrect)) { if (!glImageCache.get(src.id)) glImageCache.put(src); - _scene.add(new TextureSceneItem(src.id, dstrect, srcrect, 0xFFFFFF, 0, null, 0)); + _scene.add(new TextureSceneItem(src.id, dstrect, srcrect, applyAlpha(0xFFFFFF), 0, null, 0)); } } diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index a87a684a..22810a8a 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -156,6 +156,8 @@ class Window { p.animate(interval); } + static immutable int PERFORMANCE_LOGGING_THRESHOLD_MS = 20; + void onDraw(DrawBuf buf) { bool needDraw = false; bool needLayout = false; @@ -173,10 +175,12 @@ class Window { long measureStart = currentTimeMillis; measure(); long measureEnd = currentTimeMillis; - Log.d("measure took ", measureEnd - measureStart, " ms"); + if (measureEnd - measureStart > PERFORMANCE_LOGGING_THRESHOLD_MS) + Log.d("measure took ", measureEnd - measureStart, " ms"); layout(); long layoutEnd = currentTimeMillis; - Log.d("layout took ", layoutEnd - measureEnd, " ms"); + if (layoutEnd - measureEnd > PERFORMANCE_LOGGING_THRESHOLD_MS) + Log.d("layout took ", layoutEnd - measureEnd, " ms"); //checkUpdateNeeded(needDraw, needLayout, animationActive); } long drawStart = currentTimeMillis; @@ -186,7 +190,8 @@ class Window { foreach(p; _popups) p.onDraw(buf); long drawEnd = currentTimeMillis; - Log.d("draw took ", drawEnd - drawStart, " ms"); + if (drawEnd - drawStart > PERFORMANCE_LOGGING_THRESHOLD_MS) + Log.d("draw took ", drawEnd - drawStart, " ms"); lastDrawTs = ts; if (animationActive) scheduleAnimation(); @@ -291,9 +296,9 @@ class Window { } // if not processed by children, offer event to root if (sendAndCheckOverride(root, event)) { - Log.d("MouseEvent is processed"); + debug(mouse) Log.d("MouseEvent is processed"); if (event.action == MouseAction.ButtonDown && _mouseCaptureWidget is null && !event.doNotTrackButtonDown) { - Log.d("Setting active widget"); + debug(mouse) Log.d("Setting active widget"); setCaptureWidget(root, event); } else if (event.action == MouseAction.Move) { addTracking(root); @@ -431,7 +436,7 @@ class Window { res = sendAndCheckOverride(_mouseCaptureWidget, event); if (!currentButtons) { // usable capturing - no more buttons pressed - Log.d("unsetting active widget"); + debug(mouse) Log.d("unsetting active widget"); _mouseCaptureWidget = null; } return res; diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index bf6c325d..d8582012 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -255,7 +255,7 @@ version(USE_SDL) { MouseEvent event = new MouseEvent(action, btn, flags, cast(short)x, cast(short)y); bool res = dispatchMouseEvent(event); if (res) { - Log.d("Calling update() after mouse event"); + debug(mouse) Log.d("Calling update() after mouse event"); invalidate(); } } @@ -660,19 +660,19 @@ version(USE_SDL) { Log.d("SDL_WINDOWEVENT_MAXIMIZED"); break; case SDL_WINDOWEVENT_RESTORED: - Log.d("SDL_WINDOWEVENT_MAXIMIZED"); + Log.d("SDL_WINDOWEVENT_RESTORED"); break; case SDL_WINDOWEVENT_ENTER: - Log.d("SDL_WINDOWEVENT_MAXIMIZED"); + Log.d("SDL_WINDOWEVENT_ENTER"); break; case SDL_WINDOWEVENT_LEAVE: Log.d("SDL_WINDOWEVENT_MAXIMIZED"); break; case SDL_WINDOWEVENT_FOCUS_GAINED: - Log.d("SDL_WINDOWEVENT_MAXIMIZED"); + Log.d("SDL_WINDOWEVENT_FOCUS_GAINED"); break; case SDL_WINDOWEVENT_FOCUS_LOST: - Log.d("SDL_WINDOWEVENT_MAXIMIZED"); + Log.d("SDL_WINDOWEVENT_FOCUS_LOST"); break; default: break; diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d index 3d99bf60..61f94ff7 100644 --- a/src/dlangui/widgets/controls.d +++ b/src/dlangui/widgets/controls.d @@ -103,8 +103,8 @@ class TextWidget : Widget { super.onDraw(buf); Rect rc = _pos; applyMargins(rc); - auto saver = ClipRectSaver(buf, rc); - applyPadding(rc); + auto saver = ClipRectSaver(buf, rc, alpha); + applyPadding(rc); FontRef font = font(); Point sz = font.textSize(text); applyAlign(rc, sz); @@ -167,8 +167,8 @@ class ImageWidget : Widget { super.onDraw(buf); Rect rc = _pos; applyMargins(rc); - auto saver = ClipRectSaver(buf, rc); - applyPadding(rc); + auto saver = ClipRectSaver(buf, rc, alpha); + applyPadding(rc); DrawableRef img = drawable; if (!img.isNull) { Point sz; @@ -330,8 +330,8 @@ class Button : Widget { applyMargins(rc); buf.fillRect(_pos, backgroundColor); applyPadding(rc); - auto saver = ClipRectSaver(buf, rc); - FontRef font = font(); + auto saver = ClipRectSaver(buf, rc, alpha); + FontRef font = font(); Point sz = font.textSize(text); applyAlign(rc, sz); font.drawText(buf, rc.left, rc.top, text, textColor); @@ -778,8 +778,8 @@ class ScrollBar : AbstractSlider, OnClickHandler { Rect rc = _pos; applyMargins(rc); applyPadding(rc); - auto saver = ClipRectSaver(buf, rc); - _btnForward.onDraw(buf); + auto saver = ClipRectSaver(buf, rc, alpha); + _btnForward.onDraw(buf); _btnBack.onDraw(buf); _pageUp.onDraw(buf); _pageDown.onDraw(buf); diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 45acec02..ab1907b6 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -1849,8 +1849,8 @@ class EditLine : EditWidgetBase { Rect rc = _pos; applyMargins(rc); applyPadding(rc); - auto saver = ClipRectSaver(buf, rc); - FontRef font = font(); + auto saver = ClipRectSaver(buf, rc, alpha); + FontRef font = font(); dstring txt = text; Point sz = font.textSize(txt); //applyAlign(rc, sz); @@ -2373,7 +2373,7 @@ class EditBox : EditWidgetBase, OnScrollHandler { _hscrollbar.onDraw(buf); _vscrollbar.onDraw(buf); Rect rc = _clientRc; - auto saver = ClipRectSaver(buf, rc); + auto saver = ClipRectSaver(buf, rc, alpha); FontRef font = font(); //dstring txt = text; diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index cb25819e..2f894550 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -595,8 +595,8 @@ class ListWidget : WidgetGroup, OnScrollHandler { Rect rc = _pos; applyMargins(rc); applyPadding(rc); - auto saver = ClipRectSaver(buf, rc); - // draw scrollbar + auto saver = ClipRectSaver(buf, rc, alpha); + // draw scrollbar if (_needScrollbar) _scrollbar.onDraw(buf); diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index 24cb5808..31dac47e 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -212,7 +212,7 @@ class MenuItemWidget : WidgetGroup { Rect rc = _pos; applyMargins(rc); applyPadding(rc); - auto saver = ClipRectSaver(buf, rc); + auto saver = ClipRectSaver(buf, rc, alpha); for (int i = 0; i < _children.count; i++) { Widget item = _children.get(i); if (item.visibility != Visibility.Visible) diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index ad68d12d..07bb13e5 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -108,6 +108,7 @@ class Style { protected uint _backgroundColor = COLOR_UNSPECIFIED; protected uint _textColor = COLOR_UNSPECIFIED; protected uint _textFlags = 0; + protected uint _alpha; protected string _fontFace; protected string _backgroundImageId; protected Rect _padding; @@ -267,6 +268,14 @@ class Style { return _margins; } + /// alpha (0=opaque .. 255=transparent) + @property uint alpha() const { + if (_alpha != COLOR_UNSPECIFIED) + return _alpha; + else + return parentStyle.alpha; + } + /// text color @property uint textColor() const { if (_textColor != COLOR_UNSPECIFIED) @@ -451,6 +460,11 @@ class Style { return this; } + @property Style alpha(uint alpha) { + _alpha = alpha; + return this; + } + @property Style textFlags(uint flags) { _textFlags = flags; return this; @@ -536,7 +550,7 @@ class Style { /// create state substyle for this style Style createState(uint stateMask = 0, uint stateValue = 0) { assert(stateMask != 0); - Log.d("Creating substate ", stateMask); + debug(styles) Log.d("Creating substate ", stateMask); Style child = (_theme !is null ? _theme : currentTheme).createSubstyle(null); child._parentStyle = this; child._stateMask = stateMask; @@ -756,7 +770,7 @@ Theme createDefaultTheme() { menuItem.createState(State.Pressed, State.Pressed).backgroundColor(0x4080C000); menuItem.createState(State.Selected, State.Selected).backgroundColor(0x00F8F9Fa); menuItem.createState(State.Hovered, State.Hovered).backgroundColor(0xC0FFFF00); - res.createSubstyle("MENU_ICON").setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left); + res.createSubstyle("MENU_ICON").setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).alpha(0xA0); res.createSubstyle("MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TextFlag.UnderlineHotKeys).createState(State.Enabled,0).textColor(0x80404040); res.createSubstyle("MAIN_MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TEXT_FLAGS_USE_PARENT).createState(State.Enabled,0).textColor(0x80404040); res.createSubstyle("MENU_ACCEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).textColor(0x80404040); diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 2378134e..d0dfeb39 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -291,7 +291,15 @@ class Widget { invalidate(); return this; } - /// get text color (ARGB 32 bit value) + /// widget drawing alpha value (0=opaque .. 255=transparent) + @property uint alpha() const { return stateStyle.alpha; } + /// set widget drawing alpha value (0=opaque .. 255=transparent) + @property Widget alpha(uint value) { + ownStyle.alpha = value; + invalidate(); + return this; + } + /// get text color (ARGB 32 bit value) @property uint textColor() const { return stateStyle.textColor; } /// set text color (ARGB 32 bit value) @property Widget textColor(uint value) { @@ -879,20 +887,20 @@ class Widget { if (trackHover) { if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) { if ((state & State.Hovered)) { - Log.d("Hover off ", id); + debug(mouse) Log.d("Hover off ", id); resetState(State.Hovered); } return true; } if (event.action == MouseAction.Move) { if (!(state & State.Hovered)) { - Log.d("Hover ", id); + debug(mouse) Log.d("Hover ", id); setState(State.Hovered); } return true; } if (event.action == MouseAction.Leave) { - Log.d("Leave ", id); + debug(mouse) Log.d("Leave ", id); resetState(State.Hovered); return true; } @@ -992,6 +1000,7 @@ class Widget { return; Rect rc = _pos; applyMargins(rc); + auto saver = ClipRectSaver(buf, rc, alpha); DrawableRef bg = stateStyle.backgroundDrawable; if (!bg.isNull) { bg.drawTo(buf, rc, state);