Text-Position in JTextField bei unterschiedlichen L&Fs? Problem :(

VooDooTom

Grünschnabel
Hallo,

ein wirklich informatives Forum habt ihr - hab schon viel duch nur lesen mitbekommen :).

Ich habe momentan ein Problem mit JText-Fields. Ich habe mir ein JTextField abgeleitet damit ich diesem RenderingHints zuweisen kann (in der paintComponent-Methode). Dies mache ich da ich eine eigene Schriftart anzeigen lassen will und ohne die wird diese scheinbar nicht Anti-Aliased. Wie auch immer, in der PainComponent-Methode sitzt außerdem die Funktionalität bestimmte Klammern (aus meiner Schriftart - daher die Unicode-Zeichen) mit einem Rahmen zu versehen wenn man nach die öffnende Klammer oder vor die entsprechende schließende Klammer klickt, bzw. der Caret da ist. (Die Rechtecke um die Klammern sind dann bei der öffnenden und schließenden Klammer - vgl. mit der Funkion vieler IDEs).
Das Problem ist dass die Position der Markierungs-Rechtecke über die Font berechnet wird und das klappt auch alles ganz gut bis ich das Look&Feel umstelle. Im "Metal"-L&F beginnt der Text im TextField fast ganz vorne am Rand und da stimmt die Angabe von Position über die Font, genauso im Motif. Aber wenn ich jetzt das Win-L&F lade beginnt der Text nicht ganz vorn in der Textbox, sondern ca 10px weiter rechts und damit sind die Markierungen verschoben.
Gibt es eine Möglichkeit rauszufinden wo der Text nun tatsächlich beginnt in der Textbox oder das Problem anders zu lösen? Danke euch.
Ist mein Problem verständlich beschrieben oder sollte ich vielleicht noch ein paar Screenshots hochladen?

Hier dier Code:

Code:
package org.jalgo.module.ebnf.gui.ebnf;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;

import javax.swing.JTextField;
import javax.swing.text.BadLocationException;

/**Diese Klasse erstellt ein Textfield mit zusätzlichen RenderingHints für unsere EBNF-Schrift
 * @author Tom
 *
 */
public class EbnfTextField extends JTextField {
	
	private int movecount = 0;
	int nextpos = 0;
	
	String text2 = "\u300C"+"\u300F"+"\u3011";
	String text3 = "\u300D"+"\u3010"+"\u3012";

	public EbnfTextField() {}
	
	 
    public EbnfTextField(String text)
    {
        super(text);
    }
 
    public void paintComponent(Graphics g)
    {
    	// Unicode-Mappings: "\u300C" = [, "\u300D" = ], "\u300E" = |",
    	// "\u300F" = (, " \u3010" = ), " \u3011" = {, "\u3012" = }
    	// 3014 und 3015 sind nochmal () für die generierten Klammern
    	
    	Graphics2D g2 = (Graphics2D)g;
    	int i = this.getCaretPosition();
    	
    	String text = "";
    	
    	try {
			text = this.getText(i - 1, 1);
		} catch (BadLocationException e) {
		}
		
    	
        
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        super.paintComponent(g);
        
        if (text2.contains(text)) {				//Bracket is opening
    		g2.setColor(Color.LIGHT_GRAY);
    		g2.draw(findCorrectCharacterPos(i));
    		nextpos = findNextChaaracterPos(i, text, true);
			g2.draw(findCorrectCharacterPos(nextpos));
    		
			
			
    		if ((movecount != i) || (movecount - 1 != i) || (movecount + 1 != i)) {
    			this.repaint();
    		}
    		
    		movecount = i;
    		
        } else {
        	try {
    			text = this.getText(i, 1);
    			
    			if (text3.contains(text)) {			// Bracket is closing
    	    		g2.setColor(Color.LIGHT_GRAY);
    	    		g2.draw(findCorrectCharacterPos(i+1));
    	    		nextpos = findNextChaaracterPos(i+1, text, false);
					g2.draw(findCorrectCharacterPos(nextpos));
					
    	    		if ((movecount != i) || (movecount - 1 != i) || (movecount + 1 != i)) {
    	    			this.repaint();
    	    		}
    	    		movecount = i;
    	        }
    			
    			
    		} catch (BadLocationException e) {
    		}
    		
        }
    	
    }
    
    /**The function <code>findCorrectCharacterPos</code> generates a fitting rectangle for
     * out brackets, parenth. and braces.
     * 
     * @param pos is the character-position in the TextField
     * @return the correct rectangel is returned
     */private Rectangle2D findCorrectCharacterPos(int pos) {
    	Font font = this.getFont();
    	FontMetrics metrics = getFontMetrics( font );
    	Rectangle2D rect;
    	int position = 0;
    	int width = 0;
    	
    	try {
    		position = metrics.stringWidth(this.getText(0, pos-1));
    		width = metrics.stringWidth(this.getText(pos-1, 1));
		} catch (BadLocationException e) {
		}
		
		rect = new Rectangle2D.Float(position, 2, width + 3, 27);
    	
    	return rect;
    }
    
    /**This function finds the correct closing or opening bracket/parenth./brace for an correspondending
     * closing/opening one.
     * 
     * @param pos is the start position to search from
     * @param searchtext is the String of the bracket/parenth./brace to look for
     * @param fromlefttoright should be set to true if the clicket bracket/.../... is opening, otherwise falses
     * @return the position of the found matching bracket/.../... is given back
     */private int findNextChaaracterPos(int pos, String searchtext, boolean fromlefttoright) {
    	String nextbracket = "";
    	String text = this.getText();
    	int counter = 0;
    	if (searchtext.equals("\u300C")) nextbracket = "\u300D";
    	else if (searchtext.equals("\u300F")) nextbracket = "\u3010";
    	else if (searchtext.equals("\u3011")) nextbracket = "\u3012";
    	else if (searchtext.equals("\u300D")) nextbracket = "\u300C";
    	else if (searchtext.equals("\u3010")) nextbracket = "\u300F";
    	else if (searchtext.equals("\u3012")) nextbracket = "\u3011";

    	if (fromlefttoright) {
	    	for(int i=pos;i<text.length(); i++) {
				if ((text.substring(i,i+1).equals(nextbracket)) && (counter == 0)) {
					return i+1;
				} else if (text.substring(i,i+1).equals(searchtext)) {
					counter++;
				} else if ((text.substring(i,i+1).equals(nextbracket)) && (counter != 0)) {
					counter--;
				}
	    	}
    	} else {
    		for(int i=pos-2;i>=0; i--) {
				if ((text.substring(i,i+1).equals(nextbracket)) && (counter == 0)) {
					return i+1;
				} else if (text.substring(i,i+1).equals(searchtext)) {
					counter++;
				} else if ((text.substring(i,i+1).equals(nextbracket)) && (counter != 0)) {
					counter--;
				}
	    	}
    	}
    		
    	return 0;
    }

}
 
Hallo,

danke für die Schnelle Hilfe. Genau das habe ich gesucht :)

Ich habe noch eine Frage. Und zwar ist der Code wenn ich ihn wie oben einsetzte sehr langsam. Es geht zwar alles wie es soll und auch an sich recht schnell, nur ist die CPU-Leistung sobald ich an eine solche Klammer komme (sprich die Markierung gezeichnet wird) sehr sehr hoch, bei 100%.

Gibt es Mittel und Wege dies zu umgehen bzw. zu verbessern?

Danke euch :)
 
Hallo Voodoo,

ich würde das eher über eine JTextPane oder JEditorPane oder einer vergleichbaren Komponente machen. Soviele Berechnungen innerhalb der paintMethode für eine Textkomponente ist nicht unbedingt die beste Lösung. Das Problem hierbei ist ja, die Komponente muss bei jedem Tippen eines weiteren Zeichens neu gezeichnet werden, und jedes mal alles erneut berechnen. Hab deine Klasse ausprobiert, also die Berechnungen stimmen nicht so ganz, bspw. ist das Caret nicht immer an korrekter Stelle. Damit man das ordentlich hinbekommt, braucht man schon tiiiiiiiiiefere Kentnisse. Bedenke nur die Klasse JTextField hat einen Quellcode bestehend aus 900 Zeilen. Die Klasse JTextField erbt von JTextComponent welche 3700 Zeilen Code hat. Hinzu kommen weitere Zahlreiche andere Klassen und Interfaces. Hinter einem simplen Textfeld steckt ziemlich viel.

Alternativer Lösungsansatz:
Nimm ein JTextPane. Registriere einen Documentlistener oder Caretlistener oder irgendetwas vergleichbares. In dem Listener überprüfst du jetzt ob irgendwo deine Sonderzeichnen auftauchen. Falls ja gibst du ihm einfach eine andere Schriftfarbe oder eine andere Hintergrundfarbe oder einen Schatten. Du könnstest auch vor und nach deinen Sonderzeichen irgendwelche bestimmten Zeichen hinzufügen die die Sonderzeichen kenntlich machen. Die Klammern analog auch irgendwie markieren. Das wichtige hierbei ist, das du nicht selbst zeichnest, sondern die Möglichkeiten von JTextPane nutzt. So wirst du zum einen weniger Performance Probleme haben, aber auch werden die Markierungen wie Rahmen, Klammern, oder Schatten etc. immer an richtiger Stelle sein, da dies der JTextPane für dich macht.

Auch solltest du bevor du weiter an deinem SuperTextField arbeitest, dir einen Überblick verschaffen wie man Syntaxhervorhebung implementiert, welche Möglichkeiten es hierfür in Java gibt. Bedenke das du nicht der erste bist, der Syntax-Highlighting bzw. Syntaxhervorhebung implementiert. Mein Tip, zuerst etwas Info sammeln wie man das am besten in Java realisiert.

Na ja, das war jetzt schon etwas zuviel Weisheit und Ratschlag. Hoffe du verzeihst mir :) .

Schau mal hier:
http://www.tutorials.de/forum/java/239064-highlighting.html
http://javaalmanac.com/egs/javax.swing.text/pkg.html#Styles (Abschnitt Styles)

Wenn du doch die paint-Variante befolgen möchtest:
http://java.sun.com/developer/onlineTraining/Media/2DText/index.html


Vg Erdal
 
Zuletzt bearbeitet:
Ui vielen Dank für den Text und die Links - da war mein Ansatz ja schon recht ungünsitg gewählt. Da werd ich mich jetzt wohl mal durch Styles, etc. durchwühllen und mein TextField in eine TextArea verwandeln :)

Danke
 
Zurück