Navigate to editor on notebook creation, reduce flicker with double buffering
- Creating a notebook now auto-navigates to its first page editor (createNotebook returns ID via callback, triggers onNotebookClick) - Double-buffered rendering: all layers composited to an off-screen bitmap before presenting to the display in a single drawBitmap call. Eliminates visible intermediate states (white flash between layers) that caused flicker on e-ink displays. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,11 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
private var gridCanvas: Canvas? = null
|
||||
private var gridDirty = true
|
||||
|
||||
// --- Compositing bitmap (eliminates flicker by drawing all layers
|
||||
// off-screen before presenting to the display in one operation) ---
|
||||
private var compositeBitmap: android.graphics.Bitmap? = null
|
||||
private var compositeCanvas: Canvas? = null
|
||||
|
||||
// --- In-progress stroke ---
|
||||
private var currentPath: Path? = null
|
||||
private var currentPaint: Paint? = null
|
||||
@@ -269,11 +274,24 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
// Fill entire view with white (no super.onDraw to avoid double-clear flicker)
|
||||
canvas.drawColor(Color.WHITE)
|
||||
val w = width
|
||||
val h = height
|
||||
if (w <= 0 || h <= 0) return
|
||||
|
||||
// Draw page background in canonical space
|
||||
canvas.withMatrix(viewMatrix) {
|
||||
// Ensure compositing bitmap exists at view size
|
||||
if (compositeBitmap == null || compositeBitmap!!.width != w || compositeBitmap!!.height != h) {
|
||||
compositeBitmap?.recycle()
|
||||
compositeBitmap = androidx.core.graphics.createBitmap(w, h)
|
||||
compositeCanvas = Canvas(compositeBitmap!!)
|
||||
}
|
||||
|
||||
val c = compositeCanvas!!
|
||||
|
||||
// Compose all layers off-screen
|
||||
c.drawColor(Color.WHITE)
|
||||
|
||||
// Page background
|
||||
c.withMatrix(viewMatrix) {
|
||||
drawRect(
|
||||
0f, 0f,
|
||||
canvasState.pageSize.widthPt.toFloat(),
|
||||
@@ -282,24 +300,22 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
)
|
||||
}
|
||||
|
||||
// Draw cached grid
|
||||
// Cached grid
|
||||
ensureGrid()
|
||||
gridBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
|
||||
gridBitmap?.let { c.drawBitmap(it, 0f, 0f, null) }
|
||||
|
||||
// Draw completed strokes from backing bitmap (screen resolution)
|
||||
// Completed strokes
|
||||
ensureBacking()
|
||||
backingBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
|
||||
backingBitmap?.let { c.drawBitmap(it, 0f, 0f, null) }
|
||||
|
||||
// Draw dynamic elements in canonical space (in-progress stroke, previews, selection)
|
||||
canvas.withMatrix(viewMatrix) {
|
||||
// In-progress stroke
|
||||
// Dynamic elements
|
||||
c.withMatrix(viewMatrix) {
|
||||
currentPath?.let { path ->
|
||||
currentPaint?.let { paint ->
|
||||
drawPath(path, paint)
|
||||
}
|
||||
}
|
||||
|
||||
// Box preview
|
||||
if (isDrawingBox) {
|
||||
boxPreviewPaint.strokeWidth = canvasState.penWidthPt
|
||||
val left = minOf(boxStartX, boxEndX)
|
||||
@@ -309,7 +325,6 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
drawRect(left, top, right, bottom, boxPreviewPaint)
|
||||
}
|
||||
|
||||
// Line preview
|
||||
if (isDrawingLine) {
|
||||
val paint = if (canvasState.lineStyle == LineStyle.DASHED) {
|
||||
dashedLinePaint.also { it.strokeWidth = canvasState.penWidthPt }
|
||||
@@ -323,7 +338,6 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
)
|
||||
}
|
||||
|
||||
// Selection highlights
|
||||
if (selectedStrokeIds.isNotEmpty()) {
|
||||
for (sr in completedStrokes) {
|
||||
if (sr.id in selectedStrokeIds) {
|
||||
@@ -334,7 +348,6 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Selection rectangle
|
||||
if (isSelecting) {
|
||||
val left = minOf(selectionStartX, selectionEndX)
|
||||
val top = minOf(selectionStartY, selectionEndY)
|
||||
@@ -343,6 +356,9 @@ class PadCanvasView(context: Context) : View(context) {
|
||||
drawRect(left, top, right, bottom, selectionRectPaint)
|
||||
}
|
||||
}
|
||||
|
||||
// Present the fully composed frame in one operation
|
||||
canvas.drawBitmap(compositeBitmap!!, 0f, 0f, null)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -243,7 +243,9 @@ fun NotebookListScreen(
|
||||
CreateNotebookDialog(
|
||||
onDismiss = { showCreateDialog = false },
|
||||
onCreate = { title, pageSize ->
|
||||
viewModel.createNotebook(title, pageSize)
|
||||
viewModel.createNotebook(title, pageSize) { notebookId ->
|
||||
onNotebookClick(notebookId)
|
||||
}
|
||||
showCreateDialog = false
|
||||
},
|
||||
)
|
||||
|
||||
@@ -17,9 +17,10 @@ class NotebookListViewModel(
|
||||
val notebooks: StateFlow<List<Notebook>> = repository.getAll()
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||
|
||||
fun createNotebook(title: String, pageSize: String) {
|
||||
fun createNotebook(title: String, pageSize: String, onCreated: (Long) -> Unit = {}) {
|
||||
viewModelScope.launch {
|
||||
repository.create(title, pageSize)
|
||||
val id = repository.create(title, pageSize)
|
||||
onCreated(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user