Frame initialization

Paolo Amoroso amoroso at mclink.it
Mon Aug 9 18:32:30 CEST 2004


Paolo Amoroso <amoroso at mclink.it> writes:

> In a frame, I am trying to automatically initialize the value of a
> gadget based on the value of a frame slot at creation time.  I use
> code like this:
>
> (in-package :clim-user)
>
> (define-application-frame frame-init ()
>   ((default-value :initarg :default :accessor default-value :initform nil))
>   (:panes
>    (text :text-field :value "          "))
>   (:layouts
>    (default
>        (labelling (:label "Text field")
>          text))))
>
> (defun init-default (frame)
>   (when (default-value frame)
>     (setf (gadget-value (find-pane-named frame 'text))
>           (default-value frame))))
>
> (defmethod run-frame-top-level :before ((frame frame-init) &key)
>   (init-default *application-frame*))
[...]
> also been created.  Instead, when evaluating a form such as:
>
> (run-frame-top-level (make-application-frame 'frame-init :default "Paolo"))
>
> I get this error (from the sldb buffer):
>
> No matching method for the generic function
> #<STANDARD-GENERIC-FUNCTION GOATEE::BUFFER (2) {2834A229}>, when called with
> arguments (NIL).
>    [Condition of type PCL::NO-APPLICABLE-METHOD-ERROR]
>
> Restarts:
>   0: [CONTINUE] Retry call to :FUNCTION.
>   1: [ABORT] Abort handling SLIME request.
>   2: [ABORT] Return to Top-Level.
>
> Backtrace:
>   0: ("DEFMETHOD NO-APPLICABLE-METHOD (T)" #<#1=unused-arg> #<#1#>
>       #<STANDARD-GENERIC-FUNCTION GOATEE::BUFFER (2) {2834A229}> (NIL))
>   1: ((METHOD (SETF GADGET-VALUE) (:AFTER) (T TEXT-FIELD-PANE)) (#(67) . #())
>       #<unused-arg> "Paolo" #<TEXT-FIELD-PANE TEXT {580047ED}> ...)
[...]

In gadget.lisp, the (setf gadget-value) after method on
text-field-pane is:

(defmethod (setf gadget-value) :after (new-value (gadget text-field-pane)
                                       &key invoke-callback)
  (declare (ignore invoke-callback))
  (let* ((area (area gadget))
         (buffer (goatee::buffer area))
         (start (goatee::buffer-start buffer))
         (end (goatee::buffer-end buffer)))
    (goatee::delete-region buffer start end)
    (goatee::insert buffer new-value :position start)
    (goatee::redisplay-area area)))

Area is a text-field-pane slot defined like this:

(area :accessor area :initform nil
      :documentation "The Goatee area used for text editing.")

The area slot is initialized by this method:

;; Is there really a benefit to waiting until the first painting to
;; create the goatee instance? Why not use INITIALIZE-INSTANCE?
(defmethod handle-repaint :before ((pane text-field-pane) region)
  (declare (ignore region))
  (unless (area pane)
    (multiple-value-bind (cx cy)
        (stream-cursor-position pane)
      (setf (cursor-visibility (stream-text-cursor pane)) nil)
      (setf (area pane) (make-instance 'goatee:simple-screen-area
                                       :area-stream pane
                                       :x-position cx
                                       :y-position cy
                                       :initial-contents (slot-value pane
                                                                     'value))))
    (stream-add-output-record pane (area pane))))

But when my code in init-default calls (setf gadget-value), the text
field may not have had a chance to handle a repaint event yet, and the
area slot may still contain nil as per its initform.  Hence the nil
passed to goatee::buffer in the let* form of (setf gadget-value).

Is this a McCLIM bug?  If so, the comment before handle-repaint may
hint at how to fix it.


Paolo
-- 
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film



More information about the mcclim-devel mailing list