feat(tray): Position using a module (#2595)

Closes: #1526
Closes: #314

* debug log

* semi-working prototype. works on the left and the center but not on the right

* fixes formatting

* fixes tests

* - fixed tray_width_change signal
- implements suggestions

* - fixes error with tray positioning

* - tries to fix tests. Does not work

* - fixes tests

* - implemented suggestions

* reverted formatting in comake and doc

* - changed unique_ptr to const reference

* - fixed formatting errors in code

* - actually fixed formatting(ran clang-format)

* - implemented suggestions

* - Added CHANGELOG.md entry(not sure about wording)

* - removed bar_settings from tray_manager::setup

* - fixed issue from rebase

* - fixed issue with tests from rebase

* implemented suggestions

* Update CHANGELOG.md

Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
This commit is contained in:
raffael0 2022-04-15 23:50:04 +02:00 committed by GitHub
parent 973b1fa3d3
commit 4961a7dcfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 190 additions and 45 deletions

View File

@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `internal/pulseaudio`: `reverse-scroll` option ([`#2664`](https://github.com/polybar/polybar/pull/2664))
- `custom/script`: Repeat interval for script failure (`interval-fail`) and `exec-if` (`interval-if`) ([`#943`](https://github.com/polybar/polybar/issues/943), [`#2606`](https://github.com/polybar/polybar/issues/2606), [`#2630`](https://github.com/polybar/polybar/pull/2630))
- `custom/text`: Loads the `format` setting, which supports the `<label>` tag, if the deprecated `content` is not defined ([`#1331`](https://github.com/polybar/polybar/issues/1331), [`#2673`](https://github.com/polybar/polybar/pull/2673), [`#2676`](https://github.com/polybar/polybar/pull/2676))
- Added experimental support for positioning the tray like a module
## [3.6.2] - 2022-04-03
### Fixed

View File

@ -37,8 +37,7 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
static make_type make(eventloop::loop&, bool only_initialize_values = false);
explicit bar(connection&, signal_emitter&, const config&, const logger&, eventloop::loop&, unique_ptr<screen>&&,
unique_ptr<tray_manager>&&, unique_ptr<tags::dispatch>&&, unique_ptr<tags::action_context>&&,
bool only_initialize_values);
unique_ptr<tags::dispatch>&&, unique_ptr<tags::action_context>&&, bool only_initialize_values);
~bar();
const bar_settings& settings() const;

View File

@ -67,6 +67,8 @@ class renderer : public renderer_interface,
double get_alignment_start(const alignment align) const override;
void apply_tray_position(const tags::context& context) override;
protected:
void fill_background();
void fill_overline(rgba color, double x, double w);

View File

@ -33,6 +33,8 @@ class renderer_interface {
*/
virtual double get_alignment_start(const alignment align) const = 0;
virtual void apply_tray_position(const tags::context& context) = 0;
protected:
/**
* Stores information about actions in the current render cycle.

View File

@ -104,6 +104,12 @@ namespace signals {
struct mapped_clients : public detail::value_signal<mapped_clients, unsigned int> {
using base_type::base_type;
};
struct tray_width_change : public detail::value_signal<tray_width_change, unsigned int> {
using base_type::base_type;
};
struct tray_pos_change : public detail::value_signal<tray_pos_change, int> {
using base_type::base_type;
};
} // namespace ui_tray
} // namespace signals

View File

@ -36,6 +36,8 @@ namespace signals {
} // namespace ui
namespace ui_tray {
struct mapped_clients;
struct tray_width_change;
struct tray_pos_change;
}
} // namespace signals

View File

@ -18,6 +18,7 @@
#endif
#include "modules/temperature.hpp"
#include "modules/text.hpp"
#include "modules/tray.hpp"
#include "modules/xbacklight.hpp"
#include "modules/xwindow.hpp"
#include "modules/xworkspaces.hpp"
@ -95,6 +96,8 @@ namespace {
return new xwindow_module(bar, move(module_name));
} else if (name == xworkspaces_module::TYPE) {
return new xworkspaces_module(bar, move(module_name));
} else if (name == tray_module::TYPE) {
return new tray_module(bar, move(module_name));
} else if (name == text_module::TYPE) {
return new text_module(bar, move(module_name));
} else if (name == script_module::TYPE) {

29
include/modules/tray.hpp Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "common.hpp"
#include "components/bar.hpp"
#include "modules/meta/static_module.hpp"
POLYBAR_NS
namespace modules {
class tray_module : public static_module<tray_module>,
public signal_receiver<SIGN_PRIORITY_TRAY, signals::ui_tray::tray_width_change> {
public:
explicit tray_module(const bar_settings& bar_settings, string name_);
string get_format() const;
bool build(builder* builder, const string& tag) const;
void update() {}
void teardown();
bool on(const signals::ui_tray::tray_width_change& evt) override;
static constexpr auto TYPE = "internal/tray";
private:
static constexpr const char* TAG_TRAY{"<tray>"};
int m_width{0};
};
} // namespace modules
POLYBAR_NS_END

View File

@ -33,6 +33,7 @@ namespace tags {
void apply_alignment(alignment align);
void apply_attr(attr_activation act, attribute attr);
void apply_reset();
void store_tray_position(int x_pos);
rgba get_bg() const;
rgba get_fg() const;
@ -43,6 +44,8 @@ namespace tags {
bool has_underline() const;
alignment get_alignment() const;
std::pair<alignment, int> get_relative_tray_position() const;
protected:
/**
* Background color
@ -77,8 +80,10 @@ namespace tags {
*/
alignment m_align{alignment::NONE};
private:
const bar_settings& m_settings;
std::pair<alignment, int> m_relative_tray_position{alignment::NONE, 0};
private:
const bar_settings &m_settings;
};
} // namespace tags

View File

@ -2,6 +2,7 @@
#include "common.hpp"
#include "components/renderer_interface.hpp"
#include "components/types.hpp"
#include "errors.hpp"
POLYBAR_NS
@ -33,7 +34,7 @@ namespace tags {
void handle_action(renderer_interface& renderer, mousebtn btn, bool closing, const string&& cmd);
void handle_offset(renderer_interface& renderer, extent_val offset);
void handle_alignment(renderer_interface& renderer, alignment a);
void handle_control(controltag ctrl);
void handle_control(renderer_interface& renderer, controltag ctrl);
private:
const logger& m_log;

View File

@ -36,6 +36,7 @@ namespace tags {
enum class controltag {
NONE = 0,
R, // Reset all open tags (B, F, T, o, u). Used at module edges
t // special tag for trays
};
enum class color_type { RESET = 0, COLOR };

View File

@ -61,24 +61,26 @@ struct tray_settings {
rgba foreground{};
bool transparent{false};
bool detached{false};
bool adaptive{false};
};
class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify, evt::client_message,
evt::configure_request, evt::resize_request, evt::selection_clear, evt::property_notify,
evt::reparent_notify, evt::destroy_notify, evt::map_notify, evt::unmap_notify>,
public signal_receiver<SIGN_PRIORITY_TRAY, signals::ui::visibility_change, signals::ui::dim_window,
signals::ui::update_background> {
signals::ui::update_background, signals::ui_tray::tray_pos_change> {
public:
using make_type = unique_ptr<tray_manager>;
static make_type make();
static make_type make(const bar_settings& settings);
explicit tray_manager(connection& conn, signal_emitter& emitter, const logger& logger, background_manager& back);
explicit tray_manager(connection& conn, signal_emitter& emitter, const logger& logger, background_manager& back,
const bar_settings& settings);
~tray_manager();
const tray_settings settings() const;
void setup(const bar_settings& bar_opts);
void setup();
void activate();
void activate_delayed(chrono::duration<double, std::milli> delay = 1s);
void deactivate(bool clear_selection = true);
@ -105,8 +107,9 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
void track_selection_owner(xcb_window_t owner);
void process_docking_request(xcb_window_t win);
int calculate_x(unsigned width, bool abspos = true) const;
int calculate_x(unsigned width) const;
int calculate_y(bool abspos = true) const;
unsigned short int calculate_w() const;
unsigned short int calculate_h() const;
@ -134,6 +137,7 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
bool on(const signals::ui::visibility_change& evt) override;
bool on(const signals::ui::dim_window& evt) override;
bool on(const signals::ui::update_background& evt) override;
bool on(const signals::ui_tray::tray_pos_change& evt) override;
private:
connection& m_connection;
@ -167,6 +171,8 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
mutex m_mtx{};
bool m_firstactivation{true};
const bar_settings& m_bar_opts;
};
POLYBAR_NS_END

View File

@ -101,6 +101,8 @@ set(POLY_SOURCES
${src_dir}/modules/xbacklight.cpp
${src_dir}/modules/xwindow.cpp
${src_dir}/modules/xworkspaces.cpp
${src_dir}/modules/tray.cpp
${src_dir}/tags/action_context.cpp
${src_dir}/tags/context.cpp

View File

@ -49,7 +49,6 @@ bar::make_type bar::make(loop& loop, bool only_initialize_values) {
logger::make(),
loop,
screen::make(),
tray_manager::make(),
tags::dispatch::make(*action_ctxt),
std::move(action_ctxt),
only_initialize_values);
@ -62,19 +61,20 @@ bar::make_type bar::make(loop& loop, bool only_initialize_values) {
* TODO: Break out all tray handling
*/
bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, loop& loop,
unique_ptr<screen>&& screen, unique_ptr<tray_manager>&& tray_manager, unique_ptr<tags::dispatch>&& dispatch,
unique_ptr<tags::action_context>&& action_ctxt, bool only_initialize_values)
unique_ptr<screen>&& screen, unique_ptr<tags::dispatch>&& dispatch, unique_ptr<tags::action_context>&& action_ctxt,
bool only_initialize_values)
: m_connection(conn)
, m_sig(emitter)
, m_conf(config)
, m_log(logger)
, m_loop(loop)
, m_screen(forward<decltype(screen)>(screen))
, m_tray(forward<decltype(tray_manager)>(tray_manager))
, m_dispatch(forward<decltype(dispatch)>(dispatch))
, m_action_ctxt(forward<decltype(action_ctxt)>(action_ctxt)) {
string bs{m_conf.section()};
m_tray = tray_manager::make(m_opts);
// Get available RandR outputs
auto monitor_name = m_conf.get(bs, "monitor", ""s);
auto monitor_name_fallback = m_conf.get(bs, "monitor-fallback", ""s);
@ -393,7 +393,7 @@ void bar::parse(string&& data, bool force) {
auto rect = m_opts.inner_area();
if (m_tray && !m_tray->settings().detached && m_tray->settings().configured_slots) {
if (m_tray && !m_tray->settings().detached && m_tray->settings().configured_slots && !m_tray->settings().adaptive) {
auto trayalign = m_tray->settings().align;
auto traywidth = m_tray->settings().configured_w;
if (trayalign == alignment::LEFT) {
@ -904,7 +904,7 @@ void bar::start() {
m_renderer->end();
m_log.trace("bar: Setup tray manager");
m_tray->setup(static_cast<const bar_settings&>(m_opts));
m_tray->setup();
broadcast_visibility();
}

View File

@ -301,6 +301,9 @@ void builder::control(controltag tag) {
case controltag::R:
str = "R";
break;
case controltag::t:
str = "t";
break;
default:
throw runtime_error("Invalid controltag: " + to_string(to_integral(tag)));
}

View File

@ -811,4 +811,12 @@ bool renderer::on(const signals::ui::request_snapshot& evt) {
return true;
}
void renderer::apply_tray_position(const tags::context& context) {
if (context.get_relative_tray_position() != std::pair<alignment, int>()) {
int absolute_x = static_cast<int>(
block_x(context.get_relative_tray_position().first) + context.get_relative_tray_position().second);
m_sig.emit(signals::ui_tray::tray_pos_change{absolute_x});
}
}
POLYBAR_NS_END

40
src/modules/tray.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "modules/tray.hpp"
#include "modules/meta/base.inl"
POLYBAR_NS
namespace modules {
template class module<tray_module>;
tray_module::tray_module(const bar_settings& bar_settings, string name_)
: static_module<tray_module>(bar_settings, move(name_)) {
m_formatter->add(DEFAULT_FORMAT, TAG_TRAY, {TAG_TRAY});
m_sig.attach(this);
}
string tray_module::get_format() const {
return DEFAULT_FORMAT;
}
bool tray_module::build(builder* builder, const string& tag) const {
if (tag == TAG_TRAY) {
builder->control(tags::controltag::t);
extent_val offset_extent = {extent_type::PIXEL, static_cast<float>(m_width)};
builder->offset(offset_extent);
return true;
}
return false;
}
bool tray_module::on(const signals::ui_tray::tray_width_change& evt) {
m_width = evt.cast();
broadcast();
return true;
}
void tray_module::teardown() {
m_sig.detach(this);
}
} // namespace modules
POLYBAR_NS_END

View File

@ -70,6 +70,10 @@ namespace tags {
}
}
void context::store_tray_position(int x_pos) {
m_relative_tray_position = std::make_pair(get_alignment(), x_pos);
}
void context::apply_reset() {
m_bg = m_settings.background;
m_fg = m_settings.foreground;
@ -111,6 +115,10 @@ namespace tags {
alignment context::get_alignment() const {
return m_align;
}
std::pair<alignment, int> context::get_relative_tray_position() const {
return m_relative_tray_position;
}
} // namespace tags
POLYBAR_NS_END

View File

@ -2,11 +2,12 @@
#include <algorithm>
#include "components/renderer.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "settings.hpp"
#include "tags/parser.hpp"
#include "utils/color.hpp"
#include "components/logger.hpp"
POLYBAR_NS
@ -74,7 +75,7 @@ namespace tags {
m_ctxt->apply_ul(el.tag_data.color);
break;
case tags::syntaxtag::P:
handle_control(el.tag_data.ctrl);
handle_control(renderer, el.tag_data.ctrl);
break;
case tags::syntaxtag::l:
handle_alignment(renderer, alignment::LEFT);
@ -113,6 +114,7 @@ namespace tags {
for (auto a : {alignment::LEFT, alignment::CENTER, alignment::RIGHT}) {
m_action_ctxt.set_alignment_start(a, renderer.get_alignment_start(a));
}
renderer.apply_tray_position(*m_ctxt);
auto num_unclosed = m_action_ctxt.num_unclosed();
@ -152,11 +154,14 @@ namespace tags {
renderer.change_alignment(*m_ctxt);
}
void dispatch::handle_control(controltag ctrl) {
void dispatch::handle_control(renderer_interface& renderer, controltag ctrl) {
switch (ctrl) {
case controltag::R:
m_ctxt->apply_reset();
break;
case controltag::t:
m_ctxt->store_tray_position(renderer.get_x(*m_ctxt));
break;
default:
throw runtime_error("Unrecognized polybar control tag: " + to_string(static_cast<int>(ctrl)));
}

View File

@ -370,6 +370,8 @@ namespace tags {
}
return controltag::R;
case 't':
return controltag::t;
default:
throw control_error(s);
}

View File

@ -48,13 +48,14 @@ POLYBAR_NS
/**
* Create instance
*/
tray_manager::make_type tray_manager::make() {
tray_manager::make_type tray_manager::make(const bar_settings& settings) {
return std::make_unique<tray_manager>(
connection::make(), signal_emitter::make(), logger::make(), background_manager::make());
connection::make(), signal_emitter::make(), logger::make(), background_manager::make(), settings);
}
tray_manager::tray_manager(connection& conn, signal_emitter& emitter, const logger& logger, background_manager& back)
: m_connection(conn), m_sig(emitter), m_log(logger), m_background_manager(back) {
tray_manager::tray_manager(connection& conn, signal_emitter& emitter, const logger& logger, background_manager& back,
const bar_settings& settings)
: m_connection(conn), m_sig(emitter), m_log(logger), m_background_manager(back), m_bar_opts(settings) {
m_connection.attach_sink(this, SINK_PRIORITY_TRAY);
}
@ -66,7 +67,7 @@ tray_manager::~tray_manager() {
deactivate();
}
void tray_manager::setup(const bar_settings& bar_opts) {
void tray_manager::setup() {
const config& conf = config::make();
auto bs = conf.section();
string position;
@ -83,6 +84,8 @@ void tray_manager::setup(const bar_settings& bar_opts) {
m_opts.align = alignment::RIGHT;
} else if (position == "center") {
m_opts.align = alignment::CENTER;
} else if (position == "adaptive") {
m_opts.adaptive = true;
} else if (position != "none") {
return m_log.err("Disabling tray manager (reason: Invalid position \"" + position + "\")");
} else {
@ -90,9 +93,9 @@ void tray_manager::setup(const bar_settings& bar_opts) {
}
m_opts.detached = conf.get(bs, "tray-detached", false);
m_opts.height = bar_opts.size.h;
m_opts.height -= bar_opts.borders.at(edge::BOTTOM).size;
m_opts.height -= bar_opts.borders.at(edge::TOP).size;
m_opts.height = m_bar_opts.size.h;
m_opts.height -= m_bar_opts.borders.at(edge::BOTTOM).size;
m_opts.height -= m_bar_opts.borders.at(edge::TOP).size;
m_opts.height_fill = m_opts.height;
if (m_opts.height % 2 != 0) {
@ -105,16 +108,16 @@ void tray_manager::setup(const bar_settings& bar_opts) {
m_opts.height = maxsize;
}
m_opts.width_max = bar_opts.size.w;
m_opts.width_max = m_bar_opts.size.w;
m_opts.width = m_opts.height;
m_opts.orig_y = bar_opts.pos.y + bar_opts.borders.at(edge::TOP).size;
m_opts.orig_y = m_bar_opts.pos.y + m_bar_opts.borders.at(edge::TOP).size;
// Apply user-defined scaling
auto scale = conf.get(bs, "tray-scale", 1.0);
m_opts.width *= scale;
m_opts.height_fill *= scale;
auto inner_area = bar_opts.inner_area(true);
auto inner_area = m_bar_opts.inner_area(true);
switch (m_opts.align) {
case alignment::NONE:
@ -135,8 +138,8 @@ void tray_manager::setup(const bar_settings& bar_opts) {
}
// Set user-defined foreground and background colors.
m_opts.background = conf.get(bs, "tray-background", bar_opts.background);
m_opts.foreground = conf.get(bs, "tray-foreground", bar_opts.foreground);
m_opts.background = conf.get(bs, "tray-background", m_bar_opts.background);
m_opts.foreground = conf.get(bs, "tray-foreground", m_bar_opts.foreground);
if (m_opts.background.alpha_i() != 255) {
m_log.trace("tray: enable transparency");
@ -154,21 +157,21 @@ void tray_manager::setup(const bar_settings& bar_opts) {
int max_y;
if (m_opts.detached) {
max_x = bar_opts.monitor->w;
max_y = bar_opts.monitor->h;
max_x = m_bar_opts.monitor->w;
max_y = m_bar_opts.monitor->h;
} else {
max_x = inner_area.width;
max_y = inner_area.height;
}
m_opts.orig_x += units_utils::percentage_with_offset_to_pixel(offset_x, max_x, bar_opts.dpi_x);
m_opts.orig_y += units_utils::percentage_with_offset_to_pixel(offset_y, max_y, bar_opts.dpi_y);
m_opts.orig_x += units_utils::percentage_with_offset_to_pixel(offset_x, max_x, m_bar_opts.dpi_x);
m_opts.orig_y += units_utils::percentage_with_offset_to_pixel(offset_y, max_y, m_bar_opts.dpi_y);
;
m_opts.rel_x = m_opts.orig_x - bar_opts.pos.x;
m_opts.rel_y = m_opts.orig_y - bar_opts.pos.y;
m_opts.rel_x = m_opts.orig_x - m_bar_opts.pos.x;
m_opts.rel_y = m_opts.orig_y - m_bar_opts.pos.y;
// Put the tray next to the bar in the window stack
m_opts.sibling = bar_opts.window;
m_opts.sibling = m_bar_opts.window;
// Activate the tray manager
query_atom();
@ -379,6 +382,8 @@ void tray_manager::reconfigure_clients() {
remove_client(client, false);
}
}
m_sig.emit(signals::ui_tray::tray_width_change{calculate_w()});
}
/**
@ -794,8 +799,8 @@ void tray_manager::process_docking_request(xcb_window_t win) {
/**
* Calculate x position of tray window
*/
int tray_manager::calculate_x(unsigned int width, bool abspos) const {
auto x = abspos ? m_opts.orig_x : m_opts.rel_x;
int tray_manager::calculate_x(unsigned int width) const {
auto x = m_opts.orig_x;
if (m_opts.align == alignment::RIGHT) {
x -= ((m_opts.width + m_opts.spacing) * m_clients.size() + m_opts.spacing);
} else if (m_opts.align == alignment::CENTER) {
@ -1013,14 +1018,14 @@ void tray_manager::handle(const evt::selection_clear& evt) {
void tray_manager::handle(const evt::property_notify& evt) {
if (!m_activated) {
return;
}
}
// React an wallpaper change, if bar has transparency
if (m_opts.transparent && (evt->atom == _XROOTPMAP_ID || evt->atom == _XSETROOT_ID || evt->atom == ESETROOT_PMAP_ID)) {
redraw_window(true);
return;
}
}
if (evt->atom != _XEMBED_INFO) {
return;
}
@ -1161,4 +1166,11 @@ bool tray_manager::on(const signals::ui::update_background&) {
return false;
}
bool tray_manager::on(const signals::ui_tray::tray_pos_change& evt) {
m_opts.orig_x = m_bar_opts.inner_area(true).x + evt.cast();
reconfigure_window();
return true;
}
POLYBAR_NS_END

View File

@ -2,6 +2,7 @@
#include "common/test.hpp"
#include "components/logger.hpp"
#include "events/signal_emitter.hpp"
#include "gmock/gmock.h"
using namespace polybar;
@ -53,6 +54,8 @@ class FakeRenderer : public renderer_interface {
return 0;
};
void apply_tray_position(const tags::context&) override{};
private:
map<alignment, int> block_x = {
{alignment::LEFT, 0},
@ -70,6 +73,7 @@ class MockRenderer : public renderer_interface {
MOCK_METHOD(void, change_alignment, (const context& ctxt), (override));
MOCK_METHOD(double, get_x, (const context& ctxt), (const, override));
MOCK_METHOD(double, get_alignment_start, (const alignment align), (const, override));
MOCK_METHOD(void, apply_tray_position, (const polybar::tags::context& context), (override));
void DelegateToFake() {
ON_CALL(*this, render_offset).WillByDefault([this](const context& ctxt, const extent_val offset) {
@ -85,6 +89,10 @@ class MockRenderer : public renderer_interface {
ON_CALL(*this, get_alignment_start).WillByDefault([this](const alignment a) {
return fake.get_alignment_start(a);
});
ON_CALL(*this, apply_tray_position).WillByDefault([this](const context& context) {
return fake.apply_tray_position(context);
});
}
private: