using Gtk; using Dino.Entities; namespace Dino.Ui { public class FileVideoWidget : Widget { private const int MIN_WIDTH = 100; private const int MIN_HEIGHT = 100; private const int MAX_WIDTH = 600; private const int MAX_HEIGHT = 300; private Overlay overlay = new Overlay(); private FixedRatioVideo video = new FixedRatioVideo() { min_width = MIN_WIDTH, min_height = MIN_HEIGHT, max_width = MAX_WIDTH, max_height = MAX_HEIGHT }; private Gtk.Box video_overlay_toolbar = new Gtk.Box(Orientation.VERTICAL, 0) { halign = Align.END, valign = Align.START, margin_top = 10, margin_start = 10, margin_end = 10, margin_bottom = 10, vexpand = false, visible = false }; construct { layout_manager = new BinLayout(); check_widget_leak(this); } public FileVideoWidget(FileTransfer file_transfer) { this.halign = Align.START; this.add_css_class("file-video-widget"); MenuButton button = new MenuButton(); button.icon_name = "dino-view-more"; Menu menu_model = new Menu(); menu_model.append(_("Open"), "file.open"); menu_model.append(_("Save as…"), "file.save_as"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); button.popover = popover_menu; video_overlay_toolbar.append(button); video_overlay_toolbar.add_css_class("card"); video_overlay_toolbar.add_css_class("toolbar"); video_overlay_toolbar.add_css_class("compact-card-toolbar"); video_overlay_toolbar.set_cursor_from_name("default"); video.file = file_transfer.get_file(); overlay.set_child(video); overlay.add_overlay(video_overlay_toolbar); overlay.set_clip_overlay(video_overlay_toolbar, true); overlay.insert_after(this, null); EventControllerMotion motion_events = new EventControllerMotion(); this.add_controller(motion_events); motion_events.enter.connect(() => { video_overlay_toolbar.visible = true; }); motion_events.leave.connect(() => { if (button.popover != null && button.popover.visible) return; video_overlay_toolbar.visible = false; }); } public static bool can_display(FileTransfer file_transfer) { if (file_transfer.state != FileTransfer.State.COMPLETE || file_transfer.path == null || file_transfer.content_type == null) { return false; } string? mime_type = file_transfer.content_type.get_mime_type(); return mime_type != null && mime_type.has_prefix("video/"); } public override void dispose() { if (video.media_stream != null) video.media_stream.pause(); if (overlay != null && overlay.parent != null) overlay.unparent(); base.dispose(); } } private class FixedRatioVideo : Widget { public int min_width { get; set; default = -1; } public int max_width { get; set; default = int.MAX; } public int min_height { get; set; default = -1; } public int max_height { get; set; default = int.MAX; } public File? file { get { return inner.file; } set { inner.file = value; } } public MediaStream? media_stream { get { return inner.media_stream; } } private Gtk.Video inner = new Gtk.Video() { autoplay = false, loop = false }; construct { set_css_name("video"); inner.insert_after(this, null); this.notify.connect(queue_resize); check_widget_leak(this); } private void measure_target_size(out int width, out int height) { if (width_request != -1 && height_request != -1) { width = width_request; height = height_request; return; } width = min_width; height = min_height; if (inner.should_layout()) { int child_min, child_nat, child_min_baseline, child_nat_baseline; inner.measure(Orientation.HORIZONTAL, -1, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); width = int.max(child_nat, width); } width = int.min(width, max_width); if (inner.should_layout()) { int child_min, child_nat, child_min_baseline, child_nat_baseline; inner.measure(Orientation.VERTICAL, width, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); height = int.max(child_nat, height); } if (height > max_height) { height = max_height; width = min_width; if (inner.should_layout()) { int child_min, child_nat, child_min_baseline, child_nat_baseline; inner.measure(Orientation.HORIZONTAL, max_height, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); width = int.max(child_nat, width); } width = int.min(width, max_width); } } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { minimum_baseline = -1; natural_baseline = -1; int width, height; measure_target_size(out width, out height); if (orientation == Orientation.HORIZONTAL) { minimum = min_width; natural = int.max(min_width, int.min(width, max_width)); } else if (for_size == -1) { minimum = min_height; natural = int.max(min_height, int.min(height, max_height)); } else { minimum = natural = int.max(min_height, int.min(height * for_size / width, height)); } } public override void size_allocate(int width, int height, int baseline) { if (inner.should_layout()) inner.allocate(width, height, baseline, null); } public override SizeRequestMode get_request_mode() { return SizeRequestMode.HEIGHT_FOR_WIDTH; } public override void dispose() { inner.unparent(); base.dispose(); } } }