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:
parent
0a474bb2f2
commit
96239597ed
@ -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{};
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -46,8 +46,6 @@ namespace signals {
|
||||
}
|
||||
namespace parser {
|
||||
struct change_alignment;
|
||||
struct action_begin;
|
||||
struct action_end;
|
||||
} // namespace parser
|
||||
} // namespace signals
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)) {
|
||||
return true;
|
||||
}
|
||||
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,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) {
|
||||
|
@ -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,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<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) {
|
||||
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<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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
} 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});
|
||||
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<int>(ctrl)));
|
||||
|
Loading…
Reference in New Issue
Block a user