mirror of
https://github.com/elementary/gala.git
synced 2024-09-11 07:05:30 +03:00
Rework close button (#1848)
This commit is contained in:
parent
35677f1c06
commit
0cd3f324c1
135
lib/CloseButton.vala
Normal file
135
lib/CloseButton.vala
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 elementary, Inc. (https://elementary.io)
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gala.CloseButton : Clutter.Actor {
|
||||||
|
private const uint ANIMATION_DURATION = 100;
|
||||||
|
private static Gee.HashMap<int, Gdk.Pixbuf?> close_pixbufs;
|
||||||
|
|
||||||
|
public signal void triggered (uint32 timestamp);
|
||||||
|
public float scale { get; construct set; }
|
||||||
|
|
||||||
|
// used to avoid changing hitbox of the button
|
||||||
|
private Clutter.Actor pixbuf_actor;
|
||||||
|
private bool is_pressed = false;
|
||||||
|
|
||||||
|
public CloseButton (float scale) {
|
||||||
|
Object (scale: scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static construct {
|
||||||
|
close_pixbufs = new Gee.HashMap<int, Gdk.Pixbuf?> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
construct {
|
||||||
|
reactive = true;
|
||||||
|
|
||||||
|
pixbuf_actor = new Clutter.Actor () {
|
||||||
|
pivot_point = { 0.5f, 0.5f }
|
||||||
|
};
|
||||||
|
add_child (pixbuf_actor);
|
||||||
|
|
||||||
|
var pixbuf = get_close_button_pixbuf (scale);
|
||||||
|
if (pixbuf != null) {
|
||||||
|
try {
|
||||||
|
var image = new Clutter.Image ();
|
||||||
|
Cogl.PixelFormat pixel_format = (pixbuf.get_has_alpha () ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888);
|
||||||
|
image.set_data (pixbuf.get_pixels (), pixel_format, pixbuf.width, pixbuf.height, pixbuf.rowstride);
|
||||||
|
|
||||||
|
pixbuf_actor.set_content (image);
|
||||||
|
pixbuf_actor.set_size (pixbuf.width, pixbuf.height);
|
||||||
|
set_size (pixbuf.width, pixbuf.height);
|
||||||
|
} catch (Error e) {
|
||||||
|
create_error_texture ();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
create_error_texture ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Gdk.Pixbuf? get_close_button_pixbuf (float scale) {
|
||||||
|
var height = Utils.scale_to_int (36, scale);
|
||||||
|
|
||||||
|
if (close_pixbufs[height] == null) {
|
||||||
|
try {
|
||||||
|
close_pixbufs[height] = new Gdk.Pixbuf.from_resource_at_scale (
|
||||||
|
Config.RESOURCEPATH + "/buttons/close.svg",
|
||||||
|
-1,
|
||||||
|
height,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} catch (Error e) {
|
||||||
|
critical (e.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return close_pixbufs[height];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void create_error_texture () {
|
||||||
|
// we'll just make this red so there's at least something as an
|
||||||
|
// indicator that loading failed. Should never happen and this
|
||||||
|
// works as good as some weird fallback-image-failed-to-load pixbuf
|
||||||
|
critical ("Could not create close button");
|
||||||
|
|
||||||
|
var size = Utils.scale_to_int (36, scale);
|
||||||
|
pixbuf_actor.set_size (size, size);
|
||||||
|
pixbuf_actor.background_color = { 255, 0, 0, 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAS_MUTTER45
|
||||||
|
public override bool button_press_event (Clutter.Event e) {
|
||||||
|
#else
|
||||||
|
public override bool button_press_event (Clutter.ButtonEvent e) {
|
||||||
|
#endif
|
||||||
|
var estimated_duration = (uint) (ANIMATION_DURATION * (scale_x - 0.8) / 0.2);
|
||||||
|
|
||||||
|
pixbuf_actor.save_easing_state ();
|
||||||
|
pixbuf_actor.set_easing_duration (estimated_duration);
|
||||||
|
pixbuf_actor.set_easing_mode (Clutter.AnimationMode.EASE_IN_OUT);
|
||||||
|
pixbuf_actor.set_scale (0.8, 0.8);
|
||||||
|
pixbuf_actor.restore_easing_state ();
|
||||||
|
|
||||||
|
is_pressed = true;
|
||||||
|
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAS_MUTTER45
|
||||||
|
public override bool button_release_event (Clutter.Event e) {
|
||||||
|
#else
|
||||||
|
public override bool button_release_event (Clutter.ButtonEvent e) {
|
||||||
|
#endif
|
||||||
|
reset_scale ();
|
||||||
|
|
||||||
|
if (is_pressed) {
|
||||||
|
triggered (e.get_time ());
|
||||||
|
is_pressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAS_MUTTER45
|
||||||
|
public override bool leave_event (Clutter.Event event) {
|
||||||
|
#else
|
||||||
|
public override bool leave_event (Clutter.CrossingEvent event) {
|
||||||
|
#endif
|
||||||
|
reset_scale ();
|
||||||
|
is_pressed = false;
|
||||||
|
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset_scale () {
|
||||||
|
var estimated_duration = (uint) (ANIMATION_DURATION * (1.0 - scale_x) / 0.2);
|
||||||
|
|
||||||
|
pixbuf_actor.save_easing_state ();
|
||||||
|
pixbuf_actor.set_easing_duration (estimated_duration);
|
||||||
|
pixbuf_actor.set_easing_mode (Clutter.AnimationMode.EASE_IN_OUT);
|
||||||
|
pixbuf_actor.set_scale (1.0, 1.0);
|
||||||
|
pixbuf_actor.restore_easing_state ();
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,6 @@ namespace Gala {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Gee.HashMap<int, Gdk.Pixbuf?>? resize_pixbufs = null;
|
private static Gee.HashMap<int, Gdk.Pixbuf?>? resize_pixbufs = null;
|
||||||
private static Gee.HashMap<int, Gdk.Pixbuf?>? close_pixbufs = null;
|
|
||||||
|
|
||||||
private static Gee.HashMultiMap<DesktopAppInfo, CachedIcon?> icon_cache;
|
private static Gee.HashMultiMap<DesktopAppInfo, CachedIcon?> icon_cache;
|
||||||
private static Gee.HashMap<Meta.Window, DesktopAppInfo> window_to_desktop_cache;
|
private static Gee.HashMap<Meta.Window, DesktopAppInfo> window_to_desktop_cache;
|
||||||
@ -332,70 +331,11 @@ namespace Gala {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the pixbuf that is used for close buttons throughout gala at a
|
|
||||||
* size of 36px
|
|
||||||
*
|
|
||||||
* @return the close button pixbuf or null if it failed to load
|
|
||||||
*/
|
|
||||||
public static Gdk.Pixbuf? get_close_button_pixbuf (float scale) {
|
|
||||||
var height = scale_to_int (36, scale);
|
|
||||||
|
|
||||||
if (close_pixbufs == null) {
|
|
||||||
close_pixbufs = new Gee.HashMap<int, Gdk.Pixbuf?> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (close_pixbufs[height] == null) {
|
|
||||||
try {
|
|
||||||
close_pixbufs[height] = new Gdk.Pixbuf.from_resource_at_scale (
|
|
||||||
Config.RESOURCEPATH + "/buttons/close.svg",
|
|
||||||
-1,
|
|
||||||
height,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} catch (Error e) {
|
|
||||||
warning (e.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return close_pixbufs[height];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new reactive ClutterActor at 36px with the close pixbuf
|
|
||||||
*
|
|
||||||
* @return The close button actor
|
|
||||||
*/
|
|
||||||
public static Clutter.Actor create_close_button (float scale) {
|
|
||||||
var texture = new Clutter.Actor ();
|
|
||||||
var pixbuf = get_close_button_pixbuf (scale);
|
|
||||||
|
|
||||||
texture.reactive = true;
|
|
||||||
|
|
||||||
if (pixbuf != null) {
|
|
||||||
try {
|
|
||||||
var image = new Clutter.Image ();
|
|
||||||
Cogl.PixelFormat pixel_format = (pixbuf.get_has_alpha () ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888);
|
|
||||||
image.set_data (pixbuf.get_pixels (), pixel_format, pixbuf.width, pixbuf.height, pixbuf.rowstride);
|
|
||||||
texture.set_content (image);
|
|
||||||
texture.set_size (pixbuf.width, pixbuf.height);
|
|
||||||
} catch (Error e) {}
|
|
||||||
} else {
|
|
||||||
// we'll just make this red so there's at least something as an
|
|
||||||
// indicator that loading failed. Should never happen and this
|
|
||||||
// works as good as some weird fallback-image-failed-to-load pixbuf
|
|
||||||
texture.set_size (scale_to_int (36, scale), scale_to_int (36, scale));
|
|
||||||
texture.background_color = { 255, 0, 0, 255 };
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Returns the pixbuf that is used for resize buttons throughout gala at a
|
* Returns the pixbuf that is used for resize buttons throughout gala at a
|
||||||
* size of 36px
|
* size of 36px
|
||||||
*
|
*
|
||||||
* @return the close button pixbuf or null if it failed to load
|
* @return the resize button pixbuf or null if it failed to load
|
||||||
*/
|
*/
|
||||||
public static Gdk.Pixbuf? get_resize_button_pixbuf (float scale) {
|
public static Gdk.Pixbuf? get_resize_button_pixbuf (float scale) {
|
||||||
var height = scale_to_int (36, scale);
|
var height = scale_to_int (36, scale);
|
||||||
|
@ -3,6 +3,7 @@ gala_lib_sources = files(
|
|||||||
'App.vala',
|
'App.vala',
|
||||||
'AppCache.vala',
|
'AppCache.vala',
|
||||||
'AppSystem.vala',
|
'AppSystem.vala',
|
||||||
|
'CloseButton.vala',
|
||||||
'Constants.vala',
|
'Constants.vala',
|
||||||
'DragDropAction.vala',
|
'DragDropAction.vala',
|
||||||
'Drawing/BufferSurface.vala',
|
'Drawing/BufferSurface.vala',
|
||||||
|
@ -23,9 +23,8 @@ public class Gala.Plugins.PIP.PopupWindow : Clutter.Actor {
|
|||||||
|
|
||||||
private Clutter.Actor clone;
|
private Clutter.Actor clone;
|
||||||
private Clutter.Actor container;
|
private Clutter.Actor container;
|
||||||
private Clutter.Actor close_button;
|
private Gala.CloseButton close_button;
|
||||||
private Clutter.Actor resize_button;
|
private Clutter.Actor resize_button;
|
||||||
private Clutter.ClickAction close_action;
|
|
||||||
private DragDropAction move_action;
|
private DragDropAction move_action;
|
||||||
|
|
||||||
private float begin_resize_width = 0.0f;
|
private float begin_resize_width = 0.0f;
|
||||||
@ -111,16 +110,13 @@ public class Gala.Plugins.PIP.PopupWindow : Clutter.Actor {
|
|||||||
|
|
||||||
set_position (x_position, y_position);
|
set_position (x_position, y_position);
|
||||||
|
|
||||||
close_action = new Clutter.ClickAction ();
|
close_button = new Gala.CloseButton (scale) {
|
||||||
close_action.clicked.connect (on_close_click_clicked);
|
opacity = 0
|
||||||
|
};
|
||||||
close_button = Gala.Utils.create_close_button (scale);
|
|
||||||
close_button.opacity = 0;
|
|
||||||
close_button.reactive = true;
|
|
||||||
// TODO: Check if close button should be on the right
|
// TODO: Check if close button should be on the right
|
||||||
close_button.add_constraint (new Clutter.AlignConstraint (this, Clutter.AlignAxis.X_AXIS, 0.0f));
|
close_button.add_constraint (new Clutter.AlignConstraint (this, Clutter.AlignAxis.X_AXIS, 0.0f));
|
||||||
close_button.add_constraint (new Clutter.AlignConstraint (this, Clutter.AlignAxis.Y_AXIS, 0.0f));
|
close_button.add_constraint (new Clutter.AlignConstraint (this, Clutter.AlignAxis.Y_AXIS, 0.0f));
|
||||||
close_button.add_action (close_action);
|
close_button.triggered.connect (on_close_click_clicked);
|
||||||
|
|
||||||
resize_button = Utils.create_resize_button (scale);
|
resize_button = Utils.create_resize_button (scale);
|
||||||
resize_button.opacity = 0;
|
resize_button.opacity = 0;
|
||||||
|
@ -91,7 +91,7 @@ public class Gala.WindowClone : Clutter.Actor {
|
|||||||
private ulong check_confirm_dialog_cb = 0;
|
private ulong check_confirm_dialog_cb = 0;
|
||||||
private bool in_slot_animation = false;
|
private bool in_slot_animation = false;
|
||||||
|
|
||||||
private Clutter.Actor close_button;
|
private Gala.CloseButton close_button;
|
||||||
private ActiveShape active_shape;
|
private ActiveShape active_shape;
|
||||||
private Clutter.Actor window_icon;
|
private Clutter.Actor window_icon;
|
||||||
private Tooltip window_title;
|
private Tooltip window_title;
|
||||||
@ -158,15 +158,10 @@ public class Gala.WindowClone : Clutter.Actor {
|
|||||||
private void reallocate () {
|
private void reallocate () {
|
||||||
var window_frame_rect = window.get_frame_rect ();
|
var window_frame_rect = window.get_frame_rect ();
|
||||||
|
|
||||||
var close_button_action = new Clutter.ClickAction ();
|
close_button = new Gala.CloseButton (monitor_scale_factor) {
|
||||||
close_button_action.clicked.connect (() => {
|
opacity = 0
|
||||||
close_window ();
|
};
|
||||||
});
|
close_button.triggered.connect (close_window);
|
||||||
close_button = Utils.create_close_button (monitor_scale_factor);
|
|
||||||
close_button.opacity = 0;
|
|
||||||
// block propagation of button release event to window clone
|
|
||||||
close_button.button_release_event.connect (() => { return Clutter.EVENT_STOP; });
|
|
||||||
close_button.add_action (close_button_action);
|
|
||||||
|
|
||||||
window_icon = new WindowIcon (window, WINDOW_ICON_SIZE, (int)Math.round (monitor_scale_factor));
|
window_icon = new WindowIcon (window, WINDOW_ICON_SIZE, (int)Math.round (monitor_scale_factor));
|
||||||
window_icon.opacity = 0;
|
window_icon.opacity = 0;
|
||||||
@ -577,11 +572,11 @@ public class Gala.WindowClone : Clutter.Actor {
|
|||||||
* dialog of the window we were going to delete. If that's the case, we request
|
* dialog of the window we were going to delete. If that's the case, we request
|
||||||
* to select our window.
|
* to select our window.
|
||||||
*/
|
*/
|
||||||
private void close_window () {
|
private void close_window (uint32 timestamp) {
|
||||||
unowned Meta.Display display = window.get_display ();
|
unowned var display = window.get_display ();
|
||||||
check_confirm_dialog_cb = display.window_entered_monitor.connect (check_confirm_dialog);
|
check_confirm_dialog_cb = display.window_entered_monitor.connect (check_confirm_dialog);
|
||||||
|
|
||||||
window.@delete (display.get_current_time ());
|
window.@delete (timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void check_confirm_dialog (int monitor, Meta.Window new_window) {
|
private void check_confirm_dialog (int monitor, Meta.Window new_window) {
|
||||||
@ -624,7 +619,7 @@ public class Gala.WindowClone : Clutter.Actor {
|
|||||||
selected ();
|
selected ();
|
||||||
break;
|
break;
|
||||||
case Clutter.Button.MIDDLE:
|
case Clutter.Button.MIDDLE:
|
||||||
close_window ();
|
close_window (wm.get_display ().get_current_time ());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user