xC + frontends: merge M-! into M-a + reversed M-A

Both WeeChat and irssi merge these two by prioritising highlights
when you press M-a.
This commit is contained in:
2026-08-09 13:30:22 +02:00
parent 6ba94ba664
commit 2d1df86a46
11 changed files with 180 additions and 129 deletions
+3
View File
@@ -1,5 +1,8 @@
Unreleased
* xC + all frontends: M-a now prioritizes highlights, newly added M-A looks
for buffers in reverse order, and the old M-! binding has been removed
* xF: reached near feature parity with xC and xP
* xP: fixed channel title layout for narrow browser windows
+31 -19
View File
@@ -1420,7 +1420,7 @@ func (l *customLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// rotatedBuffers returns buffer indexes starting with the current buffer.
// rotatedBuffers returns buffer indexes starting after the current buffer.
func rotatedBuffers() []int {
r, start := make([]int, len(buffers)), 0
for i := range buffers {
@@ -1458,6 +1458,29 @@ func actionBufferLast() {
}
}
func actionBufferActivity(backwards bool) {
r := rotatedBuffers()
if len(r) == 0 {
return
}
r = r[:len(r)-1]
if backwards {
slices.Reverse(r)
}
for _, i := range r {
if buffers[i].highlighted {
bufferActivate(buffers[i].bufferName)
return
}
}
for _, i := range r {
if buffers[i].newMessages != 0 {
bufferActivate(buffers[i].bufferName)
return
}
}
}
var shortcuts = map[desktop.CustomShortcut]func(){
{
KeyName: fyne.KeyPageUp,
@@ -1487,28 +1510,17 @@ var shortcuts = map[desktop.CustomShortcut]func(){
KeyName: fyne.KeyTab,
Modifier: fyne.KeyModifierAlt,
}: actionBufferLast,
{
// XXX: This makes an assumption on the keyboard layout (we want '!').
KeyName: fyne.Key1,
Modifier: fyne.KeyModifierAlt | fyne.KeyModifierShift,
}: func() {
for _, i := range rotatedBuffers() {
if buffers[i].highlighted {
bufferActivate(buffers[i].bufferName)
break
}
}
},
{
KeyName: fyne.KeyA,
Modifier: fyne.KeyModifierAlt,
}: func() {
for _, i := range rotatedBuffers() {
if buffers[i].newMessages != 0 {
bufferActivate(buffers[i].bufferName)
break
}
}
actionBufferActivity(false)
},
{
KeyName: fyne.KeyA,
Modifier: fyne.KeyModifierAlt | fyne.KeyModifierShift,
}: func() {
actionBufferActivity(true)
},
{
KeyName: fyne.KeyH,
+3 -3
View File
@@ -56,10 +56,10 @@ This is a list of all local specialties and their respective function names:
*M-0*, *M-1*, ..., *M-9*: *goto-buffer*::
Go to the N-th buffer (normally sets a repeat counter).
Since there is no buffer number zero, *M-0* goes to the tenth one.
*M-!*: *goto-highlight*::
Go to the first following buffer with an unseen highlight.
*M-a*: *goto-activity*::
Go to the first following buffer with unseen activity.
*M-A*: *goto-backtivity*::
Go to the first following/preceding buffer with an unseen highlight,
or, if there are none, with unseen activity.
*PageUp*: *display-backlog*::
Show the in-memory backlog for this buffer using *general.pager*,
which is almost certainly the *less*(1) program.
+35 -43
View File
@@ -5105,23 +5105,20 @@ buffer_at_index (struct app_context *ctx, int n)
}
static struct buffer *
buffer_next (struct app_context *ctx, int count)
buffer_after (struct app_context *ctx, struct buffer *iter, bool backwards)
{
struct buffer *new_buffer = ctx->current_buffer;
while (count-- > 0)
if (!(new_buffer = new_buffer->next))
new_buffer = ctx->buffers;
return new_buffer;
return backwards
? (iter->prev ? iter->prev : ctx->buffers_tail)
: (iter->next ? iter->next : ctx->buffers);
}
static struct buffer *
buffer_previous (struct app_context *ctx, int count)
buffer_step (struct app_context *ctx, int count, bool backwards)
{
struct buffer *new_buffer = ctx->current_buffer;
struct buffer *iter = ctx->current_buffer;
while (count-- > 0)
if (!(new_buffer = new_buffer->prev))
new_buffer = ctx->buffers_tail;
return new_buffer;
iter = buffer_after (ctx, iter, backwards);
return iter;
}
static bool
@@ -5177,7 +5174,7 @@ buffer_remove_safe (struct app_context *ctx, struct buffer *buffer)
if (buffer == ctx->current_buffer)
buffer_activate (ctx, ctx->last_buffer
? ctx->last_buffer
: buffer_next (ctx, 1));
: buffer_step (ctx, 1, false));
buffer_remove (ctx, buffer);
}
@@ -14283,7 +14280,7 @@ static bool
on_previous_buffer (int key, void *user_data)
{
(void) key;
buffer_activate (user_data, buffer_previous (user_data, 1));
buffer_activate (user_data, buffer_step (user_data, 1, true));
return true;
}
@@ -14291,7 +14288,7 @@ static bool
on_next_buffer (int key, void *user_data)
{
(void) key;
buffer_activate (user_data, buffer_next (user_data, 1));
buffer_activate (user_data, buffer_step (user_data, 1, false));
return true;
}
@@ -14308,41 +14305,36 @@ on_switch_buffer (int key, void *user_data)
}
static bool
on_goto_highlight (int key, void *user_data)
goto_activity (struct app_context *ctx, bool backwards)
{
(void) key;
struct app_context *ctx = user_data;
struct buffer *iter = ctx->current_buffer;;
do
{
if (!(iter = iter->next))
iter = ctx->buffers;
if (iter == ctx->current_buffer)
return false;
}
while (!iter->highlighted);
buffer_activate (ctx, iter);
return true;
struct buffer *iter = ctx->current_buffer, *stop = iter;
while ((iter = buffer_after (ctx, iter, backwards)) != stop)
if (iter->highlighted)
{
buffer_activate (ctx, iter);
return true;
}
while ((iter = buffer_after (ctx, iter, backwards)) != stop)
if (iter->new_messages_count != iter->new_unimportant_count)
{
buffer_activate (ctx, iter);
return true;
}
return false;
}
static bool
on_goto_activity (int key, void *user_data)
{
(void) key;
return goto_activity (user_data, false);
}
struct app_context *ctx = user_data;
struct buffer *iter = ctx->current_buffer;
do
{
if (!(iter = iter->next))
iter = ctx->buffers;
if (iter == ctx->current_buffer)
return false;
}
while (iter->new_messages_count == iter->new_unimportant_count);
buffer_activate (ctx, iter);
return true;
static bool
on_goto_backtivity (int key, void *user_data)
{
(void) key;
return goto_activity (user_data, true);
}
static bool
@@ -14610,8 +14602,8 @@ app_input_init (struct input *self)
XX ("next-buffer", "Next buffer", on_next_buffer)
XX ("goto-buffer", "Go to buffer", on_goto_buffer)
XX ("switch-buffer", "Switch buffer", on_switch_buffer)
XX ("goto-highlight", "Go to highlight", on_goto_highlight)
XX ("goto-activity", "Go to activity", on_goto_activity)
XX ("goto-backtivity", "Go to activity", on_goto_backtivity)
XX ("move-buffer-left", "Move buffer left", on_move_buffer_left)
XX ("move-buffer-right", "Move buffer right", on_move_buffer_right)
XX ("display-backlog", "Show backlog", on_display_backlog)
@@ -14638,8 +14630,8 @@ app_input_init (struct input *self)
input_bind (self, "M-Tab", "switch-buffer");
input_bind (self, "M-/", "switch-buffer");
input_bind (self, "M-!", "goto-highlight");
input_bind (self, "M-a", "goto-activity");
input_bind (self, "M-A", "goto-backtivity");
input_bind (self, "M-m", "insert-attribute");
input_bind (self, "M-h", "display-full-log");
input_bind (self, "M-H", "toggle-unimportant");
+22 -9
View File
@@ -258,8 +258,8 @@ enum action
ACTION_BUFFER_PREVIOUS,
ACTION_BUFFER_NEXT,
ACTION_BUFFER_LAST,
ACTION_BUFFER_HIGHLIGHT,
ACTION_BUFFER_ACTIVITY,
ACTION_BUFFER_BACKTIVITY,
ACTION_BUFFER_1,
ACTION_BUFFER_2,
ACTION_BUFFER_3,
@@ -3376,16 +3376,29 @@ app_switch_buffer (int direction)
app_send_buffer_activate (next);
}
static struct buffer *
buffer_after (struct buffer *b, bool backwards)
{
return backwards
? (b->prev ? b->prev : g.buffers_tail)
: (b->next ? b->next : g.buffers);
}
static bool
app_switch_buffer_matching (bool highlighted)
app_goto_activity (bool backwards)
{
if (!g.buffers || !g.buffer_current)
return false;
struct buffer *b = g.buffer_current, *stop = b;
while ((b = b->next ? b->next : g.buffers) != stop)
if ((highlighted && b->highlighted)
|| (!highlighted && b->new_messages))
while ((b = buffer_after (b, backwards)) != stop)
if (b->highlighted)
{
app_send_buffer_activate (b);
return true;
}
while ((b = buffer_after (b, backwards)) != stop)
if (b->new_messages)
{
app_send_buffer_activate (b);
return true;
@@ -3832,10 +3845,10 @@ app_process_action (enum action action)
return false;
app_send_buffer_activate (g.buffer_last);
return true;
case ACTION_BUFFER_HIGHLIGHT:
return app_switch_buffer_matching (true);
case ACTION_BUFFER_ACTIVITY:
return app_switch_buffer_matching (false);
return app_goto_activity (false);
case ACTION_BUFFER_BACKTIVITY:
return app_goto_activity (true);
case ACTION_BUFFER_1:
case ACTION_BUFFER_2:
case ACTION_BUFFER_3:
@@ -4025,8 +4038,8 @@ g_default_bindings[] =
{ "F6", ACTION_BUFFER_NEXT }, // WeeChat
{ "M-Tab", ACTION_BUFFER_LAST }, // xC
{ "M-/", ACTION_BUFFER_LAST }, // WeeChat
{ "M-!", ACTION_BUFFER_HIGHLIGHT }, // xC
{ "M-a", ACTION_BUFFER_ACTIVITY }, // xC
{ "M-A", ACTION_BUFFER_BACKTIVITY }, // xC
{ "M-H", ACTION_TOGGLE_UNIMPORTANT }, // xC
{ "M-h", ACTION_LOG }, // xC
{ "M-e", ACTION_EDIT_INPUT }, // xC
+22 -12
View File
@@ -1210,7 +1210,7 @@ class WindowDelegate: NSObject, NSWindowDelegate {
refreshBufferList()
}
// Buffer indexes rotated to start after the current buffer.
// Buffers rotated to start after the current buffer.
func rotatedBuffers() -> Array<Buffer> {
guard let i = relayBuffers.firstIndex(
where: { $0.bufferName == relayBufferCurrent }) else {
@@ -1239,22 +1239,32 @@ class WindowDelegate: NSObject, NSWindowDelegate {
}
}
@objc func actionGotoHighlight() {
for b in rotatedBuffers() {
func gotoActivity(backwards: Bool) {
var candidates = rotatedBuffers()
candidates.popLast()
if backwards {
candidates.reverse()
}
for b in candidates {
if b.highlighted {
bufferActivate(name: b.bufferName)
return
}
}
for b in candidates {
if b.newMessages != 0 {
bufferActivate(name: b.bufferName)
return
}
}
}
@objc func actionGotoActivity() {
for b in rotatedBuffers() {
if b.newMessages != 0 {
bufferActivate(name: b.bufferName)
return
}
}
gotoActivity(backwards: false)
}
@objc func actionGotoBacktivity() {
gotoActivity(backwards: true)
}
@objc func actionToggleUnimportant() {
@@ -1314,12 +1324,12 @@ pushAccelerator("Switch buffer",
// TODO(p): Remove .command, and ignore these with the right Option key.
bufferMenu.addItem(NSMenuItem.separator())
pushAccelerator("Go to highlight",
#selector(WindowDelegate.actionGotoHighlight),
"!", [.command, .option])
pushAccelerator("Go to activity",
#selector(WindowDelegate.actionGotoActivity),
"a", [.command, .option])
pushAccelerator("Go to activity backwards",
#selector(WindowDelegate.actionGotoBacktivity),
"a", [.shift, .command, .option])
bufferMenu.addItem(NSMenuItem.separator())
pushAccelerator("Toggle unimportant",
+16 -12
View File
@@ -1234,10 +1234,22 @@ document.addEventListener('keydown', event => {
return
// Rotate names so that the current buffer comes first.
let names = [...buffers.keys()]
const names = [...buffers.keys()]
names.push.apply(names,
names.splice(0, names.findIndex(name => name == bufferCurrent)))
const gotoActivity = backwards => {
const candidates = names.slice(1)
if (backwards)
candidates.reverse()
for (const name of candidates)
if (buffers.get(name).highlighted)
return bufferActivate(name)
for (const name of candidates)
if (buffers.get(name).newMessages)
return bufferActivate(name)
}
switch (event.key) {
case 'h':
bufferToggleLog()
@@ -1247,18 +1259,10 @@ document.addEventListener('keydown', event => {
bufferToggleUnimportant(bufferCurrent)
break
case 'a':
for (const name of names.slice(1))
if (buffers.get(name).newMessages) {
bufferActivate(name)
break
}
gotoActivity(false)
break
case '!':
for (const name of names.slice(1))
if (buffers.get(name).highlighted) {
bufferActivate(name)
break
}
case 'A':
gotoActivity(true)
break
case 'Tab':
case '/':
+26 -16
View File
@@ -19,6 +19,7 @@
#include "xC-proto.cpp"
#include "config.h"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <map>
@@ -1622,6 +1623,22 @@ rotated_buffers()
return rotated;
}
static void
goto_activity_bidirectional(bool backwards)
{
auto candidates = rotated_buffers();
if (!candidates.empty())
candidates.pop_back();
if (backwards)
std::reverse(candidates.begin(), candidates.end());
for (auto i : candidates)
if (auto &b = g.buffers[i]; b.highlighted)
return buffer_activate(b.buffer_name);
for (auto i : candidates)
if (auto &b = g.buffers[i]; b.new_messages)
return buffer_activate(b.buffer_name);
}
static void
bind_shortcuts()
{
@@ -1641,24 +1658,16 @@ bind_shortcuts()
if (auto b = buffer_by_name(g.buffer_last))
buffer_activate(b->buffer_name);
};
auto goto_highlight = [] {
for (auto i : rotated_buffers())
if (g.buffers[i].highlighted) {
buffer_activate(g.buffers[i].buffer_name);
break;
}
};
auto goto_activity = [] {
for (auto i : rotated_buffers())
if (g.buffers[i].new_messages) {
buffer_activate(g.buffers[i].buffer_name);
break;
}
};
auto toggle_unimportant = [] {
if (auto b = buffer_by_name(g.buffer_current))
buffer_toggle_unimportant(b->buffer_name);
};
auto goto_activity = [] {
goto_activity_bidirectional(false);
};
auto goto_backtivity = [] {
goto_activity_bidirectional(true);
};
new QShortcut(QKeyCombination(Qt::ControlModifier, Qt::Key_Tab),
g.wMain, switch_buffer);
@@ -1682,8 +1691,9 @@ bind_shortcuts()
new QShortcut(QKeyCombination(Qt::AltModifier, Qt::Key_A),
g.wMain, goto_activity);
new QShortcut(QKeyCombination(Qt::AltModifier, Qt::Key_Exclam),
g.wMain, goto_highlight);
new QShortcut(
QKeyCombination(Qt::AltModifier | Qt::ShiftModifier, Qt::Key_A),
g.wMain, goto_backtivity);
new QShortcut(QKeyCombination(Qt::AltModifier, Qt::Key_H),
g.wMain, toggle_unimportant);
}
+2 -2
View File
@@ -7,8 +7,8 @@
#define ID_PREVIOUS_BUFFER 11
#define ID_NEXT_BUFFER 12
#define ID_SWITCH_BUFFER 13
#define ID_GOTO_HIGHLIGHT 14
#define ID_GOTO_ACTIVITY 15
#define ID_GOTO_ACTIVITY 14
#define ID_GOTO_BACKTIVITY 15
#define ID_TOGGLE_UNIMPORTANT 16
#define ID_DISPLAY_FULL_LOG 17
+19 -12
View File
@@ -1615,6 +1615,21 @@ process_bufferlist_notification(WORD code)
}
}
static void
goto_activity(std::vector<size_t> candidates, bool backwards)
{
if (!candidates.empty())
candidates.pop_back();
if (backwards)
std::reverse(candidates.begin(), candidates.end());
for (auto i : candidates)
if (auto &b = g.buffers[i]; b.highlighted)
return buffer_activate(b.buffer_name);
for (auto i : candidates)
if (auto &b = g.buffers[i]; b.new_messages)
return buffer_activate(b.buffer_name);
}
static void
process_accelerator(WORD id)
{
@@ -1645,19 +1660,11 @@ process_accelerator(WORD id)
if (!g.buffer_last.empty())
buffer_activate(g.buffer_last);
return;
case ID_GOTO_HIGHLIGHT:
for (auto i : rotated)
if (g.buffers[i].highlighted) {
buffer_activate(g.buffers[i].buffer_name);
break;
}
return;
case ID_GOTO_ACTIVITY:
for (auto i : rotated)
if (g.buffers[i].new_messages) {
buffer_activate(g.buffers[i].buffer_name);
break;
}
goto_activity(rotated, false);
return;
case ID_GOTO_BACKTIVITY:
goto_activity(rotated, true);
return;
case ID_TOGGLE_UNIMPORTANT:
if (b)
+1 -1
View File
@@ -25,8 +25,8 @@ BEGIN
VK_TAB, ID_SWITCH_BUFFER, CONTROL, VIRTKEY
// These are proper, but llvm-rc won't accept them (GitHub #64002).
#ifndef __clang__
"!", ID_GOTO_HIGHLIGHT, ALT
"a", ID_GOTO_ACTIVITY, ALT
"A", ID_GOTO_BACKTIVITY, ALT
"H", ID_TOGGLE_UNIMPORTANT, ALT
"h", ID_DISPLAY_FULL_LOG, ALT
#endif