[prev in list] [next in list] [prev in thread] [next in thread] 

List:       jedit-devel
Subject:    [ jEdit-devel ] TextAreaPainter: text and caret painting
From:       Makarius <makarius () sketis ! net>
Date:       2011-06-25 19:57:43
Message-ID: alpine.LNX.1.10.1106252104190.29425 () atbroy100 ! informatik ! tu-muenchen ! de
[Download RAW message or body]

As explained before, I have my own version of token marking and text 
painting (now for jedit-4.4.1).  The following is about some fine points 
of text and caret painting that I've noticed when implementing this in 
Scala: 
http://isabelle.in.tum.de/repos/isabelle/file/bf7400573617/src/Tools/jEdit/src/text_area_painter.scala


Since I don't know much Java, I need to explain things in terms of Scala. 
I hope this can be understood informally as "pseudo-code" for a 
corresponding Java program, for people who are not working regularly with 
Scala.


* Generally, the architecture of TextAreaPainter is easy to extend: there 
are several layers, and the jEdit default setup uses just the same 
mechanisms that are available to plugins as well (operations around 
TextAreaExtension).  Two small things have required some workarounds on my 
side, though.

(1) Inner classes TextAreaPainter.PaintText and
   TextAreaPainter.PaintCaret are private, which means in order to
   remove them one needs to play untyped tricks with reflection:

   private def pick_extension(name: String): TextAreaExtension =
   {
     text_area.getPainter.getExtensions.iterator.
       filter(x => x.getClass.getName == name).toList
     match {
       case List(x) => x
       case _ => error("Expected exactly one " + name)
     }
   }

   private val orig_text_painter =
     pick_extension("org.gjt.sp.jedit.textarea.TextAreaPainter$PaintText")


   This might look like a slight abuse of TextAreaPainter.getExtensions.
   If TextAreaPainter would make the standard extensions publicly
   available as objects, one could remove them in a statically
   typed way.


(2) The object caretExtension (class PaintCaret) is removed/added at each
   propertiesChanged event, since it might jump between layers:
   BLOCK_CARET_LAYER vs. CARET_LAYER.  (Does anybody know the reasons for
   these different layers of thin line vs. rectangle caret?  I reckon that
   one could always paint over or under the text layer, say.)

   This means I could not remove the standard caret extension reliably.
   Instead I added my own extensions just before and after the usual caret
   layers to mask its effect via clipping on the gfx context.  (If any
   other plugin installs anything tere, it will be masked as well.)
   I also had to ignore the blink feature, because that field is private.

   BTW, the deeper reason why I wanted my own caret painter is this: using
   alternative font styles (notably sub- and superscript), I wanted to have
   precise visual feedback cerning the character metrics of the caret
   position, not just a static line or rectangle (block caret).  It also
   helps for non-proportional fonts, especially long mathematical arrows.

   So the caret painting moved into my text painter, using
   java.awt.font.TextAttribute.SWAP_COLORS on suitable
   java.text.AttributedString.  Clipping ensures that the standard
   caret does not interfere with it.


(3) Precise line boundary.  Painting with exotic font styles or reverse
   text has lead to some surprises concerning default Java font metrics.
   The basic model of TextArea painting seems to be that each line can be
   re-painted independently, so they must be disjoint in the gfx view.
   Some small overshots or undershots would produce ugly residual spots
   when moving the caret between lines, for example.  (I have also seen
   this with official jEdit text painting and OpenJDK 6, with its lack of
   proper Graphics2D.)

   So what I did is to have the line text painter introduce a temporay clip
   for the precise line height according to jEdit:

   gfx.clipRect(x0, y + line_height * i, Integer.MAX_VALUE, line_height)
   ...
   gfx.drawString(...)

   See also
   http://isabelle.in.tum.de/repos/isabelle/file/bf7400573617/src/Tools/jEdit/src/text_area_painter.scala#l275
  for the greater context of this code.


   So one might consider fine-tuning of the standard TextArea painter:

     * Either do the clipping only for TextAreaPainter.PaintText
       individually.  (It might be actually relevant in jedit-4.4.1 with
       its new font substitution scheme, since alien fonts with slightly
       different metrics may be painted over the original line layout in
       unexpected ways.)

     * Or do the clipping uniformly for the default implementation of
       TextAreaExtension.paintScreenLineRange, just before and after the
       invocation of the user-provided implementations of paintInvalidLine
       and paintValidLine.

       This would leave the general paintScreenLineRange entry point open
       for advanced plugins, but the default would keep away these worries
       from clients.

Anyway, I think TextArea with its flexible layered painting mechanism is 
one of the assets of jEdit.  At a very early stage of the project, I had a 
student trying to make anything like that with Netbeans, but he failed 
miserably on all the private/final stuff in their slightly over-engineered 
architecture.  I am much more comfortable with the jEdit code base now.


 	Makarius

------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure contains a 
definitive record of customers, application performance, security 
threats, fraudulent activity and more. Splunk takes this data and makes 
sense of it. Business sense. IT sense. Common sense.. 
http://p.sf.net/sfu/splunk-d2d-c1
-- 
-----------------------------------------------
jEdit Developers' List
jEdit-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jedit-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic