diff --git a/metadata/meson.build b/metadata/meson.build index 056f18f..932be1e 100644 --- a/metadata/meson.build +++ b/metadata/meson.build @@ -9,5 +9,6 @@ install_data('join-views.xml', install_dir: wayfire.get_variable(pkgconfig: 'met install_data('keycolor.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('mag.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('showrepaint.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) +install_data('window-menu.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('water.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('workspace-names.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) diff --git a/metadata/window-menu.xml b/metadata/window-menu.xml new file mode 100644 index 0000000..1dcbf0e --- /dev/null +++ b/metadata/window-menu.xml @@ -0,0 +1,15 @@ + + + + <_short>Window Menu + Window Management + + + + diff --git a/src/meson.build b/src/meson.build index f27e8dc..0413e04 100644 --- a/src/meson.build +++ b/src/meson.build @@ -46,6 +46,10 @@ water = shared_module('water', 'water.cpp', dependencies: [wayfire, wlroots, wfconfig], install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) +window_menu = shared_module('window-menu', 'window-menu.cpp', + dependencies: [wayfire, wlroots, wfconfig], + install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) + workspace_names = shared_module('workspace-names', 'workspace-names.cpp', dependencies: [wayfire, wlroots, wfconfig, cairo], install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) diff --git a/src/window-menu.cpp b/src/window-menu.cpp new file mode 100644 index 0000000..58f5cc3 --- /dev/null +++ b/src/window-menu.cpp @@ -0,0 +1,162 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Moreau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include +#include +#include +#include +#include +#include +#include + +class wayfire_window_menu : public wf::plugin_interface_t +{ + /* The command should be set to a client that shows a menu window. */ + wf::option_wrapper_t command{"window-menu/command"}; + wf::option_wrapper_t app_id{"window-menu/app_id"}; + wf::point_t position_offset; + wayfire_view menu_view = nullptr; + wayfire_view target_view = nullptr; + + public: + void init() override + { + grab_interface->name = "window-menu"; + grab_interface->capabilities = 0; + + output->connect_signal("view-show-window-menu", &show_window_menu); + } + + wf::signal_connection_t view_mapped{[this] (wf::signal_data_t *data) + { + auto view = get_signaled_view(data); + if (target_view && (view->get_app_id() == std::string(app_id))) + { + view->set_decoration(nullptr); + + auto vg = target_view->get_output_geometry(); + auto position = wf::point_t{vg.x, vg.y} + position_offset; + + vg = view->get_wm_geometry(); + auto og = output->get_relative_geometry(); + int padding = 20; + og.x += padding; + og.y += padding; + og.width -= padding * 2 + vg.width; + og.height -= padding * 2 + vg.height; + if ((og.width <= 0) || (og.height <= 0)) + { + return; + } + + wlr_box box{og.x, og.y, og.width, og.height}; + wf::pointf_t p{(float)position.x, (float)position.y}; + p = target_view->transform_point(p); + wlr_box_closest_point(&box, p.x, p.y, &p.x, &p.y); + position.x = (int)p.x; + position.y = (int)p.y; + + ((wf::view_mapped_signal*)data)->is_positioned = true; + view->move(position.x, position.y); + + /* Place above other views */ + output->workspace->add_view(view, wf::LAYER_UNMANAGED); + + menu_view = view; + } + } + }; + + wf::signal_connection_t on_button{[=] (wf::signal_data_t *data) + { + auto ev = static_cast< + wf::input_event_signal*>(data); + + if (ev->event->state != WLR_BUTTON_PRESSED) + { + return; + } + + auto view = wf::get_core().get_cursor_focus_view(); + if (!menu_view || !view) + { + return; + } + + /* Check if the client of the views match, in case it's a + * subsurface/menu, meaning the views won't match but + * the underlying client object will */ + if (menu_view->get_client() != view->get_client()) + { + menu_view->close(); + } + } + }; + + wf::signal_connection_t view_unmapped{[this] (wf::signal_data_t *data) + { + auto view = get_signaled_view(data); + if (view && (menu_view == view)) + { + menu_view = target_view = nullptr; + output->disconnect_signal(&view_mapped); + output->disconnect_signal(&view_unmapped); + wf::get_core().disconnect_signal(&on_button); + } + } + }; + + wf::signal_connection_t show_window_menu{[this] (wf::signal_data_t *data) + { + if (target_view || menu_view) + { + return; + } + + position_offset = + ((wf::view_show_window_menu_signal*)data)->relative_position; + if (wf::get_core().run(std::string(command)) == -1) + { + return; + } + + /* Showing menu for this view */ + target_view = get_signaled_view(data); + output->connect_signal("view-mapped", &view_mapped); + output->connect_signal("view-unmapped", &view_unmapped); + wf::get_core().connect_signal("pointer_button", &on_button); + } + }; + + void fini() override + { + if (menu_view) + { + menu_view->close(); + } + } +}; + +DECLARE_WAYFIRE_PLUGIN(wayfire_window_menu);