diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 0c554678..8afbe6ea 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -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&&, - unique_ptr&&, unique_ptr&&, unique_ptr&&, bool only_initialize_values); + unique_ptr&&, unique_ptr&&, unique_ptr&&, + unique_ptr&&, bool only_initialize_values); ~bar(); const bar_settings settings() const; @@ -114,6 +116,7 @@ class bar : public xpp::event::sink m_tray; unique_ptr m_renderer; unique_ptr m_dispatch; + unique_ptr m_action_ctxt; unique_ptr m_taskqueue; bar_settings m_opts{}; diff --git a/include/components/renderer.hpp b/include/components/renderer.hpp index a6c8f0ec..9c6b099c 100644 --- a/include/components/renderer.hpp +++ b/include/components/renderer.hpp @@ -32,9 +32,9 @@ struct alignment_block { double y; }; -class renderer : public renderer_interface, - public signal_receiver { +class renderer + : public renderer_interface, + public signal_receiver { public: using make_type = unique_ptr; 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 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 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 m_actions; + std::unordered_map m_actions; bool m_fixedcenter; string m_snapshot_dst; diff --git a/include/components/renderer_interface.hpp b/include/components/renderer_interface.hpp index d94042f9..5f2cb107 100644 --- a/include/components/renderer_interface.hpp +++ b/include/components/renderer_interface.hpp @@ -1,4 +1,6 @@ #pragma once +#include + #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 get_actions(int x) = 0; + virtual tags::action_t get_action(mousebtn btn, int x) = 0; }; POLYBAR_NS_END diff --git a/include/components/types.hpp b/include/components/types.hpp index 42999a7f..dd8cae46 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -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(end_x - start_x + 0.5); diff --git a/include/events/signal.hpp b/include/events/signal.hpp index ac776937..e3df96c7 100644 --- a/include/events/signal.hpp +++ b/include/events/signal.hpp @@ -135,12 +135,6 @@ namespace signals { struct change_alignment : public detail::value_signal { using base_type::base_type; }; - struct action_begin : public detail::value_signal { - using base_type::base_type; - }; - struct action_end : public detail::value_signal { - using base_type::base_type; - }; } // namespace parser } // namespace signals diff --git a/include/events/signal_fwd.hpp b/include/events/signal_fwd.hpp index 44cb49db..f3700322 100644 --- a/include/events/signal_fwd.hpp +++ b/include/events/signal_fwd.hpp @@ -46,8 +46,6 @@ namespace signals { } namespace parser { struct change_alignment; - struct action_begin; - struct action_end; } // namespace parser } // namespace signals diff --git a/include/tags/context.hpp b/include/tags/context.hpp index 2f864f14..590a85e4 100644 --- a/include/tags/context.hpp +++ b/include/tags/context.hpp @@ -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_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 m_action_blocks; + }; + } // namespace tags POLYBAR_NS_END diff --git a/include/tags/dispatch.hpp b/include/tags/dispatch.hpp index d96818a8..2dc5f224 100644 --- a/include/tags/dispatch.hpp +++ b/include/tags/dispatch.hpp @@ -23,20 +23,23 @@ namespace tags { class dispatch { public: using make_type = unique_ptr; - 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 m_actions; const logger& m_log; + + unique_ptr m_ctxt; + action_context& m_action_ctxt; }; } // namespace tags diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 9224f6f2..88e3e6fd 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -39,6 +39,8 @@ using namespace signals::ui; * Create instance */ bar::make_type bar::make(bool only_initialize_values) { + auto action_ctxt = make_unique(); + // clang-format off return factory_util::unique( 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, unique_ptr&& tray_manager, unique_ptr&& dispatch, - unique_ptr&& taskqueue, bool only_initialize_values) + unique_ptr&& action_ctxt, unique_ptr&& 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(screen)) , m_tray(forward(tray_manager)) , m_dispatch(forward(dispatch)) + , m_action_ctxt(forward(action_ctxt)) , m_taskqueue(forward(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(action.button) >= static_cast(mousebtn::DOUBLE_LEFT)) { - return true; - } + if (m_action_ctxt->has_double_click()) { + return true; } + for (auto&& action : m_opts.actions) { if (static_cast(action.button) >= static_cast(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& 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,18 +741,12 @@ 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)) { - m_log.trace("Found matching input area"); - m_sig.emit(button_press{string{action->command}}); - return; - } + 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{m_action_ctxt->get_action(action)}); + return; } for (auto&& action : m_opts.actions) { diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp index bbc270d1..c1efcf20 100644 --- a/src/components/renderer.cpp +++ b/src/components/renderer.cpp @@ -1,5 +1,7 @@ #include "components/renderer.hpp" +#include + #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 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(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,26 +678,66 @@ 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 renderer::get_actions(int x) { + std::map buttons; + + for (int i = static_cast(mousebtn::NONE); i < static_cast(mousebtn::BTN_COUNT); i++) { + buttons[static_cast(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 hint_num{}; - for (auto&& action : m_actions) { - if (!action.active) { - int n = hint_num.find(action.align)->second++; - double x = action.start_x; - double y = m_rect.y; - double w = action.width(); - double h = m_rect.height; + 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; + double w = action.width(); + double h = m_rect.height; - m_context->save(); - *m_context << CAIRO_OPERATOR_DIFFERENCE << (n % 2 ? 0xFF00FF00 : 0xFFFF0000); - *m_context << cairo::rect{x, y, w, h}; - m_context->fill(); - m_context->restore(); - } + m_context->save(); + *m_context << CAIRO_OPERATOR_DIFFERENCE << (n % 2 ? 0xFF00FF00 : 0xFFFF0000); + *m_context << cairo::rect{x, y, w, h}; + 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(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(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 diff --git a/src/tags/context.cpp b/src/tags/context.cpp index cd633538..0ed899e9 100644 --- a/src/tags/context.cpp +++ b/src/tags/context.cpp @@ -1,8 +1,18 @@ #include "tags/context.hpp" +#include + 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_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 diff --git a/src/tags/dispatch.cpp b/src/tags/dispatch.cpp index 3b1b3ee3..38088841 100644 --- a/src/tags/dispatch.cpp +++ b/src/tags/dispatch.cpp @@ -17,23 +17,25 @@ namespace tags { /** * Create instance */ - dispatch::make_type dispatch::make() { - return factory_util::unique(signal_emitter::make(), logger::make()); + dispatch::make_type dispatch::make(action_context& action_ctxt) { + return factory_util::unique(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(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(); - } 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(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}); + 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_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(ctrl)));