Add support for attachable/detachable minibuffer

* exwm-workspace.el (exwm-workspace--update-minibuffer-height): Renamed
from `exwm-workspace--update-minibuffer' to better reflect its usage.

* exwm-input.el (exwm-input--update-focus): Directly put the floating
X window at top since the minibuffer is no longer a sibling.
* exwm-workspace.el (exwm-workspace--resize-minibuffer-frame): Calculate
position in absolute coordinate; put minibuffer at bottom by default;
resize the minibuffer frame with ConfigureWindow rather than
`set-frame-width'.
(exwm-workspace-switch): No need to reparent minibuffer any more.
(exwm-workspace--on-ConfigureNotify): Calculate position in absolute
coordinate.
(exwm-workspace--show-minibuffer, exwm-workspace--hide-minibuffer):
Show/Hide the minibuffer by adjusting the stacking order rather than
mapping/unmapping.
(exwm-workspace--init): Map the minibuffer frame container on creation.

* exwm-workspace.el (exwm-workspace--minibuffer-attached-p):
New function telling whether the minibuffer is attached.
(exwm-workspace--attached-minibuffer-height): New variable storing the
height of the attached minibuffer.
(exwm-workspace-attach-minibuffer, exwm-workspace-detach-minibuffer):
New functions for attaching/detaching the minibuffer.

* exwm-workspace.el (exwm-workspace--show-minibuffer): Remove the
workaround for minibuffer cursor (seems fixed).
This commit is contained in:
Chris Feng 2016-07-19 19:23:37 +08:00
parent 624c72945a
commit 3909f65bae
2 changed files with 113 additions and 59 deletions

View file

@ -125,24 +125,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(exwm-input--set-focus exwm--id)
(when exwm--floating-frame
;; Adjust stacking orders of the floating container.
(if (exwm-workspace--minibuffer-own-frame-p)
;; Put this floating X window just below the minibuffer.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask
(logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling (frame-parameter
exwm-workspace--minibuffer
'exwm-container)
:stack-mode xcb:StackMode:Below))
;; Put this floating X window at top.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Above)))
:stack-mode xcb:StackMode:Above))
;; This floating X window might be hide by `exwm-floating-hide'.
(when (exwm-layout--iconic-state-p)
(exwm-layout--set-state exwm--id

View file

@ -308,20 +308,32 @@ Value nil means to use the default position which is fixed at bottom, while
'exwm-container))
y width)
(setq y (if (eq exwm-workspace-minibuffer-position 'top)
0
(- (aref workarea 3)
(frame-pixel-height exwm-workspace--minibuffer)))
(- (aref workarea 1)
exwm-workspace--attached-minibuffer-height)
;; Reset the frame size.
(set-frame-height exwm-workspace--minibuffer 1)
(redisplay) ;FIXME.
(+ (aref workarea 1) (aref workarea 3)
(- (frame-pixel-height exwm-workspace--minibuffer))
exwm-workspace--attached-minibuffer-height))
width (aref workarea 2))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container
:value-mask (logior xcb:ConfigWindow:Y
:value-mask (logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y
xcb:ConfigWindow:Width
xcb:ConfigWindow:StackMode)
:x (aref workarea 0)
:y y
:width width
:stack-mode xcb:StackMode:Above))
(set-frame-width exwm-workspace--minibuffer width nil t)))
:stack-mode xcb:StackMode:Below))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-outer-id)
:value-mask xcb:ConfigWindow:Width
:width width))))
(defun exwm-workspace--switch-map-nth-prefix (&optional prefix-digits)
"Allow selecting a workspace by number.
@ -428,17 +440,11 @@ The optional FORCE option is for internal use only."
;; Might be aborted by then.
(when (active-minibuffer-window)
(abort-recursive-edit)))))
(if (not (exwm-workspace--minibuffer-own-frame-p))
(setq default-minibuffer-frame frame)
;; Resize/reposition the minibuffer frame
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window
(frame-parameter exwm-workspace--minibuffer
'exwm-container)
:parent (frame-parameter frame 'exwm-workspace)
:x 0 :y 0))
(exwm-workspace--resize-minibuffer-frame))
(if (exwm-workspace--minibuffer-own-frame-p)
;; Resize the minibuffer frame.
(exwm-workspace--resize-minibuffer-frame)
;; Set a default minibuffer frame.
(setq default-minibuffer-frame frame))
;; Hide windows in other workspaces by preprending a space
(unless exwm-workspace-show-all-buffers
(dolist (i exwm--id-buffer-alist)
@ -778,8 +784,59 @@ INDEX must not exceed the current number of workspaces."
(xcb:flush exwm--connection)
frame))
(defun exwm-workspace--update-minibuffer (&optional echo-area)
"Update the minibuffer frame."
(defsubst exwm-workspace--minibuffer-attached-p ()
"Return non-nil if the minibuffer is attached.
Please check `exwm-workspace--minibuffer-own-frame-p' first."
(assq (frame-parameter exwm-workspace--minibuffer 'exwm-container)
exwm-workspace--id-struts-alist))
(defvar exwm-workspace--attached-minibuffer-height 0
"Height (in pixel) of the attached minibuffer.
If the minibuffer is detached, this value is 0.")
(defun exwm-workspace-attach-minibuffer ()
"Attach the minibuffer so that it always shows."
(interactive)
(when (and (exwm-workspace--minibuffer-own-frame-p)
(not (exwm-workspace--minibuffer-attached-p)))
;; Reset the frame size.
(set-frame-height exwm-workspace--minibuffer 1)
(redisplay) ;FIXME.
(setq exwm-workspace--attached-minibuffer-height
(frame-pixel-height exwm-workspace--minibuffer))
(let ((container (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
(push (cons container
(if (eq exwm-workspace-minibuffer-position 'top)
(vector 0 0 exwm-workspace--attached-minibuffer-height 0)
(vector 0 0 0 exwm-workspace--attached-minibuffer-height)))
exwm-workspace--id-struts-alist)
(exwm-workspace--update-struts)
(exwm-workspace--update-workareas)
(dolist (f exwm-workspace--list)
(exwm-workspace--set-fullscreen f))
(exwm-workspace--show-minibuffer))))
(defun exwm-workspace-detach-minibuffer ()
"Detach the minibuffer so that it automatically hides."
(interactive)
(when (and (exwm-workspace--minibuffer-own-frame-p)
(exwm-workspace--minibuffer-attached-p))
(setq exwm-workspace--attached-minibuffer-height 0)
(let ((container (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
(setq exwm-workspace--id-struts-alist
(assq-delete-all container exwm-workspace--id-struts-alist))
(exwm-workspace--update-struts)
(exwm-workspace--update-workareas)
(dolist (f exwm-workspace--list)
(exwm-workspace--set-fullscreen f))
(exwm-workspace--hide-minibuffer))))
(defun exwm-workspace--update-minibuffer-height (&optional echo-area)
"Update the minibuffer frame height."
(let ((height
(with-current-buffer
(window-buffer (minibuffer-window exwm-workspace--minibuffer))
@ -802,7 +859,7 @@ INDEX must not exceed the current number of workspaces."
(defun exwm-workspace--on-ConfigureNotify (data _synthetic)
"Adjust the container to fit the minibuffer frame."
(let ((obj (make-instance 'xcb:ConfigureNotify))
value-mask y)
workarea y)
(xcb:unmarshal obj data)
(with-slots (window height) obj
(when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)
@ -818,19 +875,19 @@ INDEX must not exceed the current number of workspaces."
:window window
:value-mask xcb:ConfigWindow:Height
:height height)))
(if (eq exwm-workspace-minibuffer-position 'top)
(setq value-mask xcb:ConfigWindow:Height
y 0)
(setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height)
y (- (aref (elt exwm-workspace--workareas
(setq workarea (elt exwm-workspace--workareas
exwm-workspace-current-index)
3)
height)))
y (if (eq exwm-workspace-minibuffer-position 'top)
(- (aref workarea 1)
exwm-workspace--attached-minibuffer-height)
(+ (aref workarea 1) (aref workarea 3) (- height)
exwm-workspace--attached-minibuffer-height)))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-container)
:value-mask value-mask
:value-mask (logior xcb:ConfigWindow:Y
xcb:ConfigWindow:Height)
:y y
:height height))
(xcb:flush exwm--connection)))))
@ -851,26 +908,31 @@ INDEX must not exceed the current number of workspaces."
(setq exwm-workspace--display-echo-area-timer nil))
;; Show the minibuffer frame.
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
'exwm-container)
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Above))
(xcb:flush exwm--connection)
;; Unfortunately we need the following lines to workaround a cursor
;; flickering issue for line-mode floating X windows. They just make the
;; minibuffer appear to be focused.
(with-current-buffer (window-buffer (minibuffer-window
exwm-workspace--minibuffer))
(setq cursor-in-non-selected-windows
(frame-parameter exwm-workspace--minibuffer 'cursor-type))))
;; (FIXED?)
;; (with-current-buffer (window-buffer (minibuffer-window
;; exwm-workspace--minibuffer))
;; (setq cursor-in-non-selected-windows
;; (frame-parameter exwm-workspace--minibuffer 'cursor-type)))
)
(defun exwm-workspace--hide-minibuffer ()
"Hide the minibuffer frame."
;; Hide the minibuffer frame.
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
'exwm-container)
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(xcb:flush exwm--connection))
(defun exwm-workspace--on-minibuffer-setup ()
@ -878,7 +940,7 @@ INDEX must not exceed the current number of workspaces."
(when (and (= 1 (minibuffer-depth))
;; Exclude non-graphical frames.
(frame-parameter nil 'exwm-outer-id))
(add-hook 'post-command-hook #'exwm-workspace--update-minibuffer)
(add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
(exwm-workspace--show-minibuffer)
;; Set input focus on the Emacs frame
(x-focus-frame (window-frame (minibuffer-selected-window)))))
@ -888,7 +950,7 @@ INDEX must not exceed the current number of workspaces."
(when (and (= 1 (minibuffer-depth))
;; Exclude non-graphical frames.
(frame-parameter nil 'exwm-outer-id))
(remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer)
(remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
(exwm-workspace--hide-minibuffer)))
(defvar exwm-input--during-command)
@ -900,7 +962,7 @@ INDEX must not exceed the current number of workspaces."
(frame-parameter nil 'exwm-outer-id)
(or (current-message)
cursor-in-echo-area))
(exwm-workspace--update-minibuffer t)
(exwm-workspace--update-minibuffer-height t)
(exwm-workspace--show-minibuffer)
(unless (or (not exwm-workspace-display-echo-area-timeout)
exwm-input--during-command ;e.g. read-event
@ -1217,9 +1279,14 @@ applied to all subsequently created X frames."
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window container
:data "Minibuffer container")))
;; Reparent the minibuffer frame to the container.
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window outer-id :parent container :x 0 :y 0))
;; Map the container.
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow
:window container))
;; Attach event listener for monitoring the frame
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes