anotherim-desktop/main/src/ui/call_window/call_window.vala

260 lines
10 KiB
Vala
Raw Normal View History

2021-03-19 22:09:56 +00:00
using Dino.Entities;
using Gtk;
namespace Dino.Ui {
public class CallWindow : Gtk.Window {
public string counterpart_display_name { get; set; }
// TODO should find another place for this
public CallWindowController controller;
public Overlay overlay = new Overlay() { visible=true };
public EventBox event_box = new EventBox() { visible=true };
public CallBottomBar bottom_bar = new CallBottomBar() { visible=true };
public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true };
public HeaderBar header_bar = new HeaderBar() { show_close_button=true, visible=true };
public Revealer header_bar_revealer = new Revealer() { valign=Align.START, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true };
public Stack stack = new Stack() { visible=true };
public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { expand=true, visible=true };
private Widget? own_video = null;
private Box? own_video_border = new Box(Orientation.HORIZONTAL, 0) { expand=true }; // hack to draw a border around our own video, since we apparently can't draw a border around the Gst widget
private int own_video_width = 150;
private int own_video_height = 100;
private bool hide_controll_elements = false;
private uint hide_controll_handler = 0;
private Widget? main_widget = null;
construct {
header_bar.get_style_context().add_class("call-header-bar");
header_bar_revealer.add(header_bar);
this.get_style_context().add_class("dino-call-window");
bottom_bar_revealer.add(bottom_bar);
overlay.add_overlay(own_video_box);
overlay.add_overlay(own_video_border);
overlay.add_overlay(bottom_bar_revealer);
overlay.add_overlay(header_bar_revealer);
event_box.add(overlay);
add(event_box);
Util.force_css(own_video_border, "* { border: 1px solid #616161; background-color: transparent; }");
}
public CallWindow() {
event_box.events |= Gdk.EventMask.POINTER_MOTION_MASK;
event_box.events |= Gdk.EventMask.ENTER_NOTIFY_MASK;
event_box.events |= Gdk.EventMask.LEAVE_NOTIFY_MASK;
this.bind_property("counterpart-display-name", header_bar, "title", BindingFlags.SYNC_CREATE);
this.bind_property("counterpart-display-name", bottom_bar, "counterpart-display-name", BindingFlags.SYNC_CREATE);
event_box.motion_notify_event.connect(reveal_control_elements);
event_box.enter_notify_event.connect(reveal_control_elements);
event_box.leave_notify_event.connect(reveal_control_elements);
this.configure_event.connect(reveal_control_elements); // upon resizing
this.configure_event.connect(update_own_video_position);
this.set_titlebar(new OutsideHeaderBar(this.header_bar) { visible=true });
reveal_control_elements();
}
public void set_video_fallback(StreamInteractor stream_interactor, Conversation conversation) {
hide_controll_elements = false;
Box box = new Box(Orientation.HORIZONTAL, 0) { visible=true };
box.get_style_context().add_class("video-placeholder-box");
AvatarImage avatar = new AvatarImage() { hexpand=true, vexpand=true, halign=Align.CENTER, valign=Align.CENTER, height=100, width=100, visible=true };
avatar.set_conversation(stream_interactor, conversation);
box.add(avatar);
set_new_main_widget(box);
}
public void set_video(Widget widget) {
hide_controll_elements = true;
widget.visible = true;
set_new_main_widget(widget);
}
public void set_own_video(Widget? widget_) {
own_video_box.foreach((widget) => { own_video_box.remove(widget); });
own_video = widget_;
if (own_video == null) {
own_video = new Box(Orientation.HORIZONTAL, 0) { expand=true };
}
own_video.visible = true;
own_video.width_request = 150;
own_video.height_request = 100;
own_video_box.add(own_video);
own_video_border.visible = true;
update_own_video_position();
}
public void set_own_video_ratio(int width, int height) {
if (width / height > 150 / 100) {
this.own_video_width = 150;
this.own_video_height = height * 150 / width;
} else {
this.own_video_width = width * 100 / height;
this.own_video_height = 100;
}
own_video.width_request = own_video_width;
own_video.height_request = own_video_height;
update_own_video_position();
}
public void unset_own_video() {
own_video_box.foreach((widget) => { own_video_box.remove(widget); });
own_video_border.visible = false;
}
public void set_test_video() {
hide_controll_elements = true;
var pipeline = new Gst.Pipeline(null);
var src = Gst.ElementFactory.make("videotestsrc", null);
pipeline.add(src);
Gst.Video.Sink sink = (Gst.Video.Sink) Gst.ElementFactory.make("gtksink", null);
Gtk.Widget widget;
sink.get("widget", out widget);
widget.unparent();
pipeline.add(sink);
src.link(sink);
widget.visible = true;
pipeline.set_state(Gst.State.PLAYING);
sink.get_static_pad("sink").notify["caps"].connect(() => {
int width, height;
sink.get_static_pad("sink").caps.get_structure(0).get_int("width", out width);
sink.get_static_pad("sink").caps.get_structure(0).get_int("height", out height);
widget.width_request = width;
widget.height_request = height;
});
set_new_main_widget(widget);
}
private void set_new_main_widget(Widget widget) {
if (main_widget != null) overlay.remove(main_widget);
overlay.add(widget);
main_widget = widget;
}
public void set_status(string state) {
switch (state) {
case "requested":
2021-04-29 13:56:22 +00:00
header_bar.subtitle = _("Calling…");
2021-03-19 22:09:56 +00:00
break;
case "ringing":
header_bar.subtitle = _("Ringing…");
break;
case "establishing":
2021-04-29 13:56:22 +00:00
header_bar.subtitle = _("Connecting…");
2021-03-19 22:09:56 +00:00
break;
default:
header_bar.subtitle = null;
break;
}
}
public void show_counterpart_ended(string? reason_name, string? reason_text) {
hide_controll_elements = false;
reveal_control_elements();
string text = "";
if (reason_name == Xmpp.Xep.Jingle.ReasonElement.SUCCESS) {
text = _("%s ended the call").printf(counterpart_display_name);
} else if (reason_name == Xmpp.Xep.Jingle.ReasonElement.DECLINE || reason_name == Xmpp.Xep.Jingle.ReasonElement.BUSY) {
text = _("%s declined the call").printf(counterpart_display_name);
} else {
text = "The call has been terminated: " + (reason_name ?? "") + " " + (reason_text ?? "");
}
bottom_bar.show_counterpart_ended(text);
}
public bool reveal_control_elements() {
if (!bottom_bar_revealer.child_revealed) {
bottom_bar_revealer.set_reveal_child(true);
header_bar_revealer.set_reveal_child(true);
}
if (hide_controll_handler != 0) {
Source.remove(hide_controll_handler);
hide_controll_handler = 0;
}
if (!hide_controll_elements) {
return false;
}
hide_controll_handler = Timeout.add_seconds(3, () => {
if (!hide_controll_elements) {
return false;
}
if (bottom_bar.is_menu_active()) {
return true;
}
header_bar_revealer.set_reveal_child(false);
bottom_bar_revealer.set_reveal_child(false);
hide_controll_handler = 0;
return false;
});
return false;
}
private bool update_own_video_position() {
if (own_video == null) return false;
int width, height;
this.get_size(out width,out height);
own_video.margin_end = own_video.margin_bottom = own_video_border.margin_end = own_video_border.margin_bottom = 20;
own_video.margin_start = own_video_border.margin_start = width - own_video_width - 20;
own_video.margin_top = own_video_border.margin_top = height - own_video_height - 20;
return false;
}
}
/* Hack to make the CallHeaderBar feel like a HeaderBar (right click menu, double click, ..) although it isn't set as headerbar.
* OutsideHeaderBar is set as a headerbar and it doesn't take any space, but claims to take space (which is actually taken by CallHeaderBar).
*/
public class OutsideHeaderBar : Gtk.Box {
HeaderBar header_bar;
public OutsideHeaderBar(HeaderBar header_bar) {
this.header_bar = header_bar;
size_allocate.connect_after(on_header_bar_size_allocate);
header_bar.size_allocate.connect(on_header_bar_size_allocate);
}
public void on_header_bar_size_allocate() {
Allocation header_bar_alloc;
header_bar.get_allocation(out header_bar_alloc);
Allocation alloc;
get_allocation(out alloc);
alloc.height = header_bar_alloc.height;
set_allocation(alloc);
}
}
}