Updated clickable area processing to allow nested areas.

This commit is contained in:
szos 2022-05-28 15:28:22 +02:00
parent c456632ac2
commit 44ebc9bf5d
3 changed files with 79 additions and 67 deletions

View file

@ -332,64 +332,64 @@ string."
"Renders STRING-OR-PARTS to the pixmap in CC. Returns the height and width of
the rendered line as two values. The returned width is the value of X plus the
rendered width."
(let* ((parts (if (stringp string-or-parts)
(parse-color-string string-or-parts)
string-or-parts))
(height (max-font-height parts cc))
(current-on-click nil))
(loop
for (part . rest) on parts
for font-height-difference = (- height
(font-height (ccontext-font cc)))
for y-to-center = (floor (/ font-height-difference 2))
if (stringp part)
do (draw-image-glyphs
(ccontext-px cc)
(ccontext-gc cc)
(ccontext-font cc)
draw-x (+ y y-to-center (font-ascent (ccontext-font cc)))
part
:translate #'translate-id
:size 16)
(incf draw-x (text-line-width (ccontext-font cc)
part
:translate #'translate-id))
else
do (case (first part)
((:on-click)
(when ml
(setf current-on-click (list draw-x (cadr part) (cddr part)))))
((:on-click-end)
(when ml
(register-ml-boundaries-with-id
ml
(first current-on-click)
draw-x
y
(+ y y-to-center (font-ascent (ccontext-font cc)))
(second current-on-click)
(third current-on-click))
(setf current-on-click nil)))
((:>)
(let ((xbeg (- (xlib:drawable-width (ccontext-px cc))
x
(rendered-string-size rest cc))))
;; Register a sentinel on click boundary to prevent overlapping
;; renders from being clickable (eg if a formatter before :> is
;; larger than the space available for it).
(when ml
(register-ml-boundaries-with-id ml xbeg most-positive-fixnum
y
(+ y y-to-center (font-ascent
(ccontext-font
cc)))
:ml-on-click-do-nothing
nil))
(render-string rest cc xbeg y :ml ml))
(loop-finish))
(otherwise
(apply #'apply-color cc (first part) (rest part)))))
(values height draw-x)))
(macrolet ((register (thing)
`(let ((top ,thing))
(when top
(register-ml-boundaries-with-id ml
(first top)
draw-x
y
(+ y y-to-center
(font-ascent
(ccontext-font cc)))
(second top)
(third top))))))
(let* ((parts (if (stringp string-or-parts)
(parse-color-string string-or-parts)
string-or-parts))
(height (max-font-height parts cc))
(current-on-click nil))
(loop
for (part . rest) on parts
for font-height-difference = (- height
(font-height (ccontext-font cc)))
for y-to-center = (floor (/ font-height-difference 2))
if (stringp part)
do (draw-image-glyphs
(ccontext-px cc)
(ccontext-gc cc)
(ccontext-font cc)
draw-x (+ y y-to-center (font-ascent (ccontext-font cc)))
part
:translate #'translate-id
:size 16)
(incf draw-x (text-line-width (ccontext-font cc)
part
:translate #'translate-id))
else
do (case (first part)
((:on-click)
(when ml
(push (list draw-x (cadr part) (cddr part)) current-on-click)))
((:on-click-end)
(when ml
(register (pop current-on-click))))
((:>)
(let ((xbeg (- (xlib:drawable-width (ccontext-px cc))
x
(rendered-string-size rest cc))))
;; Register a sentinel on click boundary to prevent overlapping
;; renders from being clickable (eg if a formatter before :> is
;; larger than the space available for it).
(when ml
(loop for top = (pop current-on-click)
while top
do (register top)))
(render-string rest cc xbeg y :ml ml))
(loop-finish))
(otherwise
(apply #'apply-color cc (first part) (rest part)))))
(values height draw-x))))
(defun render-strings (cc padx pady strings highlights &key ml)
(let* ((gc (ccontext-gc cc))

View file

@ -333,7 +333,8 @@ timer.")
()
:ml ml)
(when (mode-line-new-bounds ml)
(setf (mode-line-on-click-bounds ml) (mode-line-new-bounds ml)))))
(setf (mode-line-on-click-bounds ml)
(reverse (mode-line-new-bounds ml))))))
(handler-case
(when (or force (not (string= (mode-line-contents ml) str)))
(resize-and-render str))

View file

@ -2027,10 +2027,12 @@ color variables.
@node Mode-line Interaction, , Mode-line, Mode-line
@section Mode-line Interaction
Mode line formatters can register sections of text to be clickable by
use of the color formatters :ON-CLICK and :ON-CLICK-END. Any text
enclosed by these formatters has its bounds saved, and when the
mode line recieves a button press event these bounds are checked against
to find a clickable area, whose registered function is then called.
use of the color formatters @code{:on-click} and
@code{:on-click-end}. Any text enclosed by these formatters has its
bounds saved, and when the mode line recieves a button press event these
bounds are checked against to find a clickable area, whose registered
function is then called. These formatters can be thought of as similar
to XML tags.
To disable the on-click behavior, remove the function
@code{check-for-ml-press} from the hook @var{*mode-line-click-hook*}.
@ -2049,7 +2051,7 @@ example of a click-to-focus function and its registration:
@end example
This defines a function that focuses a window based upon its X11 window
ID, and registers it under the ID @code{:ML-ON-CLICK-FOCUS-WINDOW}. Here
ID, and registers it under the ID @code{:ml-on-click-focus-window}. Here
is an example of a mode line formatter that makes use of this function:
@example
@ -2078,14 +2080,23 @@ space seperated and clickable."
In the above formatter, every windows expansion is wrapped in a
:on-click/end pair, which takes the ID we registered as the function to
call and the window ID as an argument to be passed to its function. The
arguments provided to :on-click will be read but not evaluated. The
string generated will look like so:
arguments provided to @code{:on-click} will be read but not
evaluated. The string generated will look like so:
@example
"^(:on-click :ml-on-click-focus-window 308242)window text^(:on-click-end)"
@end example
If one wished for right click to delete the window, then the following
Clickable text can be nested, in which case the innermost clickable text
will take precendent. In the following example @code{:id2} will be
dispatched when clicking @code{2}, but @code{:id1} will be dispatched
when clicking @code{1} and @code{3}:
@example
"^(:on-click :id1)1^(:on-click :id2)2^(:on-click-end)3^(:on-click-end)"
@end example
If one wished for right click to delete windows, then the following
example could be placed in the .stumpwmrc:
@example