fix image preview and add option for automatically stop auto screenshoting when idle

This commit is contained in:
iceBear67
2026-08-07 19:00:20 +00:00
parent 014b18f1ba
commit 30d124fd8b
22 changed files with 641 additions and 32 deletions
@@ -55,12 +55,19 @@ public final class ThumbnailCache implements AutoCloseable {
this.textures = textures;
this.loader = loader;
this.size = size;
this.capacity = Math.max(8, capacity);
this.capacity = Math.max(1, capacity);
}
/** Follows the player's setting without discarding what is already loaded. */
/**
* Follows the player's setting without discarding what is already loaded.
*
* <p>Only one is imposed here, because the caller is the one who knows what
* it is caching: the grid's floor comes from its config setting, and the
* detail view genuinely wants a capacity of two. Nothing thrashes at a small
* capacity anyway -- {@link #endFrame()} refuses to evict what was drawn.
*/
public void capacity(int value) {
this.capacity = Math.max(8, value);
this.capacity = Math.max(1, value);
}
/** Call once at the top of a frame, before any {@link #of} in that frame. */
@@ -215,6 +215,9 @@ public final class SettingsScreen extends PhotoSyncScreen {
toggle("photosync.settings.skip_when_screen_open", "",
() -> draft().autoCapture().skipWhenScreenOpen(),
value -> autoCapture(settings -> settings.toBuilder().skipWhenScreenOpen(value).build()));
toggle("photosync.settings.skip_when_still", "photosync.settings.skip_when_still.detail",
() -> draft().autoCapture().skipWhenStill(),
value -> autoCapture(settings -> settings.toBuilder().skipWhenStill(value).build()));
}
private void buildNotifications() {
@@ -77,8 +77,10 @@ public final class TimelineScreen extends PhotoSyncScreen {
BrowserSettings settings = ui.core().config().current().browser();
this.tiles = new ThumbnailCache(ui.bridge().textures(), ui.core().thumbnails(),
ThumbnailSize.GRID, settings.thumbnailCacheEntries());
// Three is enough for the one open photo and the two either side of it.
this.detail = new ThumbnailCache(ui.bridge().textures(), ui.core().thumbnails(), ThumbnailSize.DETAIL, 3);
// Only one photo is ever open, and these are full-resolution textures --
// a 4K screenshot is 33MB on the GPU. Two, so re-opening the last one is
// instant, and no more than that.
this.detail = new ThumbnailCache(ui.bridge().textures(), ui.core().thumbnails(), ThumbnailSize.DETAIL, 2);
}
@Override
@@ -297,17 +299,40 @@ public final class TimelineScreen extends PhotoSyncScreen {
half - uHalf, half - vHalf, half + uHalf, half + vHalf);
}
/**
* The opened photo, at whatever resolution has arrived so far.
*
* <p>What arrives first is the ThumbHash: a blurred 32x32 that is right for a
* tile and nowhere near enough for a full-screen view. It is still worth
* drawing, because it says which photo is opening, but it is dimmed and
* labelled while it stands in -- an unannotated blur is indistinguishable
* from a mod that fetched the wrong size.
*/
private void renderOpened(RenderBridge render, int mouseX, int mouseY) {
detail.beginFrame();
Rect area = body();
render.fill(area.x(), area.y(), area.width(), area.height(), theme().overlay());
Rect frame = area.inset(6);
Optional<ThumbnailCache.Thumbnail> image = detail.of(opened);
if (image.isPresent()) {
drawContained(render, image.get().texture(), frame);
} else {
if (detail.failed(opened.id())) {
chrome.notice(render, frame, chrome.translate("photosync.browse.open_failed"),
chrome.translate("photosync.browse.open_failed.hint"));
} else if (image.isEmpty()) {
chrome.notice(render, frame, chrome.translate("photosync.browse.opening"), "");
} else {
drawContained(render, image.get().texture(), frame);
if (image.get().placeholder()) {
render.fill(frame.x(), frame.y(), frame.width(), frame.height(),
theme().fade(theme().overlay(), 0.6f));
chrome.centered(render, chrome.translate("photosync.browse.opening"),
new Rect(frame.x(), frame.centerY() - render.lineHeight(), frame.width(), render.lineHeight()),
theme().text());
chrome.busyBar(render, new Rect(frame.centerX() - 60, frame.centerY() + 6, 120, 3),
System.currentTimeMillis(), theme().accent());
}
}
chrome.centered(render, chrome.translate("photosync.browse.close_hint"),
area.bottom(render.lineHeight() + 2), theme().textFaint());
detail.endFrame();
@@ -367,7 +392,7 @@ public final class TimelineScreen extends PhotoSyncScreen {
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (opened != null) {
opened = null;
closeOpened();
return true;
}
if (scroll.mouseClicked(mouseX, mouseY, button)) {
@@ -401,7 +426,7 @@ public final class TimelineScreen extends PhotoSyncScreen {
@Override
public boolean keyPressed(int key, int scanCode, int modifiers) {
if (opened != null && key == Keys.ESCAPE) {
opened = null;
closeOpened();
return true;
}
return switch (key) {
@@ -425,6 +450,19 @@ public final class TimelineScreen extends PhotoSyncScreen {
};
}
/**
* A cache entry that failed stays failed, which is right for a grid tile the
* player is scrolling past and wrong for the one photo they chose to open. It
* holds three entries, so dropping them all is the cheapest way to let a
* second click try again.
*/
private void closeOpened() {
if (detail.failed(opened.id())) {
detail.clear();
}
opened = null;
}
/** Escape dismisses the open photo before it dismisses the screen. */
@Override
public boolean closeOnEscape() {