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_fwd.hpp"
#include "events/signal_receiver.hpp" #include "events/signal_receiver.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "tags/context.hpp"
#include "utils/math.hpp" #include "utils/math.hpp"
#include "x11/types.hpp" #include "x11/types.hpp"
#include "x11/window.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); static make_type make(bool only_initialize_values = false);
explicit bar(connection&, signal_emitter&, const config&, const logger&, unique_ptr<screen>&&, 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(); ~bar();
const bar_settings settings() const; 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<tray_manager> m_tray;
unique_ptr<renderer> m_renderer; unique_ptr<renderer> m_renderer;
unique_ptr<tags::dispatch> m_dispatch; unique_ptr<tags::dispatch> m_dispatch;
unique_ptr<tags::action_context> m_action_ctxt;
unique_ptr<taskqueue> m_taskqueue; unique_ptr<taskqueue> m_taskqueue;
bar_settings m_opts{}; bar_settings m_opts{};

View File

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

View File

@ -1,4 +1,6 @@
#pragma once #pragma once
#include <map>
#include "common.hpp" #include "common.hpp"
#include "tags/context.hpp" #include "tags/context.hpp"
POLYBAR_NS POLYBAR_NS
@ -7,6 +9,12 @@ class renderer_interface {
public: public:
virtual void render_offset(const tags::context& ctxt, int pixels) = 0; 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 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 POLYBAR_NS_END

View File

@ -109,11 +109,11 @@ struct action {
string command{}; string command{};
}; };
struct action_block : public action { struct action_block {
mousebtn button{mousebtn::NONE};
alignment align{alignment::NONE}; alignment align{alignment::NONE};
double start_x{0.0}; double start_x{0.0};
double end_x{0.0}; double end_x{0.0};
bool active{true};
unsigned int width() const { unsigned int width() const {
return static_cast<unsigned int>(end_x - start_x + 0.5); 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> { struct change_alignment : public detail::value_signal<change_alignment, alignment> {
using base_type::base_type; 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 parser
} // namespace signals } // namespace signals

View File

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

View File

@ -44,8 +44,6 @@ namespace tags {
alignment get_alignment() const; alignment get_alignment() const;
protected: protected:
rgba get_color(color_value c, rgba fallback) const;
/** /**
* Background color * Background color
*/ */
@ -82,6 +80,53 @@ namespace tags {
private: private:
const bar_settings& m_settings; 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 } // namespace tags
POLYBAR_NS_END POLYBAR_NS_END

View File

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

View File

@ -39,6 +39,8 @@ using namespace signals::ui;
* Create instance * Create instance
*/ */
bar::make_type bar::make(bool only_initialize_values) { bar::make_type bar::make(bool only_initialize_values) {
auto action_ctxt = make_unique<tags::action_context>();
// clang-format off // clang-format off
return factory_util::unique<bar>( return factory_util::unique<bar>(
connection::make(), connection::make(),
@ -47,7 +49,8 @@ bar::make_type bar::make(bool only_initialize_values) {
logger::make(), logger::make(),
screen::make(), screen::make(),
tray_manager::make(), tray_manager::make(),
tags::dispatch::make(), tags::dispatch::make(*action_ctxt),
std::move(action_ctxt),
taskqueue::make(), taskqueue::make(),
only_initialize_values); only_initialize_values);
// clang-format on // 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, 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<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_connection(conn)
, m_sig(emitter) , m_sig(emitter)
, m_conf(config) , 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_screen(forward<decltype(screen)>(screen))
, m_tray(forward<decltype(tray_manager)>(tray_manager)) , m_tray(forward<decltype(tray_manager)>(tray_manager))
, m_dispatch(forward<decltype(dispatch)>(dispatch)) , m_dispatch(forward<decltype(dispatch)>(dispatch))
, m_action_ctxt(forward<decltype(action_ctxt)>(action_ctxt))
, m_taskqueue(forward<decltype(taskqueue)>(taskqueue)) { , m_taskqueue(forward<decltype(taskqueue)>(taskqueue)) {
string bs{m_conf.section()}; string bs{m_conf.section()};
@ -361,7 +365,7 @@ void bar::parse(string&& data, bool force) {
m_renderer->begin(rect); m_renderer->begin(rect);
try { try {
m_dispatch->parse(settings(), *m_renderer, data); m_dispatch->parse(settings(), *m_renderer, std::move(data));
} catch (const exception& err) { } catch (const exception& err) {
m_log.err("Failed to parse contents (reason: %s)", err.what()); 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(); m_renderer->end();
const auto check_dblclicks = [&]() -> bool { const auto check_dblclicks = [&]() -> bool {
for (auto&& action : m_renderer->actions()) { if (m_action_ctxt->has_double_click()) {
if (static_cast<int>(action.button) >= static_cast<int>(mousebtn::DOUBLE_LEFT)) { return true;
return true;
}
} }
for (auto&& action : m_opts.actions) { for (auto&& action : m_opts.actions) {
if (static_cast<int>(action.button) >= static_cast<int>(mousebtn::DOUBLE_LEFT)) { if (static_cast<int>(action.button) >= static_cast<int>(mousebtn::DOUBLE_LEFT)) {
return true; 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 // scroll cursor is less important than click cursor, so we shouldn't return until we are sure there is no click
// action // action
bool found_scroll = false; 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) { const auto find_click_area = [&](const action& action) {
if (!m_opts.cursor_click.empty() && if (!m_opts.cursor_click.empty() &&
!(action.button == mousebtn::SCROLL_UP || action.button == mousebtn::SCROLL_DOWN || !(action.button == mousebtn::SCROLL_UP || action.button == mousebtn::SCROLL_DOWN ||
@ -664,20 +696,6 @@ void bar::handle(const evt::motion_notify& evt) {
return false; 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) { for (auto&& action : m_opts.actions) {
if (!action.command.empty()) { if (!action.command.empty()) {
m_log.trace("Found matching fallback handler"); m_log.trace("Found matching fallback handler");
@ -723,18 +741,12 @@ void bar::handle(const evt::button_press& evt) {
m_buttonpress_pos = evt->event_x; m_buttonpress_pos = evt->event_x;
const auto deferred_fn = [&](size_t) { const auto deferred_fn = [&](size_t) {
/* tags::action_t action = m_renderer->get_action(m_buttonpress_btn, m_buttonpress_pos);
* 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 if (action != tags::NO_ACTION) {
* surrounding action block m_log.trace("Found matching input area");
*/ m_sig.emit(button_press{m_action_ctxt->get_action(action)});
auto actions = m_renderer->actions(); return;
for (auto action = actions.rbegin(); action != actions.rend(); action++) {
if (action->button == m_buttonpress_btn && !action->active && action->test(m_buttonpress_pos)) {
m_log.trace("Found matching input area");
m_sig.emit(button_press{string{action->command}});
return;
}
} }
for (auto&& action : m_opts.actions) { for (auto&& action : m_opts.actions) {

View File

@ -1,5 +1,7 @@
#include "components/renderer.hpp" #include "components/renderer.hpp"
#include <cassert>
#include "cairo/context.hpp" #include "cairo/context.hpp"
#include "components/config.hpp" #include "components/config.hpp"
#include "events/signal.hpp" #include "events/signal.hpp"
@ -193,13 +195,6 @@ xcb_window_t renderer::window() const {
return m_window; return m_window;
} }
/**
* Get completed action blocks
*/
const vector<action_block> renderer::actions() const {
return m_actions;
}
/** /**
* Begin render routine * Begin render routine
*/ */
@ -255,7 +250,8 @@ void renderer::begin(xcb_rectangle_t rect) {
void renderer::end() { void renderer::end() {
m_log.trace_x("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.start_x += block_x(a.align) + m_rect.x;
a.end_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; 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 * Fill background color
*/ */
@ -716,26 +678,66 @@ void renderer::render_offset(const tags::context&, int pixels) {
m_blocks[m_align].x += 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 * Colorize the bounding box of created action blocks
*/ */
void renderer::highlight_clickable_areas() { void renderer::highlight_clickable_areas() {
#ifdef DEBUG_HINTS #ifdef DEBUG_HINTS
map<alignment, int> hint_num{}; map<alignment, int> hint_num{};
for (auto&& action : m_actions) { for (auto&& entry : m_actions) {
if (!action.active) { auto&& action = entry.second;
int n = hint_num.find(action.align)->second++; int n = hint_num.find(action.align)->second++;
double x = action.start_x; double x = action.start_x;
double y = m_rect.y; double y = m_rect.y;
double w = action.width(); double w = action.width();
double h = m_rect.height; double h = m_rect.height;
m_context->save(); m_context->save();
*m_context << CAIRO_OPERATOR_DIFFERENCE << (n % 2 ? 0xFF00FF00 : 0xFFFF0000); *m_context << CAIRO_OPERATOR_DIFFERENCE << (n % 2 ? 0xFF00FF00 : 0xFFFF0000);
*m_context << cairo::rect{x, y, w, h}; *m_context << cairo::rect{x, y, w, h};
m_context->fill(); m_context->fill();
m_context->restore(); m_context->restore();
}
} }
m_surface->flush(); m_surface->flush();
#endif #endif
@ -767,34 +769,4 @@ bool renderer::on(const signals::parser::change_alignment& evt) {
return true; 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 POLYBAR_NS_END

View File

@ -1,8 +1,18 @@
#include "tags/context.hpp" #include "tags/context.hpp"
#include <cassert>
POLYBAR_NS POLYBAR_NS
namespace tags { 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) { context::context(const bar_settings& settings) : m_settings(settings) {
reset(); reset();
} }
@ -104,13 +114,50 @@ namespace tags {
return m_align; return m_align;
} }
rgba context::get_color(color_value c, rgba fallback) const { void action_context::reset() {
if (c.type == color_type::RESET) { m_action_blocks.clear();
return fallback;
} else {
return c.val;
}
} }
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 } // namespace tags
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -17,23 +17,25 @@ namespace tags {
/** /**
* Create instance * Create instance
*/ */
dispatch::make_type dispatch::make() { dispatch::make_type dispatch::make(action_context& action_ctxt) {
return factory_util::unique<dispatch>(signal_emitter::make(), logger::make()); return factory_util::unique<dispatch>(signal_emitter::make(), logger::make(), action_ctxt);
} }
/** /**
* Construct parser instance * 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 * 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; tags::parser p;
p.set(std::move(data)); p.set(std::move(data));
context ctxt(bar); m_action_ctxt.reset();
m_ctxt = make_unique<context>(bar);
while (p.has_next_element()) { while (p.has_next_element()) {
tags::element el; tags::element el;
@ -49,42 +51,42 @@ namespace tags {
case tags::tag_type::FORMAT: case tags::tag_type::FORMAT:
switch (el.tag_data.subtype.format) { switch (el.tag_data.subtype.format) {
case tags::syntaxtag::A: 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; break;
case tags::syntaxtag::B: case tags::syntaxtag::B:
ctxt.apply_bg(el.tag_data.color); m_ctxt->apply_bg(el.tag_data.color);
break; break;
case tags::syntaxtag::F: case tags::syntaxtag::F:
ctxt.apply_fg(el.tag_data.color); m_ctxt->apply_fg(el.tag_data.color);
break; break;
case tags::syntaxtag::T: case tags::syntaxtag::T:
ctxt.apply_font(el.tag_data.font); m_ctxt->apply_font(el.tag_data.font);
break; break;
case tags::syntaxtag::O: case tags::syntaxtag::O:
renderer.render_offset(ctxt, el.tag_data.offset); renderer.render_offset(*m_ctxt, el.tag_data.offset);
break; break;
case tags::syntaxtag::R: case tags::syntaxtag::R:
ctxt.apply_reverse(); m_ctxt->apply_reverse();
break; break;
case tags::syntaxtag::o: case tags::syntaxtag::o:
ctxt.apply_ol(el.tag_data.color); m_ctxt->apply_ol(el.tag_data.color);
break; break;
case tags::syntaxtag::u: case tags::syntaxtag::u:
ctxt.apply_ul(el.tag_data.color); m_ctxt->apply_ul(el.tag_data.color);
break; break;
case tags::syntaxtag::P: case tags::syntaxtag::P:
handle_control(ctxt, el.tag_data.ctrl); handle_control(el.tag_data.ctrl);
break; break;
case tags::syntaxtag::l: case tags::syntaxtag::l:
ctxt.apply_alignment(alignment::LEFT); m_ctxt->apply_alignment(alignment::LEFT);
m_sig.emit(change_alignment{alignment::LEFT}); m_sig.emit(change_alignment{alignment::LEFT});
break; break;
case tags::syntaxtag::r: case tags::syntaxtag::r:
ctxt.apply_alignment(alignment::RIGHT); m_ctxt->apply_alignment(alignment::RIGHT);
m_sig.emit(change_alignment{alignment::RIGHT}); m_sig.emit(change_alignment{alignment::RIGHT});
break; break;
case tags::syntaxtag::c: case tags::syntaxtag::c:
ctxt.apply_alignment(alignment::CENTER); m_ctxt->apply_alignment(alignment::CENTER);
m_sig.emit(change_alignment{alignment::CENTER}); m_sig.emit(change_alignment{alignment::CENTER});
break; break;
default: default:
@ -93,23 +95,24 @@ namespace tags {
} }
break; break;
case tags::tag_type::ATTR: 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; break;
} }
} else { } else {
handle_text(renderer, ctxt, std::move(el.data)); handle_text(renderer, std::move(el.data));
} }
} }
if (!m_actions.empty()) { // TODO handle unclosed action tags in ctxt
throw runtime_error(to_string(m_actions.size()) + " unclosed action block(s)"); /* if (!m_actions.empty()) { */
} /* throw runtime_error(to_string(m_actions.size()) + " unclosed action block(s)"); */
/* } */
} }
/** /**
* Process text contents * 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 #ifdef DEBUG_WHITESPACE
string::size_type p; string::size_type p;
while ((p = data.find(' ')) != string::npos) { while ((p = data.find(' ')) != string::npos) {
@ -117,43 +120,25 @@ namespace tags {
} }
#endif #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 (closing) {
if (btn == mousebtn::NONE) { action_t id;
if (!m_actions.empty()) { std::tie(id, btn) = m_action_ctxt.action_close(btn, m_ctxt->get_alignment());
btn = m_actions.back(); renderer.action_close(*m_ctxt, id);
m_actions.pop_back();
} 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 { } else {
m_actions.push_back(btn); string tmp = cmd;
m_sig.emit(action_begin{action{btn, std::move(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) { switch (ctrl) {
case controltag::R: case controltag::R:
ctxt.reset(); m_ctxt->apply_reset();
break; break;
default: default:
throw runtime_error("Unrecognized polybar control tag: " + to_string(static_cast<int>(ctrl))); throw runtime_error("Unrecognized polybar control tag: " + to_string(static_cast<int>(ctrl)));