not sure about this commit

This commit is contained in:
2026-04-03 08:21:07 -03:00
parent 51c0bdd2da
commit 908231f98a
5 changed files with 122 additions and 109 deletions

View File

@@ -194,6 +194,11 @@ class FramesPanel(Gtk.Box):
self._scroll_to(widget)
GLib.timeout_add(400, lambda: widget.remove_css_class("frame-highlight") or False)
def scroll_to_end(self):
"""Scroll to the last frame."""
if self._order and self._order[-1] in self._widgets:
self._scroll_to(self._widgets[self._order[-1]])
def clear(self):
"""Remove all items and reset state."""
self._selected = None

View File

@@ -21,14 +21,9 @@ import cairo
log = logging.getLogger(__name__)
BAR_HEIGHT = 50
BLOCK_GAP = 2
BLOCK_COLOR = (0.25, 0.25, 0.30)
BLOCK_ACTIVE_COLOR = (0.35, 0.45, 0.60)
BLOCK_HOVER_COLOR = (0.30, 0.35, 0.40)
BLOCK_GENERATING_COLOR = (0.30, 0.40, 0.35)
BAR_COLOR = (0.20, 0.20, 0.25)
CURSOR_COLOR = (0.9, 0.2, 0.2)
MARKER_COLOR = (0.9, 0.8, 0.2)
TEXT_COLOR = (0.8, 0.8, 0.8)
class ScrubBar(Gtk.DrawingArea):
@@ -170,80 +165,36 @@ class ScrubBar(Gtk.DrawingArea):
# -- Drawing --
def _draw(self, area, cr, width, height):
if not self._manifest or self._total_duration <= 0:
cr.set_source_rgb(0.15, 0.15, 0.15)
cr.rectangle(0, 0, width, height)
cr.fill()
# Solid background — always full width
cr.set_source_rgb(*BAR_COLOR)
cr.rectangle(0, 0, width, height)
cr.fill()
if self._total_duration <= 0:
return
# Draw segment blocks
for seg in self._manifest:
x, w = self._segment_rect(seg, width)
idx = seg["index"]
# Block color based on state
if idx == self._active_index:
cr.set_source_rgb(*BLOCK_ACTIVE_COLOR)
elif idx == self._hover_index:
cr.set_source_rgb(*BLOCK_HOVER_COLOR)
elif self._proxy_states.get(idx) == "generating":
cr.set_source_rgb(*BLOCK_GENERATING_COLOR)
else:
cr.set_source_rgb(*BLOCK_COLOR)
cr.rectangle(x, 0, w, height)
cr.fill()
# Segment label
dur = seg["duration"]
m, s = divmod(int(dur), 60)
label = f"S{idx}" if w < 40 else f"S{idx} {m}:{s:02d}"
cr.set_source_rgb(*TEXT_COLOR)
cr.set_font_size(11)
cr.move_to(x + 4, height - 6)
cr.show_text(label)
# Proxy state indicator
state = self._proxy_states.get(idx)
if state == "ready":
cr.set_source_rgb(0.3, 0.7, 0.3)
cr.arc(x + w - 8, 8, 3, 0, 6.28)
cr.fill()
elif state == "generating":
cr.set_source_rgb(0.7, 0.7, 0.3)
cr.arc(x + w - 8, 8, 3, 0, 6.28)
cr.fill()
# Frame thumbnails at their timestamp positions
for thumb in self._frame_thumbs:
tx = self._global_to_x(thumb["timestamp"], width)
tw, th = thumb["width"], thumb["height"]
# Center thumbnail on its timestamp, vertically centered in bar
x0 = tx - tw / 2
y0 = (height - th) / 2
cr.save()
# Dark outline for separation against any background
cr.set_source_rgba(0, 0, 0, 0.7)
cr.set_line_width(2)
cr.rectangle(x0 - 1, y0 - 1, tw + 2, th + 2)
cr.set_source_rgba(0, 0, 0, 0.6)
cr.set_line_width(1.5)
cr.rectangle(x0 - 0.5, y0 - 0.5, tw + 1, th + 1)
cr.stroke()
# Thumbnail
cr.set_source_surface(thumb["surface"], x0, y0)
cr.rectangle(x0, y0, tw, th)
cr.fill()
# Bright inner border
cr.set_source_rgba(1, 1, 1, 0.5)
cr.set_line_width(1)
cr.rectangle(x0, y0, tw, th)
cr.stroke()
cr.restore()
# Scene markers (on top of thumbs)
# Scene markers
cr.set_source_rgb(*MARKER_COLOR)
for ts in self._scene_markers:
mx = self._global_to_x(ts, width)
if 0 <= mx <= width:
cr.rectangle(mx, 0, 1, 6)
cr.rectangle(mx, 0, 1, 5)
cr.fill()
# Cursor
@@ -256,11 +207,11 @@ class ScrubBar(Gtk.DrawingArea):
# -- Geometry helpers --
def _segment_rect(self, seg, total_width):
"""Return (x, width) for a segment block."""
"""Return (x, width) for a segment's region."""
if self._total_duration <= 0:
return 0, 0
x = (seg["global_offset"] / self._total_duration) * total_width + BLOCK_GAP / 2
w = (seg["duration"] / self._total_duration) * total_width - BLOCK_GAP
x = (seg["global_offset"] / self._total_duration) * total_width
w = (seg["duration"] / self._total_duration) * total_width
return max(0, x), max(1, w)
def _global_to_x(self, global_time, total_width):
@@ -287,43 +238,36 @@ class ScrubBar(Gtk.DrawingArea):
def _on_pressed(self, gesture, n_press, x, y):
width = self.get_width()
gt = self._x_to_global(x, width)
# Activate segment if different
idx = self._segment_at_x(x, width)
if idx >= 0:
if idx != self._active_index:
# New segmentactivate it (proxy will be requested)
self._active_index = idx
self.emit("segment-activated", idx)
else:
# Already active — seek to click position
gt = self._x_to_global(x, width)
self.emit("scrub-position", gt)
self.queue_draw()
if idx >= 0 and idx != self._active_index:
self._active_index = idx
self.emit("segment-activated", idx)
# Always seek to click position
self.emit("scrub-position", gt)
self.queue_draw()
def _on_released(self, gesture, n_press, x, y):
self._scrubbing = False
def _on_motion(self, controller, x, y):
width = self.get_width()
old_hover = self._hover_index
self._hover_index = self._segment_at_x(x, width)
if self._hover_index != old_hover:
self.queue_draw()
# If scrubbing (dragging within active block), emit position
if self._scrubbing and self._active_index >= 0:
if self._scrubbing:
width = self.get_width()
gt = self._x_to_global(x, width)
gt = max(0, min(gt, self._total_duration))
# Switch segment if drag crosses boundary
idx = self._segment_at_x(x, width)
if idx >= 0 and idx != self._active_index:
self._active_index = idx
self.emit("segment-activated", idx)
self.emit("scrub-position", gt)
def _on_leave(self, controller):
if self._hover_index != -1:
self._hover_index = -1
self.queue_draw()
pass
def _on_drag_begin(self, gesture, start_x, start_y):
width = self.get_width()
idx = self._segment_at_x(start_x, width)
if idx >= 0 and idx == self._active_index:
self._scrubbing = True
self._scrubbing = True
def _on_drag_update(self, gesture, offset_x, offset_y):
if self._scrubbing:
@@ -333,6 +277,10 @@ class ScrubBar(Gtk.DrawingArea):
width = self.get_width()
gt = self._x_to_global(x, width)
gt = max(0, min(gt, self._total_duration))
idx = self._segment_at_x(x, width)
if idx >= 0 and idx != self._active_index:
self._active_index = idx
self.emit("segment-activated", idx)
self.emit("scrub-position", gt)
def _on_drag_end(self, gesture, offset_x, offset_y):

View File

@@ -173,6 +173,11 @@ class TranscriptPanel(Gtk.Box):
self._selected.clear()
self.emit("selection-changed")
def scroll_to_end(self):
"""Scroll to the bottom (latest transcript)."""
adj = self._scroll.get_vadjustment()
GLib.idle_add(lambda: adj.set_value(adj.get_upper()) or False)
def highlight_nearest(self, timestamp: float) -> None:
"""Scroll to and briefly highlight the transcript segment closest to *timestamp*."""
if not self._order: