Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Group Drawers #2574

Merged
merged 8 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/bar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <vector>

#include "AModule.hpp"
#include "group.hpp"
#include "xdg-output-unstable-v1-client-protocol.h"

namespace waybar {
Expand Down Expand Up @@ -101,7 +102,7 @@ class Bar {
private:
void onMap(GdkEventAny *);
auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &, Gtk::Box *);
void getModules(const Factory &, const std::string &, waybar::Group *);
void setupAltFormatKeyForModule(const std::string &module_name);
void setupAltFormatKeyForModuleList(const char *module_list_name);
void setMode(const bar_mode &);
Expand Down
19 changes: 16 additions & 3 deletions include/group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,31 @@
#include <json/json.h>

#include "AModule.hpp"
#include "bar.hpp"
#include "factory.hpp"
#include "gtkmm/revealer.h"

namespace waybar {

class Group : public AModule {
public:
Group(const std::string&, const std::string&, const Json::Value&, bool);
~Group() = default;
virtual ~Group() = default;
auto update() -> void override;
operator Gtk::Widget&() override;

virtual Gtk::Box& getBox();
void addWidget(Gtk::Widget& widget);

bool handleMouseHover(GdkEventCrossing* const& e);

protected:
Gtk::Box box;
Gtk::Box revealer_box;
Gtk::Revealer revealer;
bool is_first_widget = true;
bool is_drawer = false;
std::string add_class_to_drawer_children;

void addHoverHandlerTo(Gtk::Widget& widget);
};

} // namespace waybar
39 changes: 38 additions & 1 deletion man/waybar.5.scd.in
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270.

## Grouping modules

Module groups allow stacking modules in the direction orthogonal to the bar direction. When the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally.
Module groups allow stacking modules in any direction. By default, when the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally. This can be changed with the "orientation" property.

A module group is defined by specifying a module named "group/some-group-name". The group must also be configured with a list of contained modules. Example:

Expand All @@ -263,6 +263,43 @@ A module group is defined by specifying a module named "group/some-group-name".

Valid options for the (optional) "orientation" property are: "horizontal", "vertical", "inherit", and "orthogonal" (default).

## Group Drawers

A group may hide all but one element, showing them only on mouse hover. In order to configure this, you can use the `drawer` property, whose value is an object with the following properties:

*transition-duration*: ++
typeof: integer ++
default: 500 ++
Defines the duration of the transition animation in milliseconds.

*children-class*: ++
typeof: string ++
default: "hidden" ++
Defines the CSS class to be applied to the hidden elements.

*transition-left-to-right*: ++
typeof: bool ++
default: true ++
Defines the direction of the transition animation. If true, the hidden elements will slide from left to right. If false, they will slide from right to left.
When the bar is vertical, it reads as top-to-bottom.

```
"group/power": {
"orientation": "inherit",
"drawer": {
"transition-duration": 500,
"children-class": "not-power",
"transition-left-to-right": false,
},
"modules": [
"custom/power", // First element is the "group leader" and won't ever be hidden
"custom/quit",
"custom/lock",
"custom/reboot",
Copy link

@StayPirate StayPirate Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the comma required on this line? I guess no.

]
},
```

# SUPPORTED MODULES

- *waybar-backlight(5)*
Expand Down
11 changes: 6 additions & 5 deletions src/bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ void waybar::Bar::handleSignal(int signal) {
}

void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
Gtk::Box* group = nullptr) {
waybar::Group* group = nullptr) {
auto module_list = group ? config[pos]["modules"] : config[pos];
if (module_list.isArray()) {
for (const auto& name : module_list) {
Expand All @@ -753,10 +753,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
auto id_name = ref.substr(6, hash_pos - 6);
auto class_name = hash_pos != std::string::npos ? ref.substr(hash_pos + 1) : "";

auto parent = group ? group : &this->box_;
auto vertical = parent->get_orientation() == Gtk::ORIENTATION_VERTICAL;
auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) ==
Gtk::ORIENTATION_VERTICAL;

auto group_module = new waybar::Group(id_name, class_name, config[ref], vertical);
getModules(factory, ref, &group_module->box);
getModules(factory, ref, group_module);
module = group_module;
} else {
module = factory.makeModule(ref);
Expand All @@ -765,7 +766,7 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
std::shared_ptr<AModule> module_sp(module);
modules_all_.emplace_back(module_sp);
if (group) {
group->pack_start(*module, false, false);
group->addWidget(*module);
} else {
if (pos == "modules-left") {
modules_left_.emplace_back(module_sp);
Expand Down
89 changes: 87 additions & 2 deletions src/group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,32 @@

#include <util/command.hpp>

#include "gdkmm/device.h"
#include "gtkmm/widget.h"

namespace waybar {

const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical, bool left_to_right) {
if (is_vertical) {
if (left_to_right) {
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_DOWN;
} else {
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP;
}
} else {
if (left_to_right) {
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT;
} else {
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT;
}
}
}

Group::Group(const std::string& name, const std::string& id, const Json::Value& config,
bool vertical)
: AModule(config, name, id, false, false),
box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
: AModule(config, name, id, true, true),
box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
revealer_box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
box.set_name(name_);
if (!id.empty()) {
box.get_style_context()->add_class(id);
Expand All @@ -29,12 +49,77 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
} else {
throw std::runtime_error("Invalid orientation value: " + orientation);
}

if (config_["drawer"].isObject()) {
is_drawer = true;

const auto& drawer_config = config_["drawer"];
const int transition_duration =
(drawer_config["transition-duration"].isInt() ? drawer_config["transition-duration"].asInt()
: 500);
add_class_to_drawer_children =
(drawer_config["children-class"].isString() ? drawer_config["children-class"].asString()
: "drawer-child");
const bool left_to_right = (drawer_config["transition-left-to-right"].isBool()
? drawer_config["transition-left-to-right"].asBool()
: true);

auto transition_type = getPreferredTransitionType(vertical, left_to_right);

revealer.set_transition_type(transition_type);
revealer.set_transition_duration(transition_duration);
revealer.set_reveal_child(false);

revealer.get_style_context()->add_class("drawer");

revealer.add(revealer_box);
box.pack_start(revealer);

addHoverHandlerTo(revealer);
}
}

bool Group::handleMouseHover(GdkEventCrossing* const& e) {
switch (e->type) {
case GDK_ENTER_NOTIFY:
revealer.set_reveal_child(true);
break;
case GDK_LEAVE_NOTIFY:
revealer.set_reveal_child(false);
break;
default:
break;
}

return true;
}

void Group::addHoverHandlerTo(Gtk::Widget& widget) {
widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK);
widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover));
widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover));
}

auto Group::update() -> void {
// noop
}

Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }

void Group::addWidget(Gtk::Widget& widget) {
getBox().pack_start(widget, false, false);

if (is_drawer) {
// Necessary because of GTK's hitbox detection
addHoverHandlerTo(widget);
if (!is_first_widget) {
widget.get_style_context()->add_class(add_class_to_drawer_children);
}
}

is_first_widget = false;
}

Group::operator Gtk::Widget&() { return box; }

} // namespace waybar