dwm: Add layout scroll; rename settings

Rename settings and allow more flexible configuration by allowing the user to
enable click handlers for the tags and the layout label separately.

Add a scroll handler for the layout label so the user can scroll through the
available layouts. Enabling layout-scroll-wrap allows wrapping back to the
beginning/end of the array of available layouts.

next_layout and prev_layout take the address of a layout and returns the
next/previous layout in the m_layouts array if possible, wrapping if specified,
otherwise returns the same layout. This is used to for the layout scroll
handlers to cycle through layouts.
This commit is contained in:
Mihir Lad 2020-07-21 18:36:29 -04:00
parent 33a8226827
commit a22b2ffcc3
3 changed files with 98 additions and 14 deletions

View File

@ -168,11 +168,14 @@ label-urgent-padding = 2
[module/dwm] [module/dwm]
type = internal/dwm type = internal/dwm
format = <label-tags> <label-layout>> <label-title> format = <label-tags> <label-layout> <label-title>
enable-click = false enable-tags-click = false
enable-layout-click = false
enable-layout-scroll = false
layout-scroll-wrap = false
; If enable-click = true, clicking the layout symbol will switch to this layout ; If enable-layout-click = true, clicking the layout symbol will switch to this layout
secondary-layout-symbol = [M] secondary-layout-symbol = [M]
; State ; State

View File

@ -105,6 +105,18 @@ namespace modules {
*/ */
static constexpr const char* EVENT_LAYOUT_RCLICK{"setlayoutsafe"}; static constexpr const char* EVENT_LAYOUT_RCLICK{"setlayoutsafe"};
/**
* Event name is same as the IPC command name to set the layout to the
* previous layout in the array.
*/
static constexpr const char* EVENT_LAYOUT_SDOWN{"setlayoutsafe"};
/**
* Event name is same as the IPC command name to set the layout to the
* next layout in the array.
*/
static constexpr const char* EVENT_LAYOUT_SUP{"setlayoutsafe"};
/** /**
* Called by has_event on layout changes. This updates the layout label * Called by has_event on layout changes. This updates the layout label
* *
@ -182,6 +194,24 @@ namespace modules {
*/ */
auto find_layout(uintptr_t addr) const -> const dwmipc::Layout*; auto find_layout(uintptr_t addr) const -> const dwmipc::Layout*;
/**
* Get the address of the next layout in m_layouts.
*
* @param layout Address of the current layout
* @param wrap True to wrap around the array, false to return the same
* layout if the next layout does not exist.
*/
auto next_layout(const dwmipc::Layout& layout, bool wrap) const -> const dwmipc::Layout*;
/**
* Get the address of the previous layout in m_layouts.
*
* @param layout Address of the current layout
* @param wrap True to wrap around the array, false to return the same
* layout if the next layout does not exist.
*/
auto prev_layout(const dwmipc::Layout& layout, bool wrap) const -> const dwmipc::Layout*;
/** /**
* Check if the command matches the specified event name and if so, send a * Check if the command matches the specified event name and if so, send a
* command to dwm after parsing the command. * command to dwm after parsing the command.
@ -211,7 +241,22 @@ namespace modules {
/** /**
* If true, enables the click handlers for the tags * If true, enables the click handlers for the tags
*/ */
bool m_click{true}; bool m_tags_click{true};
/**
* If true, enables the click handlers for the layout label
*/
bool m_layout_click{true};
/**
* If true, scrolling the layout cycle through available layouts
*/
bool m_layout_scroll{true};
/**
* If true, scrolling the layout will wrap around to the beginning
*/
bool m_layout_wrap{true};
/** /**
* If the layout symbol is clicked on, it will set the layout represented by * If the layout symbol is clicked on, it will set the layout represented by

View File

@ -28,8 +28,7 @@ namespace modules {
m_ipc = factory_util::unique<dwmipc::Connection>(socket_path); m_ipc = factory_util::unique<dwmipc::Connection>(socket_path);
// Load configuration // Load configuration
m_formatter->add( m_formatter->add(DEFAULT_FORMAT, DEFAULT_FORMAT_TAGS, {TAG_LABEL_TAGS, TAG_LABEL_LAYOUT, TAG_LABEL_TITLE});
DEFAULT_FORMAT, DEFAULT_FORMAT_TAGS, {TAG_LABEL_TAGS, TAG_LABEL_LAYOUT, TAG_LABEL_TITLE});
// Populate m_state_labels map with labels and their states // Populate m_state_labels map with labels and their states
if (m_formatter->has(TAG_LABEL_TAGS)) { if (m_formatter->has(TAG_LABEL_TAGS)) {
@ -55,8 +54,10 @@ namespace modules {
m_title_label = load_optional_label(m_conf, name(), "label-title", "%title%"); m_title_label = load_optional_label(m_conf, name(), "label-title", "%title%");
} }
m_click = m_conf.get(name(), "enable-click", m_click); m_tags_click = m_conf.get(name(), "enable-tags-click", m_tags_click);
m_layout_click = m_conf.get(name(), "enable-layout-click", m_layout_click);
m_layout_scroll = m_conf.get(name(), "enable-layout-scroll", m_layout_scroll);
m_layout_wrap = m_conf.get(name(), "layout-scroll-wrap", m_layout_wrap);
m_secondary_layout_symbol = m_conf.get(name(), "secondary-layout-symbol", m_secondary_layout_symbol); m_secondary_layout_symbol = m_conf.get(name(), "secondary-layout-symbol", m_secondary_layout_symbol);
try { try {
@ -153,17 +154,27 @@ namespace modules {
if (tag == TAG_LABEL_TITLE) { if (tag == TAG_LABEL_TITLE) {
builder->node(m_title_label); builder->node(m_title_label);
} else if (tag == TAG_LABEL_LAYOUT) { } else if (tag == TAG_LABEL_LAYOUT) {
if (m_click) { if (m_layout_click) {
// Toggle between secondary and default layout // Toggle between secondary and default layout
auto addr = (m_current_layout == m_default_layout ? m_secondary_layout : m_default_layout)->address; auto addr = (m_current_layout == m_default_layout ? m_secondary_layout : m_default_layout)->address;
builder->cmd(mousebtn::LEFT, build_cmd(EVENT_LAYOUT_LCLICK, to_string(addr))); builder->cmd(mousebtn::LEFT, build_cmd(EVENT_LAYOUT_LCLICK, to_string(addr)));
// Set previous layout // Set previous layout
builder->cmd(mousebtn::RIGHT, build_cmd(EVENT_LAYOUT_RCLICK, "0")); builder->cmd(mousebtn::RIGHT, build_cmd(EVENT_LAYOUT_RCLICK, "0"));
builder->node(m_layout_label); }
if (m_layout_scroll) {
auto addr_next = next_layout(*m_current_layout, m_layout_wrap)->address;
auto addr_prev = prev_layout(*m_current_layout, m_layout_wrap)->address;
builder->cmd(mousebtn::SCROLL_DOWN, build_cmd(EVENT_LAYOUT_SDOWN, to_string(addr_prev)));
builder->cmd(mousebtn::SCROLL_UP, build_cmd(EVENT_LAYOUT_SUP, to_string(addr_next)));
}
builder->node(m_layout_label);
if (m_layout_click) {
builder->cmd_close();
builder->cmd_close();
}
if (m_layout_scroll) {
builder->cmd_close(); builder->cmd_close();
builder->cmd_close(); builder->cmd_close();
} else {
builder->node(m_layout_label);
} }
} else if (tag == TAG_LABEL_TAGS) { } else if (tag == TAG_LABEL_TAGS) {
bool first = true; bool first = true;
@ -175,7 +186,7 @@ namespace modules {
builder->node(m_seperator_label); builder->node(m_seperator_label);
} }
if (m_click) { if (m_tags_click) {
builder->cmd(mousebtn::LEFT, build_cmd(EVENT_TAG_LCLICK, to_string(tag.bit_mask))); builder->cmd(mousebtn::LEFT, build_cmd(EVENT_TAG_LCLICK, to_string(tag.bit_mask)));
builder->cmd(mousebtn::RIGHT, build_cmd(EVENT_TAG_RCLICK, to_string(tag.bit_mask))); builder->cmd(mousebtn::RIGHT, build_cmd(EVENT_TAG_RCLICK, to_string(tag.bit_mask)));
builder->node(tag.label); builder->node(tag.label);
@ -220,7 +231,8 @@ namespace modules {
} }
return check_send_cmd(cmd, EVENT_TAG_LCLICK) || check_send_cmd(cmd, EVENT_TAG_RCLICK) || return check_send_cmd(cmd, EVENT_TAG_LCLICK) || check_send_cmd(cmd, EVENT_TAG_RCLICK) ||
check_send_cmd(cmd, EVENT_LAYOUT_LCLICK) || check_send_cmd(cmd, EVENT_LAYOUT_RCLICK); check_send_cmd(cmd, EVENT_LAYOUT_LCLICK) || check_send_cmd(cmd, EVENT_LAYOUT_RCLICK) ||
check_send_cmd(cmd, EVENT_LAYOUT_SDOWN) || check_send_cmd(cmd, EVENT_LAYOUT_SUP);
} }
auto dwm_module::get_state(tag_mask_t bit_mask) const -> state_t { auto dwm_module::get_state(tag_mask_t bit_mask) const -> state_t {
@ -289,6 +301,30 @@ namespace modules {
return nullptr; return nullptr;
} }
auto dwm_module::next_layout(const dwmipc::Layout& layout, bool wrap) const -> const dwmipc::Layout* {
const auto* next = &layout + 1;
const auto* first = m_layouts->begin().base();
if (next < m_layouts->end().base()) {
return next;
} else if (wrap) {
return first;
} else {
return &layout;
}
}
auto dwm_module::prev_layout(const dwmipc::Layout& layout, bool wrap) const -> const dwmipc::Layout* {
const auto* prev = &layout - 1;
const auto* last = m_layouts->end().base() - 1;
if (prev >= m_layouts->begin().base()) {
return prev;
} else if (wrap) {
return last;
} else {
return &layout;
}
}
void dwm_module::update_tag_labels() { void dwm_module::update_tag_labels() {
for (auto& t : m_tags) { for (auto& t : m_tags) {
t.state = get_state(t.bit_mask); t.state = get_state(t.bit_mask);