Stop tracking action blocks in renderer
Dispatch can now directly get the current position from the renderer and create action blocks with the proper coordinates.
This commit is contained in:
parent
b5bdbdf6cb
commit
cd1d4fa183
@ -53,8 +53,9 @@ class renderer : public renderer_interface,
|
||||
|
||||
void change_alignment(const tags::context& ctxt) override;
|
||||
|
||||
void action_open(const tags::context& ctxt, tags::action_t id) override;
|
||||
void action_close(const tags::context& ctxt, tags::action_t id) override;
|
||||
double get_x(const tags::context& ctxt) const override;
|
||||
|
||||
double get_alignment_start(const alignment align) const override;
|
||||
|
||||
protected:
|
||||
void fill_background();
|
||||
|
@ -7,23 +7,36 @@ POLYBAR_NS
|
||||
|
||||
class renderer_interface {
|
||||
public:
|
||||
renderer_interface(tags::action_context& action_ctxt) : m_action_ctxt(action_ctxt){};
|
||||
renderer_interface(const tags::action_context& action_ctxt) : m_action_ctxt(action_ctxt){};
|
||||
|
||||
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 change_alignment(const tags::context& ctxt) = 0;
|
||||
|
||||
virtual void action_open(const tags::context& ctxt, tags::action_t id) = 0;
|
||||
virtual void action_close(const tags::context& ctxt, tags::action_t id) = 0;
|
||||
/**
|
||||
* Get the current x-coordinate of the renderer.
|
||||
*
|
||||
* This position denotes the coordinate where the next thing will be rendered.
|
||||
* It is relative to the start of the current alignment because the absolute
|
||||
* positions may not be known until after the renderer has finished.
|
||||
*/
|
||||
virtual double get_x(const tags::context& ctxt) const = 0;
|
||||
|
||||
/**
|
||||
* Get the absolute x-position of the start of an alignment block.
|
||||
*
|
||||
* The position is absolute in terms of the bar window.
|
||||
*
|
||||
* Only call this after all the rendering is finished as these values change
|
||||
* when new things are rendered.
|
||||
*/
|
||||
virtual double get_alignment_start(const alignment align) const = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Stores information about actions in the current action cycle.
|
||||
*
|
||||
* The renderer is only responsible for updating the start and end positions
|
||||
* of each action block.
|
||||
* Stores information about actions in the current render cycle.
|
||||
*/
|
||||
tags::action_context& m_action_ctxt;
|
||||
const tags::action_context& m_action_ctxt;
|
||||
};
|
||||
|
||||
POLYBAR_NS_END
|
||||
|
@ -98,23 +98,47 @@ namespace tags {
|
||||
|
||||
static constexpr action_t NO_ACTION = -1;
|
||||
|
||||
/**
|
||||
* Defines a clickable (or scrollable) action block.
|
||||
*
|
||||
* An action block is an area on the bar that executes some command when clicked.
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* Start position of the action block (inclusive), relative to the alignment.
|
||||
*/
|
||||
double start_x{0};
|
||||
|
||||
/**
|
||||
* End position of the action block (exclusive), relative to the alignment.
|
||||
*/
|
||||
double end_x{0};
|
||||
mousebtn button;
|
||||
alignment align;
|
||||
/**
|
||||
* Tracks whether this block is still open or whether it already has a
|
||||
* corresponding closing tag.
|
||||
*
|
||||
* After rendering, all action blocks should be closed.
|
||||
*/
|
||||
bool is_open;
|
||||
|
||||
unsigned int width() const {
|
||||
return static_cast<unsigned int>(end_x - start_x + 0.5);
|
||||
}
|
||||
|
||||
bool test(int point) const {
|
||||
return static_cast<int>(start_x) <= point && static_cast<int>(end_x) > point;
|
||||
/**
|
||||
* Tests whether a given point is inside this block.
|
||||
*
|
||||
* This additionally needs the position of the start of the alignment
|
||||
* because the given point is relative to the bar window.
|
||||
*/
|
||||
bool test(double align_start, int point) const {
|
||||
return static_cast<int>(start_x + align_start) <= point && static_cast<int>(end_x + align_start) > point;
|
||||
}
|
||||
};
|
||||
|
||||
@ -122,11 +146,10 @@ namespace tags {
|
||||
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);
|
||||
action_t action_open(mousebtn btn, const string&& cmd, alignment align, double x);
|
||||
std::pair<action_t, mousebtn> action_close(mousebtn btn, alignment align, double x);
|
||||
|
||||
void set_start(action_t id, double x);
|
||||
void set_end(action_t id, double x);
|
||||
void set_alignmnent_start(const alignment a, const double x);
|
||||
|
||||
std::map<mousebtn, tags::action_t> get_actions(int x) const;
|
||||
action_t has_action(mousebtn btn, int x) const;
|
||||
@ -137,15 +160,28 @@ namespace tags {
|
||||
size_t num_actions() const;
|
||||
|
||||
// TODO provide better interface for adjusting the start positions of actions
|
||||
std::vector<action_block>& get_blocks();
|
||||
const std::vector<action_block>& get_blocks() const;
|
||||
|
||||
protected:
|
||||
void set_start(action_t id, double x);
|
||||
void set_end(action_t id, double x);
|
||||
|
||||
/**
|
||||
* Stores all currently known action blocks.
|
||||
*
|
||||
* The action_t type is meant as an index into this vector.
|
||||
* The action_t type is an index into this vector.
|
||||
*/
|
||||
std::vector<action_block> m_action_blocks;
|
||||
|
||||
/**
|
||||
* Stores the x-coordinate for the start of all the alignment blocks.
|
||||
*
|
||||
* This is needed because the action block coordinates are relative to the
|
||||
* alignment blocks and thus need the alignment block coordinates for
|
||||
* intersection tests.
|
||||
*/
|
||||
std::map<alignment, double> m_align_start{
|
||||
{alignment::NONE, 0}, {alignment::LEFT, 0}, {alignment::CENTER, 0}, {alignment::RIGHT, 0}};
|
||||
};
|
||||
|
||||
} // namespace tags
|
||||
|
@ -34,7 +34,6 @@ namespace tags {
|
||||
void handle_control(controltag ctrl);
|
||||
|
||||
private:
|
||||
vector<mousebtn> m_actions;
|
||||
const logger& m_log;
|
||||
|
||||
unique_ptr<context> m_ctxt;
|
||||
|
@ -251,19 +251,6 @@ void renderer::begin(xcb_rectangle_t rect) {
|
||||
void renderer::end() {
|
||||
m_log.trace_x("renderer: end");
|
||||
|
||||
/*
|
||||
* Finalize the positions of the action blocks.
|
||||
* Up until this point, the positions were relative to the start of the
|
||||
* alignment block the action was located in.
|
||||
* Here we add the start position of the block as well as the start position
|
||||
* of the bar itself (without borders or tray) to create positions relative to
|
||||
* the bar window.
|
||||
*/
|
||||
for (auto& a : m_action_ctxt.get_blocks()) {
|
||||
a.start_x += block_x(a.align) + m_rect.x;
|
||||
a.end_x += block_x(a.align) + m_rect.x;
|
||||
}
|
||||
|
||||
if (m_align != alignment::NONE) {
|
||||
m_log.trace_x("renderer: pop(%i)", static_cast<int>(m_align));
|
||||
m_context->pop(&m_blocks[m_align].pattern);
|
||||
@ -706,12 +693,12 @@ void renderer::change_alignment(const tags::context& ctxt) {
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::action_open(const tags::context&, tags::action_t id) {
|
||||
m_action_ctxt.set_start(id, m_blocks.at(m_align).x);
|
||||
double renderer::get_x(const tags::context& ctxt) const {
|
||||
return m_blocks.at(ctxt.get_alignment()).x;
|
||||
}
|
||||
|
||||
void renderer::action_close(const tags::context&, tags::action_t id) {
|
||||
m_action_ctxt.set_end(id, m_blocks.at(m_align).x);
|
||||
double renderer::get_alignment_start(const alignment align) const {
|
||||
return block_x(align) + m_rect.x;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,19 +118,22 @@ namespace tags {
|
||||
m_action_blocks.clear();
|
||||
}
|
||||
|
||||
action_t action_context::action_open(mousebtn btn, const string&& cmd, alignment align) {
|
||||
action_t action_context::action_open(mousebtn btn, const string&& cmd, alignment align, double x) {
|
||||
action_t id = m_action_blocks.size();
|
||||
m_action_blocks.emplace_back(std::move(cmd), btn, align, true);
|
||||
set_start(id, x);
|
||||
return id;
|
||||
}
|
||||
|
||||
std::pair<action_t, mousebtn> action_context::action_close(mousebtn btn, alignment align) {
|
||||
std::pair<action_t, mousebtn> action_context::action_close(mousebtn btn, alignment align, double x) {
|
||||
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};
|
||||
action_t id = std::distance(m_action_blocks.begin(), it.base()) - 1;
|
||||
set_end(id, x);
|
||||
return {id, it->button};
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +148,10 @@ namespace tags {
|
||||
m_action_blocks[id].end_x = x;
|
||||
}
|
||||
|
||||
void action_context::set_alignmnent_start(const alignment a, const double x) {
|
||||
m_align_start[a] = x;
|
||||
}
|
||||
|
||||
std::map<mousebtn, tags::action_t> action_context::get_actions(int x) const {
|
||||
std::map<mousebtn, tags::action_t> buttons;
|
||||
|
||||
@ -157,7 +164,7 @@ namespace tags {
|
||||
mousebtn btn = action.button;
|
||||
|
||||
// Higher IDs are higher in the action stack.
|
||||
if (id > buttons[btn] && action.test(x)) {
|
||||
if (id > buttons[btn] && action.test(m_align_start.at(action.align), x)) {
|
||||
buttons[action.button] = id;
|
||||
}
|
||||
}
|
||||
@ -191,7 +198,7 @@ namespace tags {
|
||||
return m_action_blocks.size();
|
||||
}
|
||||
|
||||
std::vector<action_block>& action_context::get_blocks() {
|
||||
const std::vector<action_block>& action_context::get_blocks() const {
|
||||
return m_action_blocks;
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,10 @@ namespace tags {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto a : {alignment::LEFT, alignment::CENTER, alignment::RIGHT}) {
|
||||
m_action_ctxt.set_alignmnent_start(a, renderer.get_alignment_start(a));
|
||||
}
|
||||
|
||||
// TODO handle unclosed action tags in ctxt
|
||||
/* if (!m_actions.empty()) { */
|
||||
/* throw runtime_error(to_string(m_actions.size()) + " unclosed action block(s)"); */
|
||||
@ -122,12 +126,9 @@ namespace tags {
|
||||
|
||||
void dispatch::handle_action(renderer_interface& renderer, mousebtn btn, bool closing, const string&& cmd) {
|
||||
if (closing) {
|
||||
action_t id;
|
||||
std::tie(id, btn) = m_action_ctxt.action_close(btn, m_ctxt->get_alignment());
|
||||
renderer.action_close(*m_ctxt, id);
|
||||
m_action_ctxt.action_close(btn, m_ctxt->get_alignment(), renderer.get_x(*m_ctxt));
|
||||
} else {
|
||||
action_t id = m_action_ctxt.action_open(btn, std::move(cmd), m_ctxt->get_alignment());
|
||||
renderer.action_open(*m_ctxt, id);
|
||||
m_action_ctxt.action_open(btn, std::move(cmd), m_ctxt->get_alignment(), renderer.get_x(*m_ctxt));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "tags/context.hpp"
|
||||
|
||||
#include "common/test.hpp"
|
||||
#include "components/logger.hpp"
|
||||
#include "tags/context.hpp"
|
||||
|
||||
using namespace polybar;
|
||||
using namespace std;
|
||||
@ -10,20 +9,20 @@ using namespace tags;
|
||||
TEST(ActionCtxtTest, dblClick) {
|
||||
action_context ctxt;
|
||||
|
||||
ctxt.action_open(mousebtn::DOUBLE_LEFT, "", alignment::LEFT);
|
||||
ctxt.action_close(mousebtn::DOUBLE_LEFT, alignment::LEFT);
|
||||
ctxt.action_open(mousebtn::DOUBLE_LEFT, "", alignment::LEFT, 0);
|
||||
ctxt.action_close(mousebtn::DOUBLE_LEFT, alignment::LEFT, 1);
|
||||
|
||||
ASSERT_TRUE(ctxt.has_double_click());
|
||||
|
||||
ctxt.reset();
|
||||
|
||||
ctxt.action_open(mousebtn::DOUBLE_MIDDLE, "", alignment::LEFT);
|
||||
ctxt.action_close(mousebtn::DOUBLE_MIDDLE, alignment::LEFT);
|
||||
ctxt.action_open(mousebtn::DOUBLE_MIDDLE, "", alignment::LEFT, 0);
|
||||
ctxt.action_close(mousebtn::DOUBLE_MIDDLE, alignment::LEFT, 1);
|
||||
|
||||
ASSERT_TRUE(ctxt.has_double_click());
|
||||
|
||||
ctxt.action_open(mousebtn::DOUBLE_RIGHT, "", alignment::LEFT);
|
||||
ctxt.action_close(mousebtn::DOUBLE_RIGHT, alignment::LEFT);
|
||||
ctxt.action_open(mousebtn::DOUBLE_RIGHT, "", alignment::LEFT, 0);
|
||||
ctxt.action_close(mousebtn::DOUBLE_RIGHT, alignment::LEFT, 1);
|
||||
|
||||
ASSERT_TRUE(ctxt.has_double_click());
|
||||
}
|
||||
@ -31,20 +30,20 @@ TEST(ActionCtxtTest, dblClick) {
|
||||
TEST(ActionCtxtTest, closing) {
|
||||
action_context ctxt;
|
||||
|
||||
auto id1 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT);
|
||||
auto id2 = ctxt.action_open(mousebtn::RIGHT, "", alignment::CENTER);
|
||||
auto id3 = ctxt.action_open(mousebtn::RIGHT, "", alignment::LEFT);
|
||||
auto id4 = ctxt.action_open(mousebtn::MIDDLE, "", alignment::LEFT);
|
||||
auto id1 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT, 0);
|
||||
auto id2 = ctxt.action_open(mousebtn::RIGHT, "", alignment::CENTER, 0);
|
||||
auto id3 = ctxt.action_open(mousebtn::RIGHT, "", alignment::LEFT, 0);
|
||||
auto id4 = ctxt.action_open(mousebtn::MIDDLE, "", alignment::LEFT, 0);
|
||||
|
||||
EXPECT_NE(NO_ACTION, id1);
|
||||
EXPECT_NE(NO_ACTION, id2);
|
||||
EXPECT_NE(NO_ACTION, id3);
|
||||
EXPECT_NE(NO_ACTION, id4);
|
||||
|
||||
EXPECT_EQ(make_pair(id1, mousebtn::LEFT), ctxt.action_close(mousebtn::LEFT, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id4, mousebtn::MIDDLE), ctxt.action_close(mousebtn::NONE, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id3, mousebtn::RIGHT), ctxt.action_close(mousebtn::NONE, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id2, mousebtn::RIGHT), ctxt.action_close(mousebtn::NONE, alignment::CENTER));
|
||||
EXPECT_EQ(make_pair(id1, mousebtn::LEFT), ctxt.action_close(mousebtn::LEFT, alignment::LEFT, 1));
|
||||
EXPECT_EQ(make_pair(id4, mousebtn::MIDDLE), ctxt.action_close(mousebtn::NONE, alignment::LEFT, 1));
|
||||
EXPECT_EQ(make_pair(id3, mousebtn::RIGHT), ctxt.action_close(mousebtn::NONE, alignment::LEFT, 1));
|
||||
EXPECT_EQ(make_pair(id2, mousebtn::RIGHT), ctxt.action_close(mousebtn::NONE, alignment::CENTER, 1));
|
||||
|
||||
EXPECT_EQ(4, ctxt.num_actions());
|
||||
}
|
||||
@ -63,19 +62,12 @@ TEST(ActionCtxtTest, overlapping) {
|
||||
* clang-format on
|
||||
*/
|
||||
|
||||
auto id1 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT);
|
||||
auto id2 = ctxt.action_open(mousebtn::MIDDLE, "", alignment::LEFT);
|
||||
auto id3 = ctxt.action_open(mousebtn::RIGHT, "", alignment::LEFT);
|
||||
EXPECT_EQ(make_pair(id1, mousebtn::LEFT), ctxt.action_close(mousebtn::LEFT, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id3, mousebtn::RIGHT), ctxt.action_close(mousebtn::RIGHT, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id2, mousebtn::MIDDLE), ctxt.action_close(mousebtn::MIDDLE, alignment::LEFT));
|
||||
|
||||
ctxt.set_start(id1, 0);
|
||||
ctxt.set_end(id1, 3);
|
||||
ctxt.set_start(id2, 1);
|
||||
ctxt.set_end(id2, 6);
|
||||
ctxt.set_start(id3, 2);
|
||||
ctxt.set_end(id3, 5);
|
||||
auto id1 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT, 0);
|
||||
auto id2 = ctxt.action_open(mousebtn::MIDDLE, "", alignment::LEFT, 1);
|
||||
auto id3 = ctxt.action_open(mousebtn::RIGHT, "", alignment::LEFT, 2);
|
||||
EXPECT_EQ(make_pair(id1, mousebtn::LEFT), ctxt.action_close(mousebtn::LEFT, alignment::LEFT, 3));
|
||||
EXPECT_EQ(make_pair(id3, mousebtn::RIGHT), ctxt.action_close(mousebtn::RIGHT, alignment::LEFT, 6));
|
||||
EXPECT_EQ(make_pair(id2, mousebtn::MIDDLE), ctxt.action_close(mousebtn::MIDDLE, alignment::LEFT, 5));
|
||||
|
||||
auto actions = ctxt.get_actions(2);
|
||||
|
||||
@ -100,19 +92,12 @@ TEST(ActionCtxtTest, stacking) {
|
||||
* clang-format on
|
||||
*/
|
||||
|
||||
auto id1 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT);
|
||||
auto id2 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT);
|
||||
auto id3 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT);
|
||||
EXPECT_EQ(make_pair(id3, mousebtn::LEFT), ctxt.action_close(mousebtn::NONE, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id2, mousebtn::LEFT), ctxt.action_close(mousebtn::NONE, alignment::LEFT));
|
||||
EXPECT_EQ(make_pair(id1, mousebtn::LEFT), ctxt.action_close(mousebtn::NONE, alignment::LEFT));
|
||||
|
||||
ctxt.set_start(id1, 0);
|
||||
ctxt.set_end(id1, 8);
|
||||
ctxt.set_start(id2, 1);
|
||||
ctxt.set_end(id2, 7);
|
||||
ctxt.set_start(id3, 3);
|
||||
ctxt.set_end(id3, 6);
|
||||
auto id1 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT, 0);
|
||||
auto id2 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT, 1);
|
||||
auto id3 = ctxt.action_open(mousebtn::LEFT, "", alignment::LEFT, 3);
|
||||
EXPECT_EQ(make_pair(id3, mousebtn::LEFT), ctxt.action_close(mousebtn::NONE, alignment::LEFT, 6));
|
||||
EXPECT_EQ(make_pair(id2, mousebtn::LEFT), ctxt.action_close(mousebtn::NONE, alignment::LEFT, 7));
|
||||
EXPECT_EQ(make_pair(id1, mousebtn::LEFT), ctxt.action_close(mousebtn::NONE, alignment::LEFT, 8));
|
||||
|
||||
EXPECT_EQ(id1, ctxt.has_action(mousebtn::LEFT, 0));
|
||||
EXPECT_EQ(id2, ctxt.has_action(mousebtn::LEFT, 1));
|
||||
@ -131,7 +116,7 @@ TEST(ActionCtxtTest, cmd) {
|
||||
|
||||
string cmd = "foobar";
|
||||
|
||||
auto id = ctxt.action_open(mousebtn::DOUBLE_RIGHT, cmd.substr(), alignment::RIGHT);
|
||||
auto id = ctxt.action_open(mousebtn::DOUBLE_RIGHT, cmd.substr(), alignment::RIGHT, 0);
|
||||
|
||||
ASSERT_EQ(cmd, ctxt.get_action(id));
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ using namespace polybar;
|
||||
using namespace std;
|
||||
using namespace tags;
|
||||
|
||||
using ::testing::InSequence;
|
||||
using ::testing::_;
|
||||
using ::testing::InSequence;
|
||||
|
||||
class MockRenderer : public renderer_interface {
|
||||
public:
|
||||
@ -18,8 +18,8 @@ class MockRenderer : public renderer_interface {
|
||||
MOCK_METHOD(void, render_offset, (const context& ctxt, int pixels), (override));
|
||||
MOCK_METHOD(void, render_text, (const context& ctxt, const string&& str), (override));
|
||||
MOCK_METHOD(void, change_alignment, (const context& ctxt), (override));
|
||||
MOCK_METHOD(void, action_open, (const context& ctxt, action_t id), (override));
|
||||
MOCK_METHOD(void, action_close, (const context& ctxt, action_t id), (override));
|
||||
MOCK_METHOD(double, get_x, (const context& ctxt), (const, override));
|
||||
MOCK_METHOD(double, get_alignment_start, (const alignment align), (const, override));
|
||||
};
|
||||
|
||||
class TestableDispatch : public dispatch {};
|
||||
|
Loading…
Reference in New Issue
Block a user