diff --git a/metadata/dock.xml b/metadata/dock.xml index 7343d75f..3133a930 100644 --- a/metadata/dock.xml +++ b/metadata/dock.xml @@ -12,9 +12,20 @@ true - <_short>Autohide duration + <_short>Autohide animation duration + <_long>Time (in milliseconds) the dock takes to show and hide 300 + + <_short>Autohide show delay + <_long>Amount of time (in milliseconds) the cursor needs to be in the zone before showing. + 300 + + + <_short>Autohide hide delay + <_long>Amount of time (in milliseconds) the cursor needs to be out of the zone before hiding. + 500 + <_short>Position bottom @@ -26,23 +37,50 @@ bottom <_name>Bottom + + left + <_name>Left + + + right + <_name>Right + <_short>Max icons per line <_long>If greater than 0, the dock will have a maximum number of entries per line, after which a new line is created. 0 - - <_short>Dock height + + <_short>Anchor to edges + <_long>Wether the dock should extend all the way to the left and right; or top and bottom depending on orientation. + false + + + <_short>Minimal height + 100 + + + <_short>Minimal width 100 <_short>Dock icons height 72 - - <_short>Edge offset - <_long>The distance from the cursor to the edge of screen to show the panel when it's hidden. + + <_short>Edge margin + <_long>Space between the dock the edge + 0 + + + <_short>Edge hotspot size + <_long>The distance to the edge of screen to place the cursor in to show the dock when it's hidden. + 20 + + + <_short>Ajacent edge hotspot size + <_long>The distance to the edge of adjacent screens to place the cursor in to show the dock when it's hidden. 20 diff --git a/metadata/panel.xml b/metadata/panel.xml index 2368108e..ac44ad44 100644 --- a/metadata/panel.xml +++ b/metadata/panel.xml @@ -21,8 +21,26 @@ <_short>Widgets Right volume network battery clock + + <_short>Anchor to edges + <_long>Wether the pannel should extend all the way to the left and right; or top and bottom depending on orientation. + true + + + <_short>Center panel contents + <_long>Ensure the panel’s contents are centered. Effectively: +If full_span is on, it will put the left and right widgets next to the center ones. +If full_span is off, both sides of the panel will take the same amount of space, so that the center widgets are at the middle of the screen. This will lead to unused space on the side that has the least space taken up by the widgets. + false + - <_short>Minimal Height + <_short>Minimal height + <_long>Minimum height the panel takes. + 32 + + + <_short>Minimal width + <_long>Minimum width the panel takes. 32 @@ -30,9 +48,20 @@ false - <_short>Autohide Duration + <_short>Autohide animation duration + <_long>Time (in milliseconds) the panel takes to expand and retract + 300 + + + <_short>Autohide show delay + <_long>Amount of time (in milliseconds) the cursor needs to be in the zone before showing. 300 + + <_short>Autohide hide delay + <_long>Amount of time (in milliseconds) the cursor needs to be out of the zone before hiding. + 500 + <_short>Outputs on which to show a panel instance <_long>A comma separated list of output names on which to show panel instances. Set to * wildcard for all outputs. @@ -49,10 +78,28 @@ bottom <_name>Bottom + + left + <_name>Left + + + right + <_name>Right + + + + <_short>Edge margin + <_long>Space between the panel the edge + 0 + + + <_short>Edge hotspot size + <_long>The distance to the edge of screen to place the cursor in to show the panel when it's hidden. + 20 - - <_short>Edge offset - <_long>The distance from the cursor to the edge of screen to show the panel when it's hidden. + + <_short>Ajacent edge hotspot size + <_long>The distance to the edge of adjacent screens to place the cursor in to show the panel when it's hidden. 20 @@ -527,8 +574,8 @@ Set to -1 to only run it by clicking the button. row - - <_short>Target height for workspace switcher widget in pixels + + <_short>Target size for workspace switcher widget in pixels 0 diff --git a/src/dock/dock.cpp b/src/dock/dock.cpp index 54d23899..8b653102 100644 --- a/src/dock/dock.cpp +++ b/src/dock/dock.cpp @@ -21,8 +21,12 @@ class WfDock::impl Gtk::FlowBox box; WfOption css_path{"dock/css_path"}; - WfOption dock_height{"dock/dock_height"}; WfOption entries_per_line{"dock/max_per_line"}; + WfOption position{"dock/position"}; + + // needed as a workaround to shrink down when removing items + WfOption height{"dock/minimal_height"}; + WfOption width{"dock/minimal_width"}; public: impl(WayfireOutput *output) @@ -72,6 +76,31 @@ class WfDock::impl }; entries_per_line.set_callback(update_entries_per_line); update_entries_per_line(); + + auto update_position = [=] () + { + if (position.value() == "bottom") + { + // this is not great, but we lack better options without doing a + // layout with boxes in boxes (ugly) or some sort of custom layout manager + box.set_orientation(Gtk::Orientation::HORIZONTAL); + box.set_direction(Gtk::TextDirection::LTR); + } else if (position.value() == "left") + { + box.set_orientation(Gtk::Orientation::VERTICAL); + box.set_direction(Gtk::TextDirection::LTR); + } else if (position.value() == "right") + { + box.set_orientation(Gtk::Orientation::VERTICAL); + box.set_direction(Gtk::TextDirection::RTL); + } else // top + { + box.set_orientation(Gtk::Orientation::HORIZONTAL); + box.set_direction(Gtk::TextDirection::LTR); + } + }; + position.set_callback(update_position); + update_position(); } void add_child(Gtk::Widget& widget) @@ -82,7 +111,7 @@ class WfDock::impl void rem_child(Gtk::Widget& widget) { box.remove(widget); - window->set_default_size(-1, dock_height); + window->set_default_size(width, height); } wl_surface *get_wl_surface() diff --git a/src/panel/panel.cpp b/src/panel/panel.cpp index 04fd2fc6..3e0cde60 100644 --- a/src/panel/panel.cpp +++ b/src/panel/panel.cpp @@ -52,6 +52,8 @@ class WayfirePanel::impl WayfireOutput *output; + WfOption panel_position{"panel/position"}; + WfOption center{"panel/center"}; WfOption panel_layer{"panel/layer"}; std::function set_panel_layer = [=] () { @@ -76,23 +78,83 @@ class WayfirePanel::impl } }; - WfOption minimal_panel_height{"panel/minimal_height"}; + void set_boxes_orientation(Gtk::Orientation orientation) + { + content_box.set_orientation(orientation); + left_box.set_orientation(orientation); + center_box.set_orientation(orientation); + right_box.set_orientation(orientation); + } + + void update_orientation() + { + bool is_horizontal = !(panel_position.value() == "left" or panel_position.value() == + "right"); // checking like this also works with the fallback being the top + + auto orientation = is_horizontal ? Gtk::Orientation::HORIZONTAL : Gtk::Orientation::VERTICAL; + set_boxes_orientation(orientation); + + if (center) + { + left_box.set_expand(true); + right_box.set_expand(true); + + int lmin, lnat, lminb, lnatb, rmin, rnat, rminb, rnatb, mmin, mnat, mminb, mnatb; + left_box.measure(orientation, -1, lmin, lnat, lminb, lnatb); + right_box.measure(orientation, -1, rmin, rnat, rminb, rnatb); + center_box.measure(orientation, -1, mmin, mnat, mminb, mnatb); + + if (is_horizontal) + { + content_box.set_size_request((std::max(lnat, rnat) * 2) + mnat, -1); + + left_box.set_halign(Gtk::Align::END); + right_box.set_halign(Gtk::Align::START); + left_box.set_valign(Gtk::Align::CENTER); + right_box.set_valign(Gtk::Align::CENTER); + } else + { + content_box.set_size_request(-1, (std::max(lnat, rnat) * 2) + mnat); + + left_box.set_halign(Gtk::Align::CENTER); + right_box.set_halign(Gtk::Align::CENTER); + left_box.set_valign(Gtk::Align::END); + right_box.set_valign(Gtk::Align::START); + } + } else + { + left_box.set_expand(false); + right_box.set_expand(false); + + content_box.set_size_request(-1, -1); + + if (is_horizontal) + { + left_box.set_halign(Gtk::Align::START); + right_box.set_halign(Gtk::Align::END); + left_box.set_valign(Gtk::Align::CENTER); + right_box.set_valign(Gtk::Align::CENTER); + } else + { + left_box.set_valign(Gtk::Align::START); + right_box.set_valign(Gtk::Align::END); + left_box.set_halign(Gtk::Align::CENTER); + right_box.set_halign(Gtk::Align::CENTER); + } + } + } void create_window() { window = std::make_unique(output, "panel"); - window->set_default_size(0, minimal_panel_height); window->add_css_class("wf-panel"); panel_layer.set_callback(set_panel_layer); set_panel_layer(); // initial setting - gtk_layer_set_anchor(window->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, true); - gtk_layer_set_anchor(window->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, true); - gtk_layer_set_margin(window->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, 0); - gtk_layer_set_margin(window->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, 0); window->present(); init_layout(); + update_orientation(); } void init_layout() @@ -109,10 +171,9 @@ class WayfirePanel::impl content_box.set_end_widget(right_box); content_box.set_hexpand(true); - - left_box.set_halign(Gtk::Align::START); + content_box.set_vexpand(true); center_box.set_halign(Gtk::Align::CENTER); - right_box.set_halign(Gtk::Align::END); + center_box.set_valign(Gtk::Align::CENTER); window->set_child(content_box); } @@ -324,6 +385,8 @@ class WayfirePanel::impl void handle_config_reload() { + update_orientation(); + for (auto& w : left_widgets) { w->handle_config_reload(); diff --git a/src/panel/widget.hpp b/src/panel/widget.hpp index 7452a901..fc8bd46f 100644 --- a/src/panel/widget.hpp +++ b/src/panel/widget.hpp @@ -7,8 +7,10 @@ #define DEFAULT_PANEL_HEIGHT "48" #define DEFAULT_ICON_SIZE 32 -#define PANEL_POSITION_BOTTOM "bottom" #define PANEL_POSITION_TOP "top" +#define PANEL_POSITION_BOTTOM "bottom" +#define PANEL_POSITION_LEFT "left" +#define PANEL_POSITION_RIGHT "right" class wayfire_config; class WayfireWidget diff --git a/src/panel/widgets/battery.cpp b/src/panel/widgets/battery.cpp index 350a93cb..9f05e25d 100644 --- a/src/panel/widgets/battery.cpp +++ b/src/panel/widgets/battery.cpp @@ -225,6 +225,24 @@ bool WayfireBatteryInfo::setup_dbus() return false; } +void WayfireBatteryInfo::update_layout() +{ + WfOption panel_position{"panel/position"}; + + if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT) + { + button_box.set_orientation(Gtk::Orientation::VERTICAL); + } else + { + button_box.set_orientation(Gtk::Orientation::HORIZONTAL); + } +} + +void WayfireBatteryInfo::handle_config_reload() +{ + update_layout(); +} + // TODO: simplify config loading void WayfireBatteryInfo::init(Gtk::Box *container) @@ -251,6 +269,8 @@ void WayfireBatteryInfo::init(Gtk::Box *container) button.set_child(button_box); button.property_scale_factor().signal_changed() .connect(sigc::mem_fun(*this, &WayfireBatteryInfo::update_icon)); + + update_layout(); } WayfireBatteryInfo::~WayfireBatteryInfo() diff --git a/src/panel/widgets/battery.hpp b/src/panel/widgets/battery.hpp index bf0d883d..d7342719 100644 --- a/src/panel/widgets/battery.hpp +++ b/src/panel/widgets/battery.hpp @@ -44,6 +44,9 @@ class WayfireBatteryInfo : public WayfireWidget void update_details(); void update_state(); + void update_layout(); + void handle_config_reload(); + void on_properties_changed( const Gio::DBus::Proxy::MapChangedProperties& properties, const std::vector& invalidated); diff --git a/src/panel/widgets/launchers.cpp b/src/panel/widgets/launchers.cpp index d1d82566..567bbfc7 100644 --- a/src/panel/widgets/launchers.cpp +++ b/src/panel/widgets/launchers.cpp @@ -45,6 +45,9 @@ bool WfLauncherButton::initialize(std::string name, std::string icon, std::strin return false; } + m_icon.set_halign(Gtk::Align::CENTER); + m_icon.set_valign(Gtk::Align::CENTER); + button.set_child(m_icon); button.add_css_class("widget-icon"); button.add_css_class("flat"); @@ -151,17 +154,34 @@ void WayfireLaunchers::init(Gtk::Box *container) { box.add_css_class("widget-icon"); box.add_css_class("launchers"); + container->append(box); + + box.set_halign(Gtk::Align::CENTER); + box.set_valign(Gtk::Align::CENTER); + handle_config_reload(); } void WayfireLaunchers::update_layout() { box.set_spacing(spacing); + + WfOption panel_position{"panel/position"}; + + if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT) + { + box.set_orientation(Gtk::Orientation::VERTICAL); + } else + { + box.set_orientation(Gtk::Orientation::HORIZONTAL); + } } void WayfireLaunchers::handle_config_reload() { + update_layout(); + for (auto child : box.get_children()) { box.remove(*child); diff --git a/src/panel/widgets/network.cpp b/src/panel/widgets/network.cpp index 5b9bb181..f6a8307d 100644 --- a/src/panel/widgets/network.cpp +++ b/src/panel/widgets/network.cpp @@ -440,8 +440,23 @@ void WayfireNetworkInfo::init(Gtk::Box *container) handle_config_reload(); } +void WayfireNetworkInfo::update_layout() +{ + WfOption panel_position{"panel/position"}; + + if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT) + { + button_content.set_orientation(Gtk::Orientation::VERTICAL); + } else + { + button_content.set_orientation(Gtk::Orientation::HORIZONTAL); + } +} + void WayfireNetworkInfo::handle_config_reload() { + update_layout(); + if (status_opt.value() == NETWORK_STATUS_ICON) { if (status.get_parent()) diff --git a/src/panel/widgets/network.hpp b/src/panel/widgets/network.hpp index e6ab1d90..445f5040 100644 --- a/src/panel/widgets/network.hpp +++ b/src/panel/widgets/network.hpp @@ -81,6 +81,9 @@ class WayfireNetworkInfo : public WayfireWidget void update_status(); void init(Gtk::Box *container); + + void update_layout(); void handle_config_reload(); + virtual ~WayfireNetworkInfo(); }; diff --git a/src/panel/widgets/separator.cpp b/src/panel/widgets/separator.cpp index 08c4eac8..f5b88295 100644 --- a/src/panel/widgets/separator.cpp +++ b/src/panel/widgets/separator.cpp @@ -11,4 +11,24 @@ void WayfireSeparator::init(Gtk::Box *container) { separator.add_css_class("separator"); container->append(separator); + + update_layout(); +} + +void WayfireSeparator::update_layout() +{ + WfOption panel_position{"panel/position"}; + + if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT) + { + separator.set_orientation(Gtk::Orientation::VERTICAL); + } else + { + separator.set_orientation(Gtk::Orientation::HORIZONTAL); + } +} + +void WayfireSeparator::handle_config_reload() +{ + update_layout(); } diff --git a/src/panel/widgets/separator.hpp b/src/panel/widgets/separator.hpp index bb70d3da..f21c8f94 100644 --- a/src/panel/widgets/separator.hpp +++ b/src/panel/widgets/separator.hpp @@ -11,6 +11,10 @@ class WayfireSeparator : public WayfireWidget WayfireSeparator(int pixels); virtual void init(Gtk::Box *container); + + void update_layout(); + void handle_config_reload(); + virtual ~WayfireSeparator() {} }; diff --git a/src/panel/widgets/tray/tray.cpp b/src/panel/widgets/tray/tray.cpp index 0e7062e4..c9b70911 100644 --- a/src/panel/widgets/tray/tray.cpp +++ b/src/panel/widgets/tray/tray.cpp @@ -36,6 +36,16 @@ void WayfireStatusNotifier::remove_item(const Glib::ustring & service) void WayfireStatusNotifier::update_layout() { icons_box.set_spacing(spacing); + + WfOption panel_position{"panel/position"}; + + if ((panel_position.value() == PANEL_POSITION_LEFT) || (panel_position.value() == PANEL_POSITION_RIGHT)) + { + icons_box.set_orientation(Gtk::Orientation::VERTICAL); + } else + { + icons_box.set_orientation(Gtk::Orientation::HORIZONTAL); + } } void WayfireStatusNotifier::handle_config_reload() diff --git a/src/panel/widgets/workspace-switcher.cpp b/src/panel/widgets/workspace-switcher.cpp index f91dca61..0071061f 100644 --- a/src/panel/widgets/workspace-switcher.cpp +++ b/src/panel/widgets/workspace-switcher.cpp @@ -33,7 +33,7 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) ipc_client->subscribe(this, {"output-layout-changed"}); ipc_client->subscribe(this, {"wset-workspace-changed"}); - workspace_switcher_target_height_opt.set_callback([=] () {set_height();}); + workspace_switcher_target_size_opt.set_callback([=] () {set_size();}); auto mode_cb = ([=] () { @@ -71,20 +71,27 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) mode_cb(); } -void WayfireWorkspaceSwitcher::set_height() +void WayfireWorkspaceSwitcher::set_size() { - double val = workspace_switcher_target_height_opt.value(); + double val = workspace_switcher_target_size_opt.value(); if (val == 0.0) { val = (double)WfOption{"panel/minimal_height"}.value(); } - if (workspace_switcher_mode.value() == "grid") + if (workspace_switcher_mode.value() == "row") + { + WfOption panel_position{"panel/position"}; + if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT) + { + val = val / grid_width; + } + } else if (workspace_switcher_mode.value() == "grid") { val = val / grid_height; } - workspace_switcher_target_height = val; + workspace_switcher_target_size = val; } void WayfireWorkspaceSwitcher::get_wsets() @@ -106,7 +113,7 @@ void WayfireWorkspaceSwitcher::get_wsets() grid_process_workspaces(data); } - set_height(); + set_size(); }); } @@ -142,8 +149,9 @@ std::pair WayfireWorkspaceSwitcher::get_workspace(WayfireWorkspaceBox WayfireWorkspaceWindow *w) { std::pair workspace; - double scaled_output_width = ws->get_scaled_width(); - double scaled_output_height = workspace_switcher_target_height; + auto size = ws->get_scaled_size(); + double scaled_output_width = size.first; + double scaled_output_height = size.second; workspace.first = std::floor((w->x + (w->w / 2)) / scaled_output_width) + this->current_ws_x; workspace.second = std::floor((w->y + (w->h / 2)) / scaled_output_height) + this->current_ws_y; return workspace; @@ -152,8 +160,9 @@ std::pair WayfireWorkspaceSwitcher::get_workspace(WayfireWorkspaceBox std::pair WayfireWorkspaceSwitcher::grid_get_workspace(WayfireWorkspaceWindow *w) { std::pair workspace; - double scaled_output_width = this->get_scaled_width(); - double scaled_output_height = workspace_switcher_target_height; + auto size = this->get_scaled_size(); + double scaled_output_width = size.first; + double scaled_output_height = size.second; workspace.first = std::floor((w->x + (w->w / 2)) / scaled_output_width) + this->current_ws_x; workspace.second = std::floor((w->y + (w->h / 2)) / scaled_output_height) + this->current_ws_y; return workspace; @@ -177,8 +186,9 @@ bool WayfireWorkspaceSwitcher::on_grid_get_child_position(Gtk::Widget *widget, G { if (auto w = static_cast(widget)) { - allocation.set_x(w->x + this->current_ws_x * this->get_scaled_width()); - allocation.set_y(w->y + this->current_ws_y * this->workspace_switcher_target_height); + auto size = this->get_scaled_size(); + allocation.set_x(w->x + this->current_ws_x * size.first); + allocation.set_y(w->y + this->current_ws_y * size.second); allocation.set_width(w->w); allocation.set_height(w->h); return true; @@ -251,16 +261,28 @@ void WayfireWorkspaceBox::on_workspace_clicked(int count, double x, double y) } } -double WayfireWorkspaceSwitcher::get_scaled_width() +std::pair WayfireWorkspaceSwitcher::get_scaled_size() { - return this->workspace_switcher_target_height * - (this->output_width / float(this->output_height)); + WfOption panel_position{"panel/position"}; + + if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT) + { + return { + workspace_switcher_target_size, + (workspace_switcher_target_size * this->output_height / float(this->output_width)) + }; + } else + { + return { + (workspace_switcher_target_size * this->output_width / float(this->output_height)), + workspace_switcher_target_size + }; + } } -int WayfireWorkspaceBox::get_scaled_width() +std::pair WayfireWorkspaceBox::get_scaled_size() { - return this->switcher->workspace_switcher_target_height * - (this->output_width / float(this->output_height)); + return this->switcher->get_scaled_size(); } bool WayfireWorkspaceBox::on_workspace_scrolled(double x, double y) @@ -347,7 +369,8 @@ void WayfireWorkspaceSwitcher::render_workspace(wf::json_t workspace, int j, int false); ws->set_hexpand(false); ws->set_vexpand(false); - ws->set_size_request(ws->get_scaled_width(), workspace_switcher_target_height); + auto size = get_scaled_size(); + ws->set_size_request(size.first, size.second); ws->add_controller(click_gesture); ws->add_controller(scroll_controller); box.append(*ws); @@ -464,8 +487,9 @@ void WayfireWorkspaceSwitcher::grid_process_workspaces(wf::json_t workspace_data auto ws = Gtk::make_managed(this); ws->output_id = output_data["id"].as_int(); ws->set_can_target(false); - auto ws_width = this->get_scaled_width() / this->grid_width; - auto ws_height = this->workspace_switcher_target_height / this->grid_height; + auto size = this->get_scaled_size(); + auto ws_width = size.first / this->grid_width; + auto ws_height = size.second / this->grid_height; ws->set_size_request(ws_width, ws_height); ws->add_css_class("workspace"); if ((workspace_data[i]["workspace"]["x"].as_int() == k) && @@ -485,8 +509,7 @@ void WayfireWorkspaceSwitcher::grid_process_workspaces(wf::json_t workspace_data ws = Gtk::make_managed(this); ws->output_id = output_data["id"].as_int(); - ws->set_size_request( - this->get_scaled_width(), this->workspace_switcher_target_height); + ws->set_size_request(size.first, size.second); ws->add_css_class("workspace"); if ((workspace_data[i]["workspace"]["x"].as_int() == k) && (workspace_data[i]["workspace"]["y"].as_int() == j)) @@ -579,8 +602,9 @@ void WayfireWorkspaceSwitcher::add_view(wf::json_t view_data) } } - double width = ws->get_scaled_width(); - double height = workspace_switcher_target_height; + auto size = ws->get_scaled_size(); + double width = size.first; + double height = size.second; v->x = x * (width / float(ws->output_width)); v->y = y * (height / float(ws->output_height)); @@ -648,8 +672,9 @@ void WayfireWorkspaceSwitcher::grid_add_view(wf::json_t view_data) return; } - double width = this->get_scaled_width(); - double height = workspace_switcher_target_height; + auto size = this->get_scaled_size(); + double width = size.first; + double height = size.second; v->x = x * (width / float(this->output_width)); v->y = y * (height / float(this->output_height)); diff --git a/src/panel/widgets/workspace-switcher.hpp b/src/panel/widgets/workspace-switcher.hpp index be5ed99d..617857f2 100644 --- a/src/panel/widgets/workspace-switcher.hpp +++ b/src/panel/widgets/workspace-switcher.hpp @@ -24,7 +24,7 @@ class WayfireWorkspaceWindow : public Gtk::Widget class WayfireWorkspaceSwitcher : public WayfireWidget, public IIPCSubscriber { std::string output_name; - void set_height(); + void set_size(); void on_event(wf::json_t data) override; void switcher_on_event(wf::json_t data); void grid_on_event(wf::json_t data); @@ -50,7 +50,7 @@ class WayfireWorkspaceSwitcher : public WayfireWidget, public IIPCSubscriber Gtk::Box switcher_box; Gtk::Grid switch_grid; Gtk::Overlay overlay; - double get_scaled_width(); + std::pair get_scaled_size(); std::unique_ptr button; int output_width, output_height; void init(Gtk::Box *container) override; @@ -64,8 +64,8 @@ class WayfireWorkspaceSwitcher : public WayfireWidget, public IIPCSubscriber int current_ws_x, current_ws_y; std::vector windows; WfOption workspace_switcher_mode{"panel/workspace_switcher_mode"}; - WfOption workspace_switcher_target_height_opt{"panel/workspace_switcher_target_height"}; - double workspace_switcher_target_height; + WfOption workspace_switcher_target_size_opt{"panel/workspace_switcher_target_size"}; + double workspace_switcher_target_size; WfOption workspace_switcher_render_views{"panel/workspace_switcher_render_views"}; }; @@ -76,7 +76,7 @@ class WayfireWorkspaceBox : public Gtk::Overlay public: int x_index, y_index; int output_id, output_width, output_height; - int get_scaled_width(); + std::pair get_scaled_size(); WayfireWorkspaceBox(WayfireWorkspaceSwitcher *switcher) { this->switcher = switcher; diff --git a/src/util/wf-autohide-window.cpp b/src/util/wf-autohide-window.cpp index 5031a98e..530a43b6 100644 --- a/src/util/wf-autohide-window.cpp +++ b/src/util/wf-autohide-window.cpp @@ -10,15 +10,19 @@ #include #include -#define AUTOHIDE_SHOW_DELAY 300 -#define AUTOHIDE_HIDE_DELAY 500 - WayfireAutohidingWindow::WayfireAutohidingWindow(WayfireOutput *output, const std::string& section) : position{section + "/position"}, - y_position{WfOption{section + "/autohide_duration"}}, - edge_offset{section + "/edge_offset"}, - autohide_opt{section + "/autohide"} + full_span{section + "/full_span"}, + minimal_width{section + "/minimal_width"}, + minimal_height{section + "/minimal_height"}, + autohide_animation{WfOption{section + "/autohide_duration"}}, + autohide_opt{section + "/autohide"}, + autohide_show_delay{section + "/autohide_show_delay"}, + autohide_hide_delay{section + "/autohide_hide_delay"}, + edge_margin{section + "/edge_margin"}, + edge_hotspot_size{section + "/edge_hotspot_size"}, + adjacent_edge_hotspot_size{section + "/adjacent_edge_hotspot_size"} { this->output = output; this->set_decorated(false); @@ -27,9 +31,17 @@ WayfireAutohidingWindow::WayfireAutohidingWindow(WayfireOutput *output, gtk_layer_set_monitor(this->gobj(), output->monitor->gobj()); gtk_layer_set_namespace(this->gobj(), "panel"); + this->position.set_callback([=] () { this->update_position(); }); + this->full_span.set_callback([=] () { this->update_position(); }); + this->edge_margin.set_callback([=] () { update_position(); }); this->update_position(); + const auto set_size = [=] () { this->set_size_request(minimal_width, minimal_height); }; + this->minimal_height.set_callback(set_size); + this->minimal_width.set_callback(set_size); + set_size(); + auto pointer_gesture = Gtk::EventControllerMotion::create(); signals.push_back(pointer_gesture->signal_enter().connect([=] (double x, double y) { @@ -39,7 +51,7 @@ WayfireAutohidingWindow::WayfireAutohidingWindow(WayfireOutput *output, } this->input_inside_panel = true; - y_position.animate(0); + autohide_animation.animate(0); start_draw_timer(); })); signals.push_back(pointer_gesture->signal_leave().connect([=] @@ -47,17 +59,27 @@ WayfireAutohidingWindow::WayfireAutohidingWindow(WayfireOutput *output, this->input_inside_panel = false; if (this->should_autohide()) { - this->schedule_hide(AUTOHIDE_HIDE_DELAY); + this->schedule_hide(autohide_hide_delay); } })); this->add_controller(pointer_gesture); this->setup_autohide(); - this->edge_offset.set_callback([=] () { this->setup_hotspot(); }); + this->edge_hotspot_size.set_callback([=] () { this->setup_hotspot(); }); this->autohide_opt.set_callback([=] { setup_autohide(); }); + auto display = Gdk::Display::get_default(); + auto monitors = display->get_monitors(); + signals.push_back(monitors->signal_items_changed().connect([=] (auto, auto, auto) + { + reinit_ext_hotspots(); + })); + + // wait for idle, so once everything is initialised. Else, things depend on loading order. + Glib::signal_idle().connect_once([=] () { this->reinit_ext_hotspots(); }); + if (!output->output) { std::cerr << "WARNING: Compositor does not support zwf_shell_manager_v2 " << \ @@ -91,6 +113,11 @@ WayfireAutohidingWindow::~WayfireAutohidingWindow() zwf_hotspot_v2_destroy(this->panel_hotspot); } + for (auto adjacent_edge_hotspot : adjacent_edges_hotspots) + { + zwf_hotspot_v2_destroy(adjacent_edge_hotspot); + } + for (auto handler : signals) { handler.disconnect(); @@ -116,6 +143,16 @@ static std::string check_position(std::string position) return WF_WINDOW_POSITION_BOTTOM; } + if (position == WF_WINDOW_POSITION_LEFT) + { + return WF_WINDOW_POSITION_LEFT; + } + + if (position == WF_WINDOW_POSITION_RIGHT) + { + return WF_WINDOW_POSITION_RIGHT; + } + std::cerr << "Bad position in config file, defaulting to top" << std::endl; return WF_WINDOW_POSITION_TOP; } @@ -133,6 +170,16 @@ static GtkLayerShellEdge get_anchor_edge(std::string position) return GTK_LAYER_SHELL_EDGE_BOTTOM; } + if (position == WF_WINDOW_POSITION_LEFT) + { + return GTK_LAYER_SHELL_EDGE_LEFT; + } + + if (position == WF_WINDOW_POSITION_RIGHT) + { + return GTK_LAYER_SHELL_EDGE_RIGHT; + } + assert(false); // not reached because check_position() } @@ -146,7 +193,7 @@ void WayfireAutohidingWindow::m_show_uncertain() { schedule_hide(0); return false; - }, AUTOHIDE_HIDE_DELAY); + }, autohide_hide_delay); } } @@ -155,18 +202,43 @@ void WayfireAutohidingWindow::update_position() /* Reset old anchors */ gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, false); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, false); /* Set new anchor */ - GtkLayerShellEdge anchor = get_anchor_edge(position); - gtk_layer_set_anchor(this->gobj(), anchor, true); + GtkLayerShellEdge edge = get_anchor_edge(position); + gtk_layer_set_anchor(this->gobj(), edge, true); + gtk_layer_set_margin(this->gobj(), edge, edge_margin); + + if (full_span) + { + if ((edge == GTK_LAYER_SHELL_EDGE_TOP) || (edge == GTK_LAYER_SHELL_EDGE_BOTTOM)) + { + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, true); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, true); + } else if ((edge == GTK_LAYER_SHELL_EDGE_LEFT) || (edge == GTK_LAYER_SHELL_EDGE_RIGHT)) + { + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, true); + } + } if (!output->output) { return; } + // need different measurements depending on position + if ((edge == GTK_LAYER_SHELL_EDGE_LEFT) || (edge == GTK_LAYER_SHELL_EDGE_RIGHT)) + { + get_allocated_height_or_width = &Gtk::Widget::get_allocated_width; + } else + { + get_allocated_height_or_width = &Gtk::Widget::get_allocated_height; + } + /* When the position changes, show an animation from the new edge. */ - y_position.animate(-this->get_allocated_height(), 0); + autohide_animation.animate(-(this->*get_allocated_height_or_width)(), 0); start_draw_timer(); m_show_uncertain(); setup_hotspot(); @@ -195,6 +267,97 @@ static zwf_hotspot_v2_listener hotspot_listener = { .leave = handle_hotspot_leave, }; +void WayfireAutohidingWindow::reinit_ext_hotspots() +{ + for (auto adjacent_edge_hotspot : adjacent_edges_hotspots) + { + zwf_hotspot_v2_destroy(adjacent_edge_hotspot); + } + + adjacent_edges_hotspots.clear(); + + uint32_t adjacent_edge; + auto position = check_position(this->position); + if (position == WF_WINDOW_POSITION_TOP) + { + adjacent_edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_BOTTOM; + } else if (position == WF_WINDOW_POSITION_BOTTOM) + { + adjacent_edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_TOP; + } else if (position == WF_WINDOW_POSITION_LEFT) + { + adjacent_edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_RIGHT; + } else if (position == WF_WINDOW_POSITION_RIGHT) + { + adjacent_edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_LEFT; + } + + Gdk::Rectangle geom_this; + this->output->monitor->get_geometry(geom_this); + auto pos = check_position(this->position); + + std::map wo_edg; + + for (auto& wo : *WayfireShellApp::get().get_wayfire_outputs()) + { + if (!wo->output || (wo.get() == this->output)) + { + continue; + } + + Gdk::Rectangle other_geo; + wo->monitor->get_geometry(other_geo); + + if (pos == WF_WINDOW_POSITION_TOP) + { + if (other_geo.get_y() + other_geo.get_height() == geom_this.get_y()) + { + wo_edg[wo.get()] = ZWF_OUTPUT_V2_HOTSPOT_EDGE_BOTTOM; + } + } else if (pos == WF_WINDOW_POSITION_BOTTOM) + { + if (other_geo.get_y() == geom_this.get_y() + geom_this.get_height()) + { + wo_edg[wo.get()] = ZWF_OUTPUT_V2_HOTSPOT_EDGE_TOP; + } + } else if (pos == WF_WINDOW_POSITION_LEFT) + { + if (other_geo.get_x() + other_geo.get_width() == geom_this.get_x()) + { + wo_edg[wo.get()] = ZWF_OUTPUT_V2_HOTSPOT_EDGE_RIGHT; + } + } else if (pos == WF_WINDOW_POSITION_RIGHT) + { + if (other_geo.get_x() == geom_this.get_x() + geom_this.get_width()) + { + wo_edg[wo.get()] = ZWF_OUTPUT_V2_HOTSPOT_EDGE_LEFT; + } + } + } + + for (auto pair : wo_edg) + { + adjacent_edges_hotspots.push_back(zwf_output_v2_create_hotspot(pair.first->output, + adjacent_edge, adjacent_edge_hotspot_size, autohide_show_delay)); + } + + adjacent_edge_callbacks->on_enter = [=] () + { + schedule_show(0); + }; + + adjacent_edge_callbacks->on_leave = [=] () + { + m_do_hide(); + }; + + for (auto ad_ed_ht : adjacent_edges_hotspots) + { + zwf_hotspot_v2_add_listener(ad_ed_ht, &hotspot_listener, + adjacent_edge_callbacks.get()); + } +} + /** * An autohide window needs 2 hotspots. * One of them is used to trigger autohide and is generally a tiny strip on the @@ -202,19 +365,13 @@ static zwf_hotspot_v2_listener hotspot_listener = { * * The other hotspot covers the whole window. It is used primarily to know when * the input leaves the window, in which case we need to hide the window again. + * + * A last one is optional and is placed as a mirror of the first one on an + * adjacent monitor, and serves to have more room to pop out the window */ void WayfireAutohidingWindow::setup_hotspot() { - /* No need to recreate hotspots if the height didn't change */ - if ((this->get_allocated_height() == last_hotspot_height) && (edge_offset == last_edge_offset)) - { - return; - } - - this->last_hotspot_height = get_allocated_height(); - this->last_edge_offset = edge_offset; - if (this->edge_hotspot) { zwf_hotspot_v2_destroy(edge_hotspot); @@ -226,17 +383,31 @@ void WayfireAutohidingWindow::setup_hotspot() } auto position = check_position(this->position); - uint32_t edge = (position == WF_WINDOW_POSITION_TOP) ? - ZWF_OUTPUT_V2_HOTSPOT_EDGE_TOP : ZWF_OUTPUT_V2_HOTSPOT_EDGE_BOTTOM; + uint32_t edge; + if (position == WF_WINDOW_POSITION_TOP) + { + edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_TOP; + } else if (position == WF_WINDOW_POSITION_BOTTOM) + { + edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_BOTTOM; + } else if (position == WF_WINDOW_POSITION_LEFT) + { + edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_LEFT; + } else if (position == WF_WINDOW_POSITION_RIGHT) + { + edge = ZWF_OUTPUT_V2_HOTSPOT_EDGE_RIGHT; + } this->edge_hotspot = zwf_output_v2_create_hotspot(output->output, - edge, edge_offset, AUTOHIDE_SHOW_DELAY); + edge, edge_hotspot_size, autohide_show_delay); this->panel_hotspot = zwf_output_v2_create_hotspot(output->output, - edge, this->get_allocated_height(), 0); // immediate + edge, (this->*get_allocated_height_or_width)(), 0); // immediate this->edge_callbacks = std::make_unique(); + this->adjacent_edge_callbacks = + std::make_unique(); this->panel_callbacks = std::make_unique(); @@ -266,12 +437,13 @@ void WayfireAutohidingWindow::setup_hotspot() this->input_inside_panel = false; if (this->should_autohide()) { - this->schedule_hide(AUTOHIDE_HIDE_DELAY); + this->schedule_hide(autohide_hide_delay); } }; zwf_hotspot_v2_add_listener(edge_hotspot, &hotspot_listener, edge_callbacks.get()); + zwf_hotspot_v2_add_listener(panel_hotspot, &hotspot_listener, panel_callbacks.get()); } @@ -335,7 +507,7 @@ bool WayfireAutohidingWindow::should_autohide() const bool WayfireAutohidingWindow::m_do_hide() { - y_position.animate(-get_allocated_height()); + autohide_animation.animate(-((this->*get_allocated_height_or_width)() + edge_margin)); start_draw_timer(); update_margin(); return false; // disconnect @@ -351,7 +523,7 @@ gboolean WayfireAutohidingWindow::update_animation(Glib::RefPtr update_margin(); // this->queue_draw(); // Once we've finished fading, stop this callback - return y_position.running() ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE; + return autohide_animation.running() ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE; } void WayfireAutohidingWindow::schedule_hide(int delay) @@ -372,7 +544,7 @@ void WayfireAutohidingWindow::schedule_hide(int delay) bool WayfireAutohidingWindow::m_do_show() { - y_position.animate(0); + autohide_animation.animate(0); start_draw_timer(); update_margin(); return false; // disconnect @@ -396,10 +568,10 @@ void WayfireAutohidingWindow::schedule_show(int delay) bool WayfireAutohidingWindow::update_margin() { - if (y_position.running()) + if (autohide_animation.running()) { gtk_layer_set_margin(this->gobj(), - get_anchor_edge(position), y_position); + get_anchor_edge(position), edge_margin + autohide_animation); // queue_draw does not work when the panel is hidden // so calling wl_surface_commit to make WM show the panel back if (get_surface()) @@ -466,7 +638,7 @@ void WayfireAutohidingWindow::unset_active_popover(WayfireMenuButton& button) if (should_autohide()) { - schedule_hide(AUTOHIDE_HIDE_DELAY); + schedule_hide(autohide_hide_delay); } } diff --git a/src/util/wf-autohide-window.hpp b/src/util/wf-autohide-window.hpp index cd6cd6c5..4ac84825 100644 --- a/src/util/wf-autohide-window.hpp +++ b/src/util/wf-autohide-window.hpp @@ -3,15 +3,18 @@ #include #include -#include "wf-popover.hpp" -#include #include +#include "wf-option-wrap.hpp" +#include "wf-popover.hpp" + struct WayfireOutput; struct zwf_hotspot_v2; #define WF_WINDOW_POSITION_TOP "top" #define WF_WINDOW_POSITION_BOTTOM "bottom" +#define WF_WINDOW_POSITION_LEFT "left" +#define WF_WINDOW_POSITION_RIGHT "right" struct WayfireAutohidingWindowHotspotCallbacks; /** @@ -27,9 +30,15 @@ class WayfireAutohidingWindow : public Gtk::Window * file options: * * 1. section/position - * 2. section/autohide_duration - * 3. section/edge_offset - * 4. section/autohide + * 2. section/full_span + * 3. section/minimal_height + * 4. section/minimal_width + * 5. section/autohide + * 6. section/autohide_duration + * 7. section/autohide_show_delay + * 8. section/autohide_hide_delay + * 9. section/edge_hotspot_size + * 10.section/adjacent_edge_hotspot_size */ WayfireAutohidingWindow(WayfireOutput *output, const std::string& section); WayfireAutohidingWindow(WayfireAutohidingWindow&&) = delete; @@ -82,24 +91,35 @@ class WayfireAutohidingWindow : public Gtk::Window std::vector signals; WfOption position; + WfOption full_span; void update_position(); - wf::animation::simple_animation_t y_position; - bool update_margin(); + WfOption minimal_width; + WfOption minimal_height; - WfOption edge_offset; - int last_edge_offset = -1; + wf::animation::simple_animation_t autohide_animation; + int (Gtk::Widget::*get_allocated_height_or_width)() const; + bool update_margin(); WfOption autohide_opt; bool last_autohide_value = autohide_opt; void setup_autohide(); void update_autohide(); + WfOption autohide_show_delay; + WfOption autohide_hide_delay; + + WfOption edge_margin; + bool auto_exclusive_zone = !autohide_opt; int auto_exclusive_zone_size = 0; void setup_auto_exclusive_zone(); void update_auto_exclusive_zone(); + WfOption edge_hotspot_size, adjacent_edge_hotspot_size; + int last_edge_hotspot_size = 0, last_adjacent_edge_hotspot_size = 0; + int last_edge_offset = -1; + sigc::connection pending_show, pending_hide; bool m_do_show(); bool m_do_hide(); @@ -111,12 +131,12 @@ class WayfireAutohidingWindow : public Gtk::Window /** Show the window but hide if no pointer input */ void m_show_uncertain(); - int32_t last_hotspot_height = -1; - bool input_inside_panel = false; - zwf_hotspot_v2 *edge_hotspot = NULL; - zwf_hotspot_v2 *panel_hotspot = NULL; - std::unique_ptr edge_callbacks; - std::unique_ptr panel_callbacks; + bool input_inside_panel = false; + zwf_hotspot_v2 *edge_hotspot = NULL, *panel_hotspot = NULL; + std::vector adjacent_edges_hotspots; + std::unique_ptr edge_callbacks, adjacent_edge_callbacks, + panel_callbacks; + void reinit_ext_hotspots(); void setup_hotspot(); sigc::connection popover_hide; diff --git a/src/util/wf-popover.cpp b/src/util/wf-popover.cpp index 0a38af47..045216e1 100644 --- a/src/util/wf-popover.cpp +++ b/src/util/wf-popover.cpp @@ -5,15 +5,24 @@ WayfireMenuButton::WayfireMenuButton(const std::string& section) : panel_position{section + "/position"} { add_css_class("flat"); - // m_popover.set_constrain_to(Gtk::POPOVER_CONSTRAINT_NONE); auto cb = [=] () { - // set_direction((std::string)panel_position == "top" ? - // Gtk::Arrow::DOWN : Gtk::Arrow::UP); + if (panel_position.value() == "bottom") + { + set_direction(Gtk::ArrowType::UP); + } else if (panel_position.value() == "left") + { + set_direction(Gtk::ArrowType::RIGHT); + } else if (panel_position.value() == "right") + { + set_direction(Gtk::ArrowType::LEFT); + } else // top, is also the fallback when invalid + { + set_direction(Gtk::ArrowType::DOWN); + } this->unset_popover(); - // m_popover.set_constrain_to(Gtk::POPOVER_CONSTRAINT_NONE); set_popover(m_popover); }; panel_position.set_callback(cb);