diff --git a/assets/config.conf b/assets/config.conf index 90853a8d..aab45074 100644 --- a/assets/config.conf +++ b/assets/config.conf @@ -120,6 +120,7 @@ swipe_min_threshold=1 gesturebind=none,horizontal,4,tagscrub gesture_swipe_distance=500 gesture_commit_ratio=0.5 +gesture_axis_lock=16 # mouse # need relogin to make it apply diff --git a/docs/bindings/mouse-gestures.md b/docs/bindings/mouse-gestures.md index 1c44c1c9..4ce4c6f9 100644 --- a/docs/bindings/mouse-gestures.md +++ b/docs/bindings/mouse-gestures.md @@ -107,6 +107,8 @@ The scrub follows your `trackpad_natural_scrolling` setting. `gesture_commit_ratio` (0–1, default `0.5`) sets how far through that distance you must swipe to commit a `tagscrub`, e.g. `0.3` commits after 30%. A fast flick can commit a bit below this via momentum. +`gesture_axis_lock` (px, default `16`) is how far the swipe must travel before the scrub decides whether it's along its axis. Raise it if imprecise swipes get misread. + ```ini gesture_swipe_distance=500 gesture_commit_ratio=0.5 diff --git a/src/animation/tag_scrub.h b/src/animation/tag_scrub.h index d9d6c06c..d5039cca 100644 --- a/src/animation/tag_scrub.h +++ b/src/animation/tag_scrub.h @@ -50,6 +50,23 @@ static inline void tag_scrub_stage(Monitor *m, int dir) { m->scrub_rubberband = false; m->scrub_incoming_tag = (uint32_t)target; + /* Mango lays out tags lazily: a tag you moved a window off of + * keeps stale window geometry until it's viewed. This is very annoying + * Here i try to predict the arrangement of the tag just so the tags don't + * appear miss-aligned in the scrub. + * */ + { + uint32_t saved_curtag = m->pertag->curtag; + uint32_t saved_tagset = m->tagset[m->seltags]; + m->pertag->curtag = (uint32_t)target; + m->tagset[m->seltags] = (1u << (target - 1)); + pre_caculate_before_arrange(m, false, false, true); + m->pertag->ltidxs[target]->arrange(m); + m->pertag->curtag = saved_curtag; + m->tagset[m->seltags] = saved_tagset; + pre_caculate_before_arrange(m, false, false, true); + } + Client *c; uint32_t inbit = 1u << (target - 1); wl_list_for_each(c, &clients, link) { @@ -122,6 +139,23 @@ static inline void tag_scrub_apply(Monitor *m, double progress) { static inline bool tag_scrub_active(Monitor *m) { return m && m->scrub_active; } +static inline bool tag_scrub_engaged(Monitor *m) { + return m && m->scrub_active && m->scrub_axis_locked; +} + +static inline void tag_scrub_abort(Monitor *m) { + if (!m) + return; + m->scrub_active = false; + m->scrub_axis_locked = false; + m->scrub_dir = 0; + m->scrub_progress = 0.0; + m->scrub_accum = 0.0; + m->scrub_velocity = 0.0; + m->scrub_incoming_tag = 0; + m->scrub_rubberband = false; +} + static inline bool tag_scrub_arm(Monitor *m, const GestureBinding *g) { if (!m || m->isoverview || m->pertag->curtag == 0) return false; @@ -136,6 +170,7 @@ static inline bool tag_scrub_arm(Monitor *m, const GestureBinding *g) { m->scrub_velocity = 0.0; m->scrub_last_delta = 0.0; m->scrub_last_time = 0; + m->scrub_axis_locked = false; return true; } @@ -143,6 +178,21 @@ static inline void tag_scrub_feed(Monitor *m, double dx, double dy, uint32_t time_msec) { if (!m->scrub_active) return; + + if (!m->scrub_axis_locked) { + double along = (m->scrub_axis == HORIZONTAL) ? swipe_dx : swipe_dy; + double perp = (m->scrub_axis == HORIZONTAL) ? swipe_dy : swipe_dx; + double a = along < 0 ? -along : along; + double p = perp < 0 ? -perp : perp; + if (a + p < (double)config.gesture_axis_lock) + return; + if (p > a) { + m->scrub_active = false; + return; + } + m->scrub_axis_locked = true; + } + double delta = (m->scrub_axis == HORIZONTAL) ? dx : dy; if (config.trackpad_natural_scrolling) delta = -delta; @@ -162,7 +212,13 @@ static inline void tag_scrub_feed(Monitor *m, double dx, double dy, if (!config.animations) { m->scrub_dir = (int8_t)dir; - m->scrub_progress = mag_p; + uint32_t mask = tag_scrub_occupied_mask(m); + int target = + tag_scrub_neighbor((int)m->pertag->curtag, dir, LENGTH(tags), mask, + m->scrub_have_client, config.tag_carousel); + m->scrub_rubberband = (dir != 0 && target == 0); + m->scrub_incoming_tag = 0; + tag_scrub_apply(m, mag_p); return; } @@ -178,69 +234,68 @@ static inline void tag_scrub_release(Monitor *m, bool cancelled) { if (!m->scrub_active) return; + double oriented_v = m->scrub_velocity * (double)m->scrub_dir; + if (!config.animations) { - double oriented_v = m->scrub_velocity * (double)m->scrub_dir; - if (!cancelled && m->scrub_dir != 0 && - gesture_scrub_should_commit(m->scrub_progress, oriented_v, - config.gesture_commit_ratio)) { + bool commit = !cancelled && m->scrub_dir != 0 && !m->scrub_rubberband && + gesture_scrub_should_commit(m->scrub_progress, oriented_v, + config.gesture_commit_ratio); + int target = 0; + if (commit) { uint32_t mask = tag_scrub_occupied_mask(m); - int target = tag_scrub_neighbor( + target = tag_scrub_neighbor( (int)m->pertag->curtag, m->scrub_dir, LENGTH(tags), mask, m->scrub_have_client, config.tag_carousel); - if (target != 0) - view_in_mon(&(Arg){.ui = 1u << (target - 1)}, false, m, true); } - m->scrub_active = false; - m->scrub_dir = 0; - m->scrub_progress = 0.0; - m->scrub_accum = 0.0; - m->scrub_velocity = 0.0; - return; - } - - double oriented_v = m->scrub_velocity * (double)m->scrub_dir; - bool commit = !cancelled && !m->scrub_rubberband && m->scrub_incoming_tag && - gesture_scrub_should_commit(m->scrub_progress, oriented_v, - config.gesture_commit_ratio); - - if (commit) { - Client *c; - uint32_t inbit = 1u << (m->scrub_incoming_tag - 1); - uint32_t curbit = 1u << (m->pertag->curtag - 1); - wl_list_for_each(c, &clients, link) { - if (c->mon == m && ISTILED(c) && - ((c->tags & inbit) || (c->tags & curbit))) - c->animation.running = true; - } - view_in_mon(&(Arg){.ui = inbit}, true, m, true); + if (target != 0) + view_in_mon(&(Arg){.ui = 1u << (target - 1)}, false, m, true); + else + arrange(m, true, true); } else { - uint32_t curbit = 1u << (m->pertag->curtag - 1); - uint32_t inbit = - m->scrub_incoming_tag ? (1u << (m->scrub_incoming_tag - 1)) : 0; - uint32_t now = get_now_in_ms(); - Client *c; - wl_list_for_each(c, &clients, link) { - if (c->mon != m || !ISTILED(c)) - continue; - bool is_incoming = - inbit && (c->tags & inbit) && !(c->isglobal || c->isunglobal); - if (!is_incoming && !(c->tags & curbit)) - continue; + bool commit = !cancelled && !m->scrub_rubberband && + m->scrub_incoming_tag && + gesture_scrub_should_commit(m->scrub_progress, oriented_v, + config.gesture_commit_ratio); + if (commit) { + uint32_t inbit = 1u << (m->scrub_incoming_tag - 1); + uint32_t curbit = 1u << (m->pertag->curtag - 1); + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->mon == m && ISTILED(c) && + ((c->tags & inbit) || (c->tags & curbit))) + c->animation.running = true; + } + view_in_mon(&(Arg){.ui = inbit}, true, m, true); + } else { + uint32_t curbit = 1u << (m->pertag->curtag - 1); + uint32_t inbit = + m->scrub_incoming_tag ? (1u << (m->scrub_incoming_tag - 1)) : 0; + uint32_t now = get_now_in_ms(); + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !ISTILED(c)) + continue; + bool is_incoming = inbit && (c->tags & inbit) && + !(c->isglobal || c->isunglobal); + if (!is_incoming && !(c->tags & curbit)) + continue; - c->animation.initial = c->animation.current; - c->current = is_incoming ? c->animainit_geom : c->geom; - c->animation.tagining = false; - c->animation.tagouting = is_incoming; - c->animation.action = TAG; - c->animation.duration = config.animation_duration_tag; - c->animation.time_started = now; - c->animation.running = true; - c->need_output_flush = true; + c->animation.initial = c->animation.current; + c->current = is_incoming ? c->animainit_geom : c->geom; + c->animation.tagining = false; + c->animation.tagouting = is_incoming; + c->animation.action = TAG; + c->animation.duration = config.animation_duration_tag; + c->animation.time_started = now; + c->animation.running = true; + c->need_output_flush = true; + } + request_fresh_all_monitors(); } - request_fresh_all_monitors(); } m->scrub_active = false; + m->scrub_axis_locked = false; m->scrub_dir = 0; m->scrub_progress = 0.0; m->scrub_accum = 0.0; diff --git a/src/animation/tag_scrub_math.h b/src/animation/tag_scrub_math.h index a25e1e13..208cb2dc 100644 --- a/src/animation/tag_scrub_math.h +++ b/src/animation/tag_scrub_math.h @@ -21,6 +21,8 @@ static inline int tag_scrub_neighbor(int curtag, int dir, int ntags, bool wrap) { if (curtag < 1 || curtag > ntags || (dir != 1 && dir != -1)) return 0; + bool only_current_occupied = (occupied_mask & ~(1u << (curtag - 1))) == 0; + bool skip_empty = have_client && !only_current_occupied; int t = curtag; for (int step = 0; step < ntags; step++) { t += dir; @@ -35,7 +37,7 @@ static inline int tag_scrub_neighbor(int curtag, int dir, int ntags, } if (t == curtag) return 0; - if (!have_client) + if (!skip_empty) return t; if (occupied_mask & (1u << (t - 1))) return t; diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 8023b298..b54aadab 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -238,6 +238,7 @@ typedef struct { uint32_t swipe_min_threshold; float gesture_commit_ratio; int32_t gesture_swipe_distance; + int32_t gesture_axis_lock; float focused_opacity; float unfocused_opacity; float *scroller_proportion_preset; @@ -1538,6 +1539,8 @@ bool parse_option(Config *config, char *key, char *value) { config->gesture_commit_ratio = atof(value); } else if (strcmp(key, "gesture_swipe_distance") == 0) { config->gesture_swipe_distance = atoi(value); + } else if (strcmp(key, "gesture_axis_lock") == 0) { + config->gesture_axis_lock = atoi(value); } else if (strcmp(key, "focused_opacity") == 0) { config->focused_opacity = atof(value); } else if (strcmp(key, "unfocused_opacity") == 0) { @@ -3569,6 +3572,7 @@ void override_config(void) { CLAMP_FLOAT(config.gesture_commit_ratio, 0.0f, 1.0f); config.gesture_swipe_distance = CLAMP_INT(config.gesture_swipe_distance, 50, 4000); + config.gesture_axis_lock = CLAMP_INT(config.gesture_axis_lock, 0, 1000); config.mouse_natural_scrolling = CLAMP_INT(config.mouse_natural_scrolling, 0, 1); config.mouse_accel_profile = CLAMP_INT(config.mouse_accel_profile, 0, 2); @@ -3726,6 +3730,7 @@ void set_value_default() { config.swipe_min_threshold = 1; config.gesture_commit_ratio = 0.5f; config.gesture_swipe_distance = 500; + config.gesture_axis_lock = 16; config.idleinhibit_ignore_visible = 0; diff --git a/src/mango.c b/src/mango.c index 3c861e2c..fa70d3ec 100644 --- a/src/mango.c +++ b/src/mango.c @@ -591,6 +591,7 @@ struct Monitor { double scrub_last_delta; uint32_t scrub_last_time; double scrub_velocity; + bool scrub_axis_locked; }; typedef struct { @@ -2212,9 +2213,10 @@ void swipe_update(struct wl_listener *listener, void *data) { void swipe_end(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_end_event *event = data; - if (tag_scrub_active(selmon)) { + if (tag_scrub_engaged(selmon)) { tag_scrub_release(selmon, event->cancelled); } else { + tag_scrub_abort(selmon); ongesture(event); } swipe_dx = 0;