diff --git a/src/bin/clapper-app/clapper-app-window.c b/src/bin/clapper-app/clapper-app-window.c
index 96a511ec..8fb997d6 100644
--- a/src/bin/clapper-app/clapper-app-window.c
+++ b/src/bin/clapper-app/clapper-app-window.c
@@ -28,6 +28,9 @@
#include "clapper-app-file-dialog.h"
#include "clapper-app-utils.h"
+#define MIN_WINDOW_WIDTH 352
+#define MIN_WINDOW_HEIGHT 198
+
#define DEFAULT_WINDOW_WIDTH 1024
#define DEFAULT_WINDOW_HEIGHT 576
@@ -40,6 +43,8 @@
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define AXIS_WINS_OVER(a,b) ((a > 0 && a - 0.3 > b) || (a < 0 && a + 0.3 < b))
+#define MIN_STEP_DELAY 12000
+
#define GST_CAT_DEFAULT clapper_app_window_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -59,6 +64,7 @@ struct _ClapperAppWindow
GSettings *settings;
guint seek_timeout;
+ guint resize_tick_id;
gboolean key_held;
gboolean scrolling;
@@ -74,6 +80,12 @@ struct _ClapperAppWindow
#define parent_class clapper_app_window_parent_class
G_DEFINE_TYPE (ClapperAppWindow, clapper_app_window, GTK_TYPE_APPLICATION_WINDOW)
+typedef struct
+{
+ gint dest_width, dest_height;
+ gint64 last_tick;
+} ClapperAppWindowResizeData;
+
static guint16 instance_count = 0;
static inline GQuark
@@ -246,12 +258,16 @@ _open_subtitles_cb (ClapperGtkExtraMenuButton *button G_GNUC_UNUSED,
}
static void
-right_click_pressed_cb (GtkGestureClick *click, gint n_press,
+click_pressed_cb (GtkGestureClick *click, gint n_press,
gdouble x, gdouble y, ClapperAppWindow *self)
{
GdkCursor *cursor;
const gchar *cursor_name = NULL;
+ if (gtk_gesture_single_get_current_button (
+ GTK_GESTURE_SINGLE (click)) != GDK_BUTTON_SECONDARY)
+ return;
+
GST_LOG_OBJECT (self, "Right click pressed");
if ((cursor = gtk_widget_get_cursor (self->video)))
@@ -266,9 +282,152 @@ right_click_pressed_cb (GtkGestureClick *click, gint n_press,
}
}
+static gboolean
+_resize_tick (GtkWidget *widget, GdkFrameClock *frame_clock,
+ ClapperAppWindowResizeData *resize_data)
+{
+ gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
+
+ if (now - resize_data->last_tick >= MIN_STEP_DELAY) {
+ ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (widget);
+ gint win_width, win_height;
+
+ GST_LOG_OBJECT (self, "Resize step, last: %" G_GINT64_FORMAT
+ ", now: %" G_GINT64_FORMAT, resize_data->last_tick, now);
+
+ gtk_window_get_default_size (GTK_WINDOW (self), &win_width, &win_height);
+
+ if (win_width != resize_data->dest_width) {
+ gint width_diff = ABS (win_width - resize_data->dest_width);
+ gint step_size = (width_diff > 180) ? 120 : MAX (width_diff / 4, 1);
+
+ win_width += (win_width > resize_data->dest_width) ? -step_size : step_size;
+ }
+ if (win_height != resize_data->dest_height) {
+ gint height_diff = ABS (win_height - resize_data->dest_height);
+ gint step_size = (height_diff > 180) ? 120 : MAX (height_diff / 4, 1);
+
+ win_height += (win_height > resize_data->dest_height) ? -step_size : step_size;
+ }
+
+ gtk_window_set_default_size (GTK_WINDOW (self), win_width, win_height);
+
+ if (win_width == resize_data->dest_width
+ && win_height == resize_data->dest_height) {
+ GST_DEBUG_OBJECT (self, "Window resize finish");
+ self->resize_tick_id = 0;
+
+ return G_SOURCE_REMOVE;
+ }
+
+ resize_data->last_tick = now;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
static void
-right_click_released_cb (GtkGestureClick *click, gint n_press,
- gdouble x, gdouble y, ClapperAppWindow *self)
+_calculate_win_resize (gint win_w, gint win_h,
+ gint vid_w, gint vid_h, gint *dest_w, gint *dest_h)
+{
+ gdouble win_aspect = (gdouble) win_w / win_h;
+ gdouble vid_aspect = (gdouble) vid_w / vid_h;
+
+ if (win_aspect < vid_aspect) {
+ while (!G_APPROX_VALUE (fmod (win_w, vid_aspect), 0, FLT_EPSILON))
+ win_w++;
+
+ win_h = round ((gdouble) win_w / vid_aspect);
+
+ if (win_h < MIN_WINDOW_HEIGHT) {
+ _calculate_win_resize (G_MAXINT, MIN_WINDOW_HEIGHT, vid_w, vid_h, dest_w, dest_h);
+ return;
+ }
+ } else {
+ while (!G_APPROX_VALUE (fmod (win_h * vid_aspect, 1.0), 0, FLT_EPSILON))
+ win_h++;
+
+ win_w = round ((gdouble) win_h * vid_aspect);
+
+ if (win_w < MIN_WINDOW_WIDTH) {
+ _calculate_win_resize (MIN_WINDOW_WIDTH, G_MAXINT, vid_w, vid_h, dest_w, dest_h);
+ return;
+ }
+ }
+
+ *dest_w = win_w;
+ *dest_h = win_h;
+}
+
+static void
+_resize_window (ClapperAppWindow *self)
+{
+ ClapperPlayer *player;
+ ClapperStreamList *vstreams;
+ ClapperVideoStream *vstream;
+ GdkToplevelState toplevel_state, disallowed;
+
+ if (self->resize_tick_id != 0)
+ return;
+
+ toplevel_state = gdk_toplevel_get_state (GDK_TOPLEVEL (
+ gtk_native_get_surface (GTK_NATIVE (self))));
+ disallowed = (GDK_TOPLEVEL_STATE_MINIMIZED
+ | GDK_TOPLEVEL_STATE_MAXIMIZED
+ | GDK_TOPLEVEL_STATE_FULLSCREEN
+ | GDK_TOPLEVEL_STATE_TILED);
+
+ if ((toplevel_state & disallowed) > 0) {
+ GST_DEBUG_OBJECT (self, "Cannot resize window in disallowed state");
+ return;
+ }
+
+ player = clapper_app_window_get_player (self);
+ vstreams = clapper_player_get_video_streams (player);
+ vstream = CLAPPER_VIDEO_STREAM_CAST (
+ clapper_stream_list_get_current_stream (vstreams));
+
+ if (vstream) {
+ gint video_width = clapper_video_stream_get_width (vstream);
+ gint video_height = clapper_video_stream_get_height (vstream);
+
+ if (G_LIKELY (video_width > 0 && video_height > 0)) {
+ gint win_width, win_height, dest_width, dest_height;
+
+ gtk_window_get_default_size (GTK_WINDOW (self), &win_width, &win_height);
+
+ _calculate_win_resize (win_width, win_height,
+ video_width, video_height, &dest_width, &dest_height);
+
+ /* Only begin resize when not already at perfect size */
+ if (dest_width != win_width || dest_height != win_height) {
+ ClapperAppWindowResizeData *resize_data;
+
+ resize_data = g_new0 (ClapperAppWindowResizeData, 1);
+ resize_data->dest_width = dest_width;
+ resize_data->dest_height = dest_height;
+
+ GST_DEBUG_OBJECT (self, "Window resize start, dest: %ix%i",
+ resize_data->dest_width, resize_data->dest_height);
+
+ self->resize_tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
+ (GtkTickCallback) _resize_tick, resize_data, g_free);
+ }
+ }
+
+ gst_object_unref (vstream);
+ }
+}
+
+static void
+_handle_middle_click (ClapperAppWindow *self, GtkGestureClick *click)
+{
+ _resize_window (self);
+ gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+_handle_right_click (ClapperAppWindow *self, GtkGestureClick *click)
{
GdkSurface *surface;
GdkEventSequence *sequence;
@@ -289,6 +448,22 @@ right_click_released_cb (GtkGestureClick *click, gint n_press,
gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_CLAIMED);
}
+static void
+click_released_cb (GtkGestureClick *click, gint n_press,
+ gdouble x, gdouble y, ClapperAppWindow *self)
+{
+ switch (gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (click))) {
+ case GDK_BUTTON_MIDDLE:
+ _handle_middle_click (self, click);
+ break;
+ case GDK_BUTTON_SECONDARY:
+ _handle_right_click (self, click);
+ break;
+ default:
+ break;
+ }
+}
+
static void
drag_begin_cb (GtkGestureDrag *drag,
gdouble start_x, gdouble start_y, ClapperAppWindow *self)
@@ -998,6 +1173,9 @@ clapper_app_window_init (ClapperAppWindow *self)
GtkWidget *dummy_titlebar;
gint distance = 0;
+ gtk_widget_set_size_request (GTK_WIDGET (self),
+ MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
+
extra_opts = g_new0 (ClapperAppWindowExtraOptions, 1);
GST_TRACE ("Created window extra options: %p", extra_opts);
@@ -1126,6 +1304,11 @@ clapper_app_window_dispose (GObject *object)
{
ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (object);
+ if (self->resize_tick_id != 0) {
+ gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->resize_tick_id);
+ self->resize_tick_id = 0;
+ }
+
g_clear_handle_id (&self->seek_timeout, g_source_remove);
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_APP_TYPE_WINDOW);
@@ -1185,8 +1368,8 @@ clapper_app_window_class_init (ClapperAppWindowClass *klass)
gtk_widget_class_bind_template_callback (widget_class, key_pressed_cb);
gtk_widget_class_bind_template_callback (widget_class, key_released_cb);
- gtk_widget_class_bind_template_callback (widget_class, right_click_pressed_cb);
- gtk_widget_class_bind_template_callback (widget_class, right_click_released_cb);
+ gtk_widget_class_bind_template_callback (widget_class, click_pressed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, click_released_cb);
gtk_widget_class_bind_template_callback (widget_class, drag_begin_cb);
gtk_widget_class_bind_template_callback (widget_class, drag_update_cb);
diff --git a/src/bin/clapper-app/css/styles.css b/src/bin/clapper-app/css/styles.css
index f988f468..0f2e2293 100644
--- a/src/bin/clapper-app/css/styles.css
+++ b/src/bin/clapper-app/css/styles.css
@@ -1,8 +1,3 @@
-window.app {
- min-width: 352px;
- min-height: 198px;
-}
-
window .initialstate {
padding-left: 6px;
padding-right: 6px;
diff --git a/src/bin/clapper-app/ui/clapper-app-window.ui b/src/bin/clapper-app/ui/clapper-app-window.ui
index d25521b9..5dd2b924 100644
--- a/src/bin/clapper-app/ui/clapper-app-window.ui
+++ b/src/bin/clapper-app/ui/clapper-app-window.ui
@@ -47,9 +47,9 @@
@@ -68,8 +68,5 @@
-