diff --git a/cmd-display-menu.c b/cmd-display-menu.c index e1766fc2e..22d4bd503 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -126,6 +126,10 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item, format_add(ft, "popup_mouse_y", "%u", event->m.y); } + /* Position of the previous menu, for -x/-y L. */ + format_add(ft, "popup_last_x", "%u", tc->menu_last_px); + format_add(ft, "popup_last_y", "%u", tc->menu_last_py + h); + /* * If there are any status lines, add this window position and the * status line position. @@ -238,6 +242,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item, xp = "#{popup_pane_left}"; else if (strcmp(xp, "M") == 0) xp = "#{popup_mouse_centre_x}"; + else if (strcmp(xp, "L") == 0) + xp = "#{popup_last_x}"; else if (strcmp(xp, "W") == 0) xp = "#{popup_window_status_line_x}"; p = format_expand(ft, xp); @@ -258,6 +264,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item, yp = "#{popup_pane_bottom}"; else if (strcmp(yp, "M") == 0) yp = "#{popup_mouse_top}"; + else if (strcmp(yp, "L") == 0) + yp = "#{popup_last_y}"; else if (strcmp(yp, "S") == 0) yp = "#{popup_status_line_y}"; else if (strcmp(yp, "W") == 0) diff --git a/key-bindings.c b/key-bindings.c index 9b5197e4e..27cbb0e86 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -59,6 +59,8 @@ " '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \ " '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \ " ''" \ + " '#{?#{#{pane_floating_flag}},Move,}' '' {display-menu -xL -yL -T '#[align=centre]Move' " DEFAULT_MOVE_MENU " }" \ + " '#{?#{#{pane_floating_flag}},Move & Resize,}' '' {display-menu -xL -yL -T '#[align=centre]Move & Resize' " DEFAULT_MOVE_RESIZE_MENU " }" \ " '#{?#{#{pane_floating_flag}},Tile,}' 't' { join-pane }" \ " '#{?#{!:#{pane_floating_flag}},Float,}' 'f' { break-pane -W }" \ " '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \ @@ -72,6 +74,30 @@ " 'Respawn' 'R' {respawn-pane -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ " '#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" +#define DEFAULT_MOVE_MENU \ + " 'Centre' 'c' {move-pane -P centre}" \ + " ''" \ + " 'Top Left' '1' {move-pane -P top-left}" \ + " 'Top Right' '2' {move-pane -P top-right}" \ + " 'Bottom Left' '3' {move-pane -P bottom-left}" \ + " 'Bottom Right' '4' {move-pane -P bottom-right}" \ + " ''" \ + " 'Top' 't' {move-pane -P top-centre}" \ + " 'Bottom' 'b' {move-pane -P bottom-centre}" \ + " 'Left' 'l' {move-pane -P centre-left}" \ + " 'Right' 'r' {move-pane -P centre-right}" +#define DEFAULT_MOVE_RESIZE_MENU \ + " 'Fill' '0' {resize-pane -x100% -y100%; move-pane -P top-left}" \ + " ''" \ + " 'Top Left' '1' {resize-pane -x50% -y50%; move-pane -P top-left}" \ + " 'Top Right' '2' {resize-pane -x50% -y50%; move-pane -P top-right}" \ + " 'Bottom Left' '3' {resize-pane -x50% -y50%; move-pane -P bottom-left}" \ + " 'Bottom Right' '4' {resize-pane -x50% -y50%; move-pane -P bottom-right}" \ + " ''" \ + " 'Top' 't' {resize-pane -x100% -y50%; move-pane -P top-centre}" \ + " 'Bottom' 'b' {resize-pane -x100% -y50%; move-pane -P bottom-centre}" \ + " 'Left' 'l' {resize-pane -x50% -y100%; move-pane -P centre-left}" \ + " 'Right' 'r' {resize-pane -x50% -y100%; move-pane -P centre-right}" static int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); @@ -460,6 +486,9 @@ key_bindings_init(void) "bind -Tmove -N 'Move pane to bottom and resize' 'M-Down' { resizep -x100% -y50%; move-pane -P bottom-centre }", "bind -Tmove -N 'Move pane to left and resize' 'M-Left' { resizep -x50% -y100%; move-pane -P centre-left }", "bind -Tmove -N 'Move pane to right and resize' 'M-Right' { resizep -x50% -y100%; move-pane -P centre-right }", + "bind -Tmove -N 'Move pane to fill the window' 0 { resize-pane -x100% -y100%; move-pane -P top-left }", + "bind -Tmove -N 'Display move menu' , { if -F '#{pane_floating_flag}' { display-menu -xP -yP -T '#[align=centre]Move' " DEFAULT_MOVE_MENU " } }", + "bind -Tmove -N 'Display move and resize menu' . { if -F '#{pane_floating_flag}' { display-menu -xP -yP -T '#[align=centre]Move & Resize' " DEFAULT_MOVE_RESIZE_MENU " } }", /* Menu keys */ "bind -N 'Display window menu' < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }", diff --git a/menu.c b/menu.c index 756bb5693..257452fd7 100644 --- a/menu.c +++ b/menu.c @@ -313,6 +313,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) struct mouse_event *m = &event->m; u_int i; int count = menu->count, old = md->choice; + int move; const char *name = NULL; const struct menu_item *item; struct cmdq_state *state; @@ -320,6 +321,14 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) char *error; if (KEYC_IS_MOUSE(event->key)) { + /* + * A mouse move with no button held reports as a release, so + * treat it as highlight-only: it must never select or close the + * menu, otherwise a menu opened without a button already down + * (such as a submenu opened from another menu) would vanish as + * soon as the mouse moved over it. + */ + move = MOUSE_DRAG(m->b) && MOUSE_RELEASE(m->b); if (md->flags & MENU_NOMOUSE) { if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) return (1); @@ -330,7 +339,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) m->y < md->py + 1 || m->y > md->py + 1 + count - 1) { if (~md->flags & MENU_STAYOPEN) { - if (MOUSE_RELEASE(m->b)) + if (!move && MOUSE_RELEASE(m->b)) return (1); } else { if (!MOUSE_RELEASE(m->b) && @@ -345,7 +354,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) return (0); } if (~md->flags & MENU_STAYOPEN) { - if (MOUSE_RELEASE(m->b)) + if (!move && MOUSE_RELEASE(m->b)) goto chosen; } else { if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) @@ -558,6 +567,10 @@ menu_prepare(struct menu *menu, int flags, int starting_choice, if (py + menu->count + 2 > c->tty.sy) py = c->tty.sy - menu->count - 2; + /* Remember where this menu is so -x/-y L can reuse the position. */ + c->menu_last_px = px; + c->menu_last_py = py; + if (lines == BOX_LINES_DEFAULT) lines = options_get_number(o, "menu-border-lines"); diff --git a/tmux.1 b/tmux.1 index ef0034b4a..24edbc6c1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -7959,6 +7959,7 @@ Both may be a row or column number, or one of the following special values: .It Li "R" Ta Fl x Ta "The right side of the terminal" .It Li "P" Ta "Both" Ta "The bottom left of the pane" .It Li "M" Ta "Both" Ta "The mouse position" +.It Li "L" Ta "Both" Ta "The position of the last menu" .It Li "W" Ta "Both" Ta "The window position on the status line" .It Li "S" Ta Fl y Ta "The line above or below the status line" .El @@ -7969,6 +7970,8 @@ Or a format, which is expanded including the following additional variables: .It Li "popup_centre_x" Ta "Centered in the client" .It Li "popup_centre_y" Ta "Centered in the client" .It Li "popup_height" Ta "Height of menu or popup" +.It Li "popup_last_x" Ta "Left of the last menu" +.It Li "popup_last_y" Ta "Bottom of the last menu" .It Li "popup_mouse_bottom" Ta "Bottom of at the mouse" .It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse" .It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse" diff --git a/tmux.h b/tmux.h index 2fea9e74d..db46e6fb1 100644 --- a/tmux.h +++ b/tmux.h @@ -2309,6 +2309,9 @@ struct client { void *overlay_data; struct event overlay_timer; + u_int menu_last_px; + u_int menu_last_py; + struct client_files files; u_int source_file_depth;