mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 704784 - Fade scrollbars when content stops moving [r=pcwalton]
This commit is contained in:
parent
9297a9e422
commit
e2c4df2f39
@ -44,6 +44,7 @@ import android.graphics.RectF;
|
||||
import android.util.Log;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
|
||||
public abstract class Layer {
|
||||
private final ReentrantLock mTransactionLock;
|
||||
@ -191,6 +192,15 @@ public abstract class Layer {
|
||||
pageSize = aPageSize;
|
||||
zoomFactor = aZoomFactor;
|
||||
}
|
||||
|
||||
public boolean fuzzyEquals(RenderContext other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
return RectUtils.fuzzyEquals(viewport, other.viewport)
|
||||
&& pageSize.fuzzyEquals(other.pageSize)
|
||||
&& FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,8 @@ import java.nio.ByteBuffer;
|
||||
* The layer renderer implements the rendering logic for a layer view.
|
||||
*/
|
||||
public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||
private static final String LOGTAG = "GeckoLayerRenderer";
|
||||
|
||||
private static final float BACKGROUND_COLOR_R = 0.81f;
|
||||
private static final float BACKGROUND_COLOR_G = 0.81f;
|
||||
private static final float BACKGROUND_COLOR_B = 0.81f;
|
||||
@ -86,6 +88,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||
private final TextLayer mFrameRateLayer;
|
||||
private final ScrollbarLayer mHorizScrollLayer;
|
||||
private final ScrollbarLayer mVertScrollLayer;
|
||||
private final FadeRunnable mFadeRunnable;
|
||||
private RenderContext mLastPageContext;
|
||||
|
||||
// Dropped frames display
|
||||
private int[] mFrameTimings;
|
||||
@ -108,6 +112,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
mHorizScrollLayer = ScrollbarLayer.create(false);
|
||||
mVertScrollLayer = ScrollbarLayer.create(true);
|
||||
mFadeRunnable = new FadeRunnable();
|
||||
|
||||
mFrameTimings = new int[60];
|
||||
mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
|
||||
@ -140,6 +145,20 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||
Layer rootLayer = controller.getRoot();
|
||||
RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
|
||||
|
||||
if (!pageContext.fuzzyEquals(mLastPageContext)) {
|
||||
// the viewport or page changed, so show the scrollbars again
|
||||
// as per UX decision
|
||||
mVertScrollLayer.unfade();
|
||||
mHorizScrollLayer.unfade();
|
||||
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
|
||||
} else if (mFadeRunnable.timeToFade()) {
|
||||
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
|
||||
if (stillFading) {
|
||||
mFadeRunnable.scheduleNextFadeFrame();
|
||||
}
|
||||
}
|
||||
mLastPageContext = pageContext;
|
||||
|
||||
/* Update layers. */
|
||||
if (rootLayer != null) rootLayer.update(gl);
|
||||
mShadowLayer.update(gl);
|
||||
@ -299,4 +318,40 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
|
||||
class FadeRunnable implements Runnable {
|
||||
private boolean mStarted;
|
||||
private long mRunAt;
|
||||
|
||||
void scheduleStartFade(long delay) {
|
||||
mRunAt = SystemClock.elapsedRealtime() + delay;
|
||||
if (!mStarted) {
|
||||
mView.postDelayed(this, delay);
|
||||
mStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void scheduleNextFadeFrame() {
|
||||
if (mStarted) {
|
||||
Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade");
|
||||
}
|
||||
mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps
|
||||
}
|
||||
|
||||
boolean timeToFade() {
|
||||
return !mStarted;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
long timeDelta = mRunAt - SystemClock.elapsedRealtime();
|
||||
if (timeDelta > 0) {
|
||||
// the run-at time was pushed back, so reschedule
|
||||
mView.postDelayed(this, timeDelta);
|
||||
} else {
|
||||
// reached the run-at time, execute
|
||||
mStarted = false;
|
||||
mView.requestRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,4 +130,11 @@ public final class RectUtils {
|
||||
FloatUtils.interpolate(from.right, to.right, t),
|
||||
FloatUtils.interpolate(from.bottom, to.bottom, t));
|
||||
}
|
||||
|
||||
public static boolean fuzzyEquals(RectF a, RectF b) {
|
||||
return FloatUtils.fuzzyEquals(a.top, b.top)
|
||||
&& FloatUtils.fuzzyEquals(a.left, b.left)
|
||||
&& FloatUtils.fuzzyEquals(a.right, b.right)
|
||||
&& FloatUtils.fuzzyEquals(a.bottom, b.bottom);
|
||||
}
|
||||
}
|
||||
|
@ -50,15 +50,18 @@ import android.opengl.GLES11Ext;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
|
||||
/**
|
||||
* Draws a small rect. This is scaled to become a scrollbar.
|
||||
*/
|
||||
public class ScrollbarLayer extends TileLayer {
|
||||
public static final long FADE_DELAY = 500; // milliseconds before fade-out starts
|
||||
private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame
|
||||
|
||||
private static final int PADDING = 1; // gap between scrollbar and edge of viewport
|
||||
private static final int BAR_SIZE = 6;
|
||||
private static final int CAP_RADIUS = (BAR_SIZE / 2);
|
||||
private static final int SCROLLBAR_COLOR = Color.argb(127, 0, 0, 0);
|
||||
|
||||
private static final int[] CROPRECT_MIDPIXEL = new int[] { CAP_RADIUS, CAP_RADIUS, 1, 1 };
|
||||
private static final int[] CROPRECT_TOPCAP = new int[] { 0, CAP_RADIUS, BAR_SIZE, -CAP_RADIUS };
|
||||
@ -67,30 +70,80 @@ public class ScrollbarLayer extends TileLayer {
|
||||
private static final int[] CROPRECT_RIGHTCAP = new int[] { CAP_RADIUS, BAR_SIZE, CAP_RADIUS, -BAR_SIZE };
|
||||
|
||||
private final boolean mVertical;
|
||||
private final ByteBuffer mBuffer;
|
||||
private final Bitmap mBitmap;
|
||||
private final Canvas mCanvas;
|
||||
private float mOpacity;
|
||||
|
||||
private ScrollbarLayer(CairoImage image, boolean vertical) {
|
||||
private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) {
|
||||
super(false, image);
|
||||
mVertical = vertical;
|
||||
mBuffer = buffer;
|
||||
|
||||
mBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
mCanvas = new Canvas(mBitmap);
|
||||
}
|
||||
|
||||
public static ScrollbarLayer create(boolean vertical) {
|
||||
// just create an empty image for now, it will get drawn
|
||||
// on demand anyway
|
||||
int imageSize = nextPowerOfTwo(BAR_SIZE);
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(imageSize * imageSize * 4);
|
||||
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
||||
return new ScrollbarLayer(image, vertical, buffer);
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(imageSize, imageSize, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
/**
|
||||
* Decrease the opacity of the scrollbar by one frame's worth.
|
||||
* Return true if the opacity was decreased, or false if the scrollbars
|
||||
* are already fully faded out.
|
||||
*/
|
||||
public boolean fade() {
|
||||
if (FloatUtils.fuzzyEquals(mOpacity, 0.0f)) {
|
||||
return false;
|
||||
}
|
||||
beginTransaction();
|
||||
try {
|
||||
setOpacity(Math.max(mOpacity - FADE_AMOUNT, 0.0f));
|
||||
invalidate();
|
||||
} finally {
|
||||
endTransaction();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the opacity of the scrollbar to fully opaque.
|
||||
* Return true if the opacity was changed, or false if the scrollbars
|
||||
* are already fully opaque.
|
||||
*/
|
||||
public boolean unfade() {
|
||||
if (FloatUtils.fuzzyEquals(mOpacity, 1.0f)) {
|
||||
return false;
|
||||
}
|
||||
beginTransaction();
|
||||
try {
|
||||
setOpacity(1.0f);
|
||||
invalidate();
|
||||
} finally {
|
||||
endTransaction();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setOpacity(float opacity) {
|
||||
mOpacity = opacity;
|
||||
|
||||
Paint foregroundPaint = new Paint();
|
||||
foregroundPaint.setAntiAlias(true);
|
||||
foregroundPaint.setStyle(Paint.Style.FILL);
|
||||
foregroundPaint.setColor(SCROLLBAR_COLOR);
|
||||
canvas.drawColor(Color.argb(0, 0, 0, 0), PorterDuff.Mode.CLEAR);
|
||||
canvas.drawCircle(CAP_RADIUS, CAP_RADIUS, CAP_RADIUS, foregroundPaint);
|
||||
// use a (a,r,g,b) color of (127,0,0,0), and multiply the alpha by mOpacity for fading
|
||||
foregroundPaint.setColor(Color.argb((int)Math.round(mOpacity * 127), 0, 0, 0));
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(imageSize * imageSize * 4);
|
||||
bitmap.copyPixelsToBuffer(buffer.asIntBuffer());
|
||||
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
||||
mCanvas.drawColor(Color.argb(0, 0, 0, 0), PorterDuff.Mode.CLEAR);
|
||||
mCanvas.drawCircle(CAP_RADIUS, CAP_RADIUS, CAP_RADIUS, foregroundPaint);
|
||||
|
||||
return new ScrollbarLayer(image, vertical);
|
||||
mBitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user