diff --git a/config.cmake b/config.cmake index 710de7fd..7ad14ffc 100644 --- a/config.cmake +++ b/config.cmake @@ -168,11 +168,14 @@ label-urgent-padding = 2 [module/dwm] type = internal/dwm -format = > +format = -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] ; State diff --git a/include/modules/dwm.hpp b/include/modules/dwm.hpp index cf49284b..1b86de27 100644 --- a/include/modules/dwm.hpp +++ b/include/modules/dwm.hpp @@ -105,6 +105,18 @@ namespace modules { */ 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 * @@ -182,6 +194,24 @@ namespace modules { */ 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 * command to dwm after parsing the command. @@ -211,7 +241,22 @@ namespace modules { /** * 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 diff --git a/src/modules/dwm.cpp b/src/modules/dwm.cpp index e73458f9..2ee041a1 100644 --- a/src/modules/dwm.cpp +++ b/src/modules/dwm.cpp @@ -28,8 +28,7 @@ namespace modules { m_ipc = factory_util::unique(socket_path); // Load configuration - m_formatter->add( - DEFAULT_FORMAT, DEFAULT_FORMAT_TAGS, {TAG_LABEL_TAGS, TAG_LABEL_LAYOUT, TAG_LABEL_TITLE}); + m_formatter->add(DEFAULT_FORMAT, DEFAULT_FORMAT_TAGS, {TAG_LABEL_TAGS, TAG_LABEL_LAYOUT, TAG_LABEL_TITLE}); // Populate m_state_labels map with labels and their states 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_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); try { @@ -153,17 +154,27 @@ namespace modules { if (tag == TAG_LABEL_TITLE) { builder->node(m_title_label); } else if (tag == TAG_LABEL_LAYOUT) { - if (m_click) { + if (m_layout_click) { // Toggle between secondary and default layout 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))); // Set previous layout 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(); - } else { - builder->node(m_layout_label); } } else if (tag == TAG_LABEL_TAGS) { bool first = true; @@ -175,7 +186,7 @@ namespace modules { 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::RIGHT, build_cmd(EVENT_TAG_RCLICK, to_string(tag.bit_mask))); 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) || - 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 { @@ -289,6 +301,30 @@ namespace modules { 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() { for (auto& t : m_tags) { t.state = get_state(t.bit_mask);