Move most action state out of renderer

The renderer now only stores the positions of the actions because that
is specific to the renderer and because the actual position can only be
finalized after all the rendering is done because intitially the
positions are relative to the alignment and not the bar.
This commit is contained in:
patrick96 2021-01-10 20:49:50 +01:00 committed by Patrick Ziegler
parent 0a474bb2f2
commit 96239597ed
12 changed files with 275 additions and 208 deletions

View File

@ -10,6 +10,7 @@
#include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp"
#include "settings.hpp"
#include "tags/context.hpp"
#include "utils/math.hpp"
#include "x11/types.hpp"
#include "x11/window.hpp"
@ -66,7 +67,8 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
static make_type make(bool only_initialize_values = false);
explicit bar(connection&, signal_emitter&, const config&, const logger&, unique_ptr<screen>&&,
unique_ptr<tray_manager>&&, unique_ptr<tags::dispatch>&&, unique_ptr<taskqueue>&&, bool only_initialize_values);
unique_ptr<tray_manager>&&, unique_ptr<tags::dispatch>&&, unique_ptr<tags::action_context>&&,
unique_ptr<taskqueue>&&, bool only_initialize_values);
~bar();
const bar_settings settings() const;
@ -114,6 +116,7 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
unique_ptr<tray_manager> m_tray;
unique_ptr<renderer> m_renderer;
unique_ptr<tags::dispatch> m_dispatch;
unique_ptr<tags::action_context> m_action_ctxt;
unique_ptr<taskqueue> m_taskqueue;
bar_settings m_opts{};

View File

@ -32,9 +32,9 @@ struct alignment_block {
double y;
};
class renderer : public renderer_interface,
public signal_receiver<SIGN_PRIORITY_RENDERER, signals::ui::request_snapshot,
signals::parser::change_alignment, signals::parser::action_begin, signals::parser::action_end> {
class renderer
: public renderer_interface,
public signal_receiver<SIGN_PRIORITY_RENDERER, signals::ui::request_snapshot, signals::parser::change_alignment> {
public:
using make_type = unique_ptr<renderer>;
static make_type make(const bar_settings& bar);
@ -44,24 +44,26 @@ class renderer : public renderer_interface,
~renderer();
xcb_window_t window() const;
const vector<action_block> actions() const;
void begin(xcb_rectangle_t rect);
void end();
void flush();
#if 0
void reserve_space(edge side, unsigned int w);
#endif
void render_offset(const tags::context& ctxt, int pixels) override;
void render_text(const tags::context& ctxt, const string&&) override;
void action_open(const tags::context& ctxt, mousebtn btn, tags::action_t id) override;
void action_close(const tags::context& ctxt, tags::action_t id) override;
std::map<mousebtn, tags::action_t> get_actions(int x) override;
tags::action_t get_action(mousebtn btn, int x) override;
protected:
void fill_background();
void fill_overline(rgba color, double x, double w);
void fill_underline(rgba color, double x, double w);
void fill_borders();
void render_offset(const tags::context& ctxt, int pixels) override;
void render_text(const tags::context& ctxt, const string&&) override;
protected:
double block_x(alignment a) const;
double block_y(alignment a) const;
double block_w(alignment a) const;
@ -72,8 +74,6 @@ class renderer : public renderer_interface,
bool on(const signals::ui::request_snapshot& evt) override;
bool on(const signals::parser::change_alignment& evt) override;
bool on(const signals::parser::action_begin& evt) override;
bool on(const signals::parser::action_end& evt) override;
protected:
struct reserve_area {
@ -114,7 +114,7 @@ class renderer : public renderer_interface,
bool m_pseudo_transparency{false};
alignment m_align;
vector<action_block> m_actions;
std::unordered_map<tags::action_t, action_block> m_actions;
bool m_fixedcenter;
string m_snapshot_dst;

View File

@ -1,4 +1,6 @@
#pragma once
#include <map>
#include "common.hpp"
#include "tags/context.hpp"
POLYBAR_NS
@ -7,6 +9,12 @@ class renderer_interface {
public:
virtual void render_offset(const tags::context& ctxt, int pixels) = 0;
virtual void render_text(const tags::context& ctxt, const string&& str) = 0;
virtual void action_open(const tags::context& ctxt, mousebtn btn, tags::action_t id) = 0;
virtual void action_close(const tags::context& ctxt, tags::action_t id) = 0;
virtual std::map<mousebtn, tags::action_t> get_actions(int x) = 0;
virtual tags::action_t get_action(mousebtn btn, int x) = 0;
};
POLYBAR_NS_END

View File

@ -109,11 +109,11 @@ struct action {
string command{};
};
struct action_block : public action {
struct action_block {
mousebtn button{mousebtn::NONE};
alignment align{alignment::NONE};
double start_x{0.0};
double end_x{0.0};
bool active{true};
unsigned int width() const {
return static_cast<unsigned int>(end_x - start_x + 0.5);

View File

@ -135,12 +135,6 @@ namespace signals {
struct change_alignment : public detail::value_signal<change_alignment, alignment> {
using base_type::base_type;
};
struct action_begin : public detail::value_signal<action_begin, action> {
using base_type::base_type;
};
struct action_end : public detail::value_signal<action_end, mousebtn> {
using base_type::base_type;
};
} // namespace parser
} // namespace signals

View File

@ -46,8 +46,6 @@ namespace signals {
}
namespace parser {
struct change_alignment;
struct action_begin;
struct action_end;
} // namespace parser
} // namespace signals

View File

@ -44,8 +44,6 @@ namespace tags {
alignment get_alignment() const;
protected:
rgba get_color(color_value c, rgba fallback) const;
/**
* Background color
*/
@ -82,6 +80,53 @@ namespace tags {
private:
const bar_settings& m_settings;
};
/**
* An identifier for an action block.
*
* A value of NO_ACTION denotes an undefined identifier and is guaranteed to
* be smaller (<) than any valid identifier.
*
* If two action blocks overlap, the action with the higher identifier will
* be above.
*
* Except for NO_ACTION, negative values are not allowed
*/
using action_t = int;
static constexpr action_t NO_ACTION = -1;
class action_context {
public:
void reset();
action_t action_open(mousebtn btn, const string&& cmd, alignment align);
std::pair<action_t, mousebtn> action_close(mousebtn btn, alignment align);
string get_action(action_t id) const;
bool has_double_click() const;
size_t num_actions() const;
protected:
struct action_block {
action_block(const string&& cmd, mousebtn button, alignment align, bool is_open)
: cmd(std::move(cmd)), button(button), align(align), is_open(is_open){};
string cmd;
mousebtn button;
alignment align;
bool is_open;
};
/**
* Stores all currently known action blocks.
*
* The action_t type is meant as an index into this vector.
*/
std::vector<action_block> m_action_blocks;
};
} // namespace tags
POLYBAR_NS_END

View File

@ -23,20 +23,23 @@ namespace tags {
class dispatch {
public:
using make_type = unique_ptr<dispatch>;
static make_type make();
static make_type make(action_context& action_ctxt);
explicit dispatch(signal_emitter& emitter, const logger& logger);
void parse(const bar_settings& bar, renderer_interface&, string data);
explicit dispatch(signal_emitter& emitter, const logger& logger, action_context& action_ctxt);
void parse(const bar_settings& bar, renderer_interface&, const string&& data);
protected:
void handle_text(renderer_interface& renderer, context& ctxt, string&& data);
void handle_action(mousebtn btn, bool closing, const string&& cmd);
void handle_control(context& ctxt, controltag ctrl);
void handle_text(renderer_interface& renderer, string&& data);
void handle_action(renderer_interface& renderer, mousebtn btn, bool closing, const string&& cmd);
void handle_control(controltag ctrl);
private:
signal_emitter& m_sig;
vector<mousebtn> m_actions;
const logger& m_log;
unique_ptr<context> m_ctxt;
action_context& m_action_ctxt;
};
} // namespace tags

View File

@ -39,6 +39,8 @@ using namespace signals::ui;
* Create instance
*/
bar::make_type bar::make(bool only_initialize_values) {
auto action_ctxt = make_unique<tags::action_context>();
// clang-format off
return factory_util::unique<bar>(
connection::make(),
@ -47,7 +49,8 @@ bar::make_type bar::make(bool only_initialize_values) {
logger::make(),
screen::make(),
tray_manager::make(),
tags::dispatch::make(),
tags::dispatch::make(*action_ctxt),
std::move(action_ctxt),
taskqueue::make(),
only_initialize_values);
// clang-format on
@ -60,7 +63,7 @@ bar::make_type bar::make(bool only_initialize_values) {
*/
bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger,
unique_ptr<screen>&& screen, unique_ptr<tray_manager>&& tray_manager, unique_ptr<tags::dispatch>&& dispatch,
unique_ptr<taskqueue>&& taskqueue, bool only_initialize_values)
unique_ptr<tags::action_context>&& action_ctxt, unique_ptr<taskqueue>&& taskqueue, bool only_initialize_values)
: m_connection(conn)
, m_sig(emitter)
, m_conf(config)
@ -68,6 +71,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
, 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))
, m_taskqueue(forward<decltype(taskqueue)>(taskqueue)) {
string bs{m_conf.section()};
@ -361,7 +365,7 @@ void bar::parse(string&& data, bool force) {
m_renderer->begin(rect);
try {
m_dispatch->parse(settings(), *m_renderer, data);
m_dispatch->parse(settings(), *m_renderer, std::move(data));
} catch (const exception& err) {
m_log.err("Failed to parse contents (reason: %s)", err.what());
}
@ -369,11 +373,10 @@ void bar::parse(string&& data, bool force) {
m_renderer->end();
const auto check_dblclicks = [&]() -> bool {
for (auto&& action : m_renderer->actions()) {
if (static_cast<int>(action.button) >= static_cast<int>(mousebtn::DOUBLE_LEFT)) {
if (m_action_ctxt->has_double_click()) {
return true;
}
}
for (auto&& action : m_opts.actions) {
if (static_cast<int>(action.button) >= static_cast<int>(mousebtn::DOUBLE_LEFT)) {
return true;
@ -646,6 +649,35 @@ void bar::handle(const evt::motion_notify& evt) {
// scroll cursor is less important than click cursor, so we shouldn't return until we are sure there is no click
// action
bool found_scroll = false;
const auto& actions = m_renderer->get_actions(m_motion_pos);
const auto has_action = [&](const vector<mousebtn>& buttons) -> bool {
for (auto btn : buttons) {
if (actions.at(btn) != tags::NO_ACTION) {
return true;
}
}
return false;
};
if (has_action({mousebtn::LEFT, mousebtn::MIDDLE, mousebtn::RIGHT, mousebtn::DOUBLE_LEFT, mousebtn::DOUBLE_MIDDLE,
mousebtn::DOUBLE_RIGHT})) {
if (!string_util::compare(m_opts.cursor, m_opts.cursor_click)) {
m_opts.cursor = m_opts.cursor_click;
m_sig.emit(cursor_change{string{m_opts.cursor}});
}
return;
}
if (has_action({mousebtn::SCROLL_DOWN, mousebtn::SCROLL_UP})) {
if (!string_util::compare(m_opts.cursor, m_opts.cursor_scroll)) {
m_opts.cursor = m_opts.cursor_scroll;
m_sig.emit(cursor_change{string{m_opts.cursor}});
}
return;
}
const auto find_click_area = [&](const action& action) {
if (!m_opts.cursor_click.empty() &&
!(action.button == mousebtn::SCROLL_UP || action.button == mousebtn::SCROLL_DOWN ||
@ -664,20 +696,6 @@ void bar::handle(const evt::motion_notify& evt) {
return false;
};
for (auto&& action : m_renderer->actions()) {
if (action.test(m_motion_pos)) {
m_log.trace("Found matching input area");
if (find_click_area(action))
return;
}
}
if (found_scroll) {
if (!string_util::compare(m_opts.cursor, m_opts.cursor_scroll)) {
m_opts.cursor = m_opts.cursor_scroll;
m_sig.emit(cursor_change{string{m_opts.cursor}});
}
return;
}
for (auto&& action : m_opts.actions) {
if (!action.command.empty()) {
m_log.trace("Found matching fallback handler");
@ -723,19 +741,13 @@ void bar::handle(const evt::button_press& evt) {
m_buttonpress_pos = evt->event_x;
const auto deferred_fn = [&](size_t) {
/*
* Iterate over all defined actions in reverse order until matching action is found
* To properly handle nested actions we iterate in reverse because nested actions are added later than their
* surrounding action block
*/
auto actions = m_renderer->actions();
for (auto action = actions.rbegin(); action != actions.rend(); action++) {
if (action->button == m_buttonpress_btn && !action->active && action->test(m_buttonpress_pos)) {
tags::action_t action = m_renderer->get_action(m_buttonpress_btn, m_buttonpress_pos);
if (action != tags::NO_ACTION) {
m_log.trace("Found matching input area");
m_sig.emit(button_press{string{action->command}});
m_sig.emit(button_press{m_action_ctxt->get_action(action)});
return;
}
}
for (auto&& action : m_opts.actions) {
if (action.button == m_buttonpress_btn && !action.command.empty()) {

View File

@ -1,5 +1,7 @@
#include "components/renderer.hpp"
#include <cassert>
#include "cairo/context.hpp"
#include "components/config.hpp"
#include "events/signal.hpp"
@ -193,13 +195,6 @@ xcb_window_t renderer::window() const {
return m_window;
}
/**
* Get completed action blocks
*/
const vector<action_block> renderer::actions() const {
return m_actions;
}
/**
* Begin render routine
*/
@ -255,7 +250,8 @@ void renderer::begin(xcb_rectangle_t rect) {
void renderer::end() {
m_log.trace_x("renderer: end");
for (auto&& a : m_actions) {
for (auto& entry : m_actions) {
auto& a = entry.second;
a.start_x += block_x(a.align) + m_rect.x;
a.end_x += block_x(a.align) + m_rect.x;
}
@ -530,40 +526,6 @@ double renderer::block_h(alignment) const {
return m_rect.height;
}
#if 0
void renderer::reserve_space(edge side, unsigned int w) {
m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast<int>(side), w);
m_cleararea.side = side;
m_cleararea.size = w;
switch (side) {
case edge::NONE:
break;
case edge::TOP:
m_rect.y += w;
m_rect.height -= w;
break;
case edge::BOTTOM:
m_rect.height -= w;
break;
case edge::LEFT:
m_rect.x += w;
m_rect.width -= w;
break;
case edge::RIGHT:
m_rect.width -= w;
break;
case edge::ALL:
m_rect.x += w;
m_rect.y += w;
m_rect.width -= w * 2;
m_rect.height -= w * 2;
break;
}
}
#endif
/**
* Fill background color
*/
@ -716,14 +678,55 @@ void renderer::render_offset(const tags::context&, int pixels) {
m_blocks[m_align].x += pixels;
}
void renderer::action_open(const tags::context& ctxt, mousebtn btn, tags::action_t id) {
assert(btn != mousebtn::NONE);
action_block block;
block.align = ctxt.get_alignment();
block.button = btn;
block.start_x = m_blocks.at(block.align).x;
m_actions[id] = block;
}
void renderer::action_close(const tags::context&, tags::action_t id) {
auto& block = m_actions[id];
block.end_x = m_blocks.at(block.align).x;
}
std::map<mousebtn, tags::action_t> renderer::get_actions(int x) {
std::map<mousebtn, tags::action_t> buttons;
for (int i = static_cast<int>(mousebtn::NONE); i < static_cast<int>(mousebtn::BTN_COUNT); i++) {
buttons[static_cast<mousebtn>(i)] = tags::NO_ACTION;
}
for (auto&& entry : m_actions) {
tags::action_t id;
action_block action;
std::tie(id, action) = entry;
mousebtn btn = action.button;
// Higher IDs are higher in the action stack.
if (id > buttons[btn] && action.test(x)) {
buttons[action.button] = id;
}
}
return buttons;
}
tags::action_t renderer::get_action(mousebtn btn, int x) {
return get_actions(x)[btn];
}
/**
* Colorize the bounding box of created action blocks
*/
void renderer::highlight_clickable_areas() {
#ifdef DEBUG_HINTS
map<alignment, int> hint_num{};
for (auto&& action : m_actions) {
if (!action.active) {
for (auto&& entry : m_actions) {
auto&& action = entry.second;
int n = hint_num.find(action.align)->second++;
double x = action.start_x;
double y = m_rect.y;
@ -736,7 +739,6 @@ void renderer::highlight_clickable_areas() {
m_context->fill();
m_context->restore();
}
}
m_surface->flush();
#endif
}
@ -767,34 +769,4 @@ bool renderer::on(const signals::parser::change_alignment& evt) {
return true;
}
bool renderer::on(const signals::parser::action_begin& evt) {
auto a = evt.cast();
m_log.trace_x("renderer: action_begin(btn=%i, command=%s)", static_cast<int>(a.button), a.command);
action_block action{};
action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button;
action.align = m_align;
action.start_x = m_blocks.at(m_align).x;
action.command = a.command;
action.active = true;
m_actions.emplace_back(action);
return true;
}
bool renderer::on(const signals::parser::action_end& evt) {
auto btn = evt.cast();
/*
* Iterate actions in reverse and find the FIRST active action that matches
*/
m_log.trace_x("renderer: action_end(btn=%i)", static_cast<int>(btn));
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
if (action->active && action->align == m_align && action->button == btn) {
action->end_x = m_blocks.at(action->align).x;
action->active = false;
break;
}
}
return true;
}
POLYBAR_NS_END

View File

@ -1,8 +1,18 @@
#include "tags/context.hpp"
#include <cassert>
POLYBAR_NS
namespace tags {
static rgba get_color(color_value c, rgba fallback) {
if (c.type == color_type::RESET) {
return fallback;
} else {
return c.val;
}
}
context::context(const bar_settings& settings) : m_settings(settings) {
reset();
}
@ -104,13 +114,50 @@ namespace tags {
return m_align;
}
rgba context::get_color(color_value c, rgba fallback) const {
if (c.type == color_type::RESET) {
return fallback;
} else {
return c.val;
void action_context::reset() {
m_action_blocks.clear();
}
action_t action_context::action_open(mousebtn btn, const string&& cmd, alignment align) {
action_t id = m_action_blocks.size();
m_action_blocks.emplace_back(std::move(cmd), btn, align, true);
return id;
}
std::pair<action_t, mousebtn> action_context::action_close(mousebtn btn, alignment align) {
for (auto it = m_action_blocks.rbegin(); it != m_action_blocks.rend(); it++) {
if (it->is_open && it->align == align && (btn == mousebtn::NONE || it->button == btn)) {
it->is_open = false;
// Converts a reverse iterator into an index
return {std::distance(m_action_blocks.begin(), it.base()) - 1, it->button};
}
}
return {NO_ACTION, mousebtn::NONE};
}
string action_context::get_action(action_t id) const {
assert(id >= 0 && (unsigned)id < num_actions());
return m_action_blocks[id].cmd;
}
bool action_context::has_double_click() const {
for (auto&& a : m_action_blocks) {
if (a.button == mousebtn::DOUBLE_LEFT || a.button == mousebtn::DOUBLE_MIDDLE ||
a.button == mousebtn::DOUBLE_RIGHT) {
return true;
}
}
return false;
}
size_t action_context::num_actions() const {
return m_action_blocks.size();
}
} // namespace tags
POLYBAR_NS_END

View File

@ -17,23 +17,25 @@ namespace tags {
/**
* Create instance
*/
dispatch::make_type dispatch::make() {
return factory_util::unique<dispatch>(signal_emitter::make(), logger::make());
dispatch::make_type dispatch::make(action_context& action_ctxt) {
return factory_util::unique<dispatch>(signal_emitter::make(), logger::make(), action_ctxt);
}
/**
* Construct parser instance
*/
dispatch::dispatch(signal_emitter& emitter, const logger& logger) : m_sig(emitter), m_log(logger) {}
dispatch::dispatch(signal_emitter& emitter, const logger& logger, action_context& action_ctxt)
: m_sig(emitter), m_log(logger), m_action_ctxt(action_ctxt) {}
/**
* Process input string
*/
void dispatch::parse(const bar_settings& bar, renderer_interface& renderer, string data) {
void dispatch::parse(const bar_settings& bar, renderer_interface& renderer, const string&& data) {
tags::parser p;
p.set(std::move(data));
context ctxt(bar);
m_action_ctxt.reset();
m_ctxt = make_unique<context>(bar);
while (p.has_next_element()) {
tags::element el;
@ -49,42 +51,42 @@ namespace tags {
case tags::tag_type::FORMAT:
switch (el.tag_data.subtype.format) {
case tags::syntaxtag::A:
handle_action(el.tag_data.action.btn, el.tag_data.action.closing, std::move(el.data));
handle_action(renderer, el.tag_data.action.btn, el.tag_data.action.closing, std::move(el.data));
break;
case tags::syntaxtag::B:
ctxt.apply_bg(el.tag_data.color);
m_ctxt->apply_bg(el.tag_data.color);
break;
case tags::syntaxtag::F:
ctxt.apply_fg(el.tag_data.color);
m_ctxt->apply_fg(el.tag_data.color);
break;
case tags::syntaxtag::T:
ctxt.apply_font(el.tag_data.font);
m_ctxt->apply_font(el.tag_data.font);
break;
case tags::syntaxtag::O:
renderer.render_offset(ctxt, el.tag_data.offset);
renderer.render_offset(*m_ctxt, el.tag_data.offset);
break;
case tags::syntaxtag::R:
ctxt.apply_reverse();
m_ctxt->apply_reverse();
break;
case tags::syntaxtag::o:
ctxt.apply_ol(el.tag_data.color);
m_ctxt->apply_ol(el.tag_data.color);
break;
case tags::syntaxtag::u:
ctxt.apply_ul(el.tag_data.color);
m_ctxt->apply_ul(el.tag_data.color);
break;
case tags::syntaxtag::P:
handle_control(ctxt, el.tag_data.ctrl);
handle_control(el.tag_data.ctrl);
break;
case tags::syntaxtag::l:
ctxt.apply_alignment(alignment::LEFT);
m_ctxt->apply_alignment(alignment::LEFT);
m_sig.emit(change_alignment{alignment::LEFT});
break;
case tags::syntaxtag::r:
ctxt.apply_alignment(alignment::RIGHT);
m_ctxt->apply_alignment(alignment::RIGHT);
m_sig.emit(change_alignment{alignment::RIGHT});
break;
case tags::syntaxtag::c:
ctxt.apply_alignment(alignment::CENTER);
m_ctxt->apply_alignment(alignment::CENTER);
m_sig.emit(change_alignment{alignment::CENTER});
break;
default:
@ -93,23 +95,24 @@ namespace tags {
}
break;
case tags::tag_type::ATTR:
ctxt.apply_attr(el.tag_data.subtype.activation, el.tag_data.attr);
m_ctxt->apply_attr(el.tag_data.subtype.activation, el.tag_data.attr);
break;
}
} else {
handle_text(renderer, ctxt, std::move(el.data));
handle_text(renderer, std::move(el.data));
}
}
if (!m_actions.empty()) {
throw runtime_error(to_string(m_actions.size()) + " unclosed action block(s)");
}
// TODO handle unclosed action tags in ctxt
/* if (!m_actions.empty()) { */
/* throw runtime_error(to_string(m_actions.size()) + " unclosed action block(s)"); */
/* } */
}
/**
* Process text contents
*/
void dispatch::handle_text(renderer_interface& renderer, context& ctxt, string&& data) {
void dispatch::handle_text(renderer_interface& renderer, string&& data) {
#ifdef DEBUG_WHITESPACE
string::size_type p;
while ((p = data.find(' ')) != string::npos) {
@ -117,43 +120,25 @@ namespace tags {
}
#endif
renderer.render_text(ctxt, std::move(data));
renderer.render_text(*m_ctxt, std::move(data));
}
void dispatch::handle_action(mousebtn btn, bool closing, const string&& cmd) {
void dispatch::handle_action(renderer_interface& renderer, mousebtn btn, bool closing, const string&& cmd) {
if (closing) {
if (btn == mousebtn::NONE) {
if (!m_actions.empty()) {
btn = m_actions.back();
m_actions.pop_back();
action_t id;
std::tie(id, btn) = m_action_ctxt.action_close(btn, m_ctxt->get_alignment());
renderer.action_close(*m_ctxt, id);
} else {
m_log.err("parser: Closing action tag without matching tag");
}
} else {
auto it = std::find(m_actions.crbegin(), m_actions.crend(), btn);
if (it == m_actions.rend()) {
m_log.err("parser: Closing action tag for button %d without matching opening tag", static_cast<int>(btn));
} else {
/*
* We can't erase with a reverse iterator, we have to get
* the forward iterator first
* https://stackoverflow.com/a/1830240/5363071
*/
m_actions.erase(std::next(it).base());
}
}
m_sig.emit(action_end{btn});
} else {
m_actions.push_back(btn);
m_sig.emit(action_begin{action{btn, std::move(cmd)}});
string tmp = cmd;
action_t id = m_action_ctxt.action_open(btn, std::move(cmd), m_ctxt->get_alignment());
renderer.action_open(*m_ctxt, btn, id);
}
}
void dispatch::handle_control(context& ctxt, controltag ctrl) {
void dispatch::handle_control(controltag ctrl) {
switch (ctrl) {
case controltag::R:
ctxt.reset();
m_ctxt->apply_reset();
break;
default:
throw runtime_error("Unrecognized polybar control tag: " + to_string(static_cast<int>(ctrl)));