implement APLHA support for widgets; use semitransparency for icons in disabled menu items; reduce excessive logging

This commit is contained in:
Vadim Lopatin 2014-05-12 15:13:54 +04:00
parent 6d518ed5c0
commit 8a04d6aea6
12 changed files with 124 additions and 42 deletions

View File

@ -23,6 +23,11 @@
<Includes>
<Includes>
<Path>../../src</Path>
<Path>../../../DerelictUtil/source</Path>
<Path>../../../DerelictFI/source</Path>
<Path>../../../DerelictFT/source</Path>
<Path>../../../DerelictSDL2/source</Path>
<Path>../../../DerelictGL3/source</Path>
</Includes>
</Includes>
</PropertyGroup>
@ -36,6 +41,12 @@
<Target>Executable</Target>
<Externalconsole>true</Externalconsole>
<DebugLevel>0</DebugLevel>
<VersionIds>
<VersionIds>
<String>USE_SDL</String>
<String>USE_OPENGL</String>
</VersionIds>
</VersionIds>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release</OutputPath>

View File

@ -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))

View File

@ -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)) {

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);