Hinweis bei Caps Lock

Status
Nicht offen für weitere Antworten.

maximilius

Mitglied
Ich würde gerne realisieren, dass ein Benutzer bei einem Passworteingabefeld einen Hinweis eingeblendet bekommt, wenn die Umschalttaste aktiviert ist.

Der Hinweis soll erscheinen, sobald die Umschalttaste aktiv ist und wieder verschwinden, wenn sie deaktiviert ist.

Ich habe schon überlegt, ob ich JToolTip dafür missbrauchen sollte und habe angefangen mir etwas zusammenzusuchen.

Aber dann fiel mir auf, dass ich nicht so recht weiß, wie ich den Zustand der Umschalttaste erkennen soll.
Wenn ich im JFrame Keylistener einsetze bemerke ich das Drücken der Umschalttaste nicht, wenn sie außerhalb des Programms gedrückt wird, dann wird der ToolTip eventuell angezeigt, obwohl er es gar nicht soll.

Jemand eine Idee?
Oder einen ganz anderen Vorschlag?

lg Stephan
PS: Der ToolTip soll nicht an der Maus hängen, sondern irgendwo um das Eingabefeld platziert.
 

Wildcard

Top Contributor
Code:
Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
Für den Hinweistext:
Für SWT (JFace) gibt es fertige Klasse um solche Decorations anzuzeigen, vielleicht findest du für Swing ähnliches. Ansonsten ist selbst Zeichnen angesagt, aber nicht unbedingt Tooltip, sondern eher Popup.
 

maximilius

Mitglied
Danke für den Codeschnipsel.

Ich habe nun folgende Klasse geschrieben:
Code:
package net.siedler3.alobby.gui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Popup;
import javax.swing.PopupFactory;
/**
 * Wird eine Instanz dieser Klasse einer Component als FocusListener
 * hinzugefügt, erzeugt sie über der Component ein Hinweispopup, wenn
 * die Umschalttaste aktiv ist.
 * @author Stephan Bauer
 */
public class CapsLockFocusListener implements FocusListener {

	private Popup popup;
	private Component observedComponent;
	private JPanel popupPanel;
	private PopupShowHideThread thread;
	
	/**
	 * Konstruktor.
	 */
	public CapsLockFocusListener() {
		popupPanel = new JPanel();
		popupPanel.setBackground(new Color(255, 255, 153));
		popupPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		popupPanel.add(new JLabel("Hinweis: Die Umschalttaste ist aktiv!"));
	}
	
	/**
	 * Startet die Umschalttasten-Überwachung, was zur Folge hat, dass
	 * der Hinweis je nach Zustand ein- oder ausgeblendet ist.
	 */
	@Override
	public void focusGained(FocusEvent e) {
		observedComponent = e.getComponent();
		thread = new PopupShowHideThread();
		thread.start();
	}

	/**
	 * Stoppt die Umschalttasten-Überwachung und blendet den eventuell
	 * angezeigten Hinweis aus.
	 */
	@Override
	public void focusLost(FocusEvent e) {
		if (thread != null) {
			if (thread.isAlive()) {
				thread.interrupt();
			}
			thread = null;
		}
		hidePopup();
	}

	/**
	 * Blendet den Hinweis aus, so einer angezeigt wird.
	 */
	private synchronized void hidePopup() {
		if (popup != null) {
			popup.hide();
			popup = null;
		}
	}
	
	/**
	 * Umschalttasten-Überwachung 
	 * @author stephan
	 */
	private class PopupShowHideThread extends Thread {
		/**
		 * Prueft im Sekundentakt, ob die Umschalttaste ein- oder aus-
		 * geschaltet ist. Zeigt dementsprechend den Hinweis an oder blendet
		 * ihn aus.
		 */
		public void run() {
			while (!isInterrupted()) {
				if (Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK)) {
					if (popup == null) {
						Point point = observedComponent.getLocationOnScreen();
						PopupFactory factory = PopupFactory.getSharedInstance();
					    popup = factory.getPopup(null, popupPanel, point.x, point.y - popupPanel.getHeight());
					    popup.show();
					}
				} else {
					hidePopup();
				}
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					interrupt();
				}
			}
		}
	}
}

Ich verwende die Klasse wie folgt:
Code:
// ...
JPasswordField inputPassword = new JPasswordField();
inputPassword.addFocusListener(new CapsLockFocusListener());
// ...

Und es klappt, wie ich mir das vorgestellt habe.

Ein Problem habe ich jedoch noch:
Beim erstmaligen Anzeigen des Hinweistexts, liegt er direkt auf dem Passwortfeld drauf, anstelle dadrüber (Der Hinweistext wird also nicht in Y-Richtung verschoben)

popupPanel.getHeight() liefert beim ersten mal 0.
Was muss ich tun, damit schon beim ersten mal die korrekte Höhe zurückgegeben wird?

lg Stephan
 

Wildcard

Top Contributor
Nur der EDT darf in die GUI eingreifen. Dein Thread ist so nicht zulässig. Warum nicht einfach einmal bei Focus gained prüfen und dann von mir aus noch wenn das document geändert wird?
 

maximilius

Mitglied
Wildcard hat gesagt.:
Nur der EDT darf in die GUI eingreifen.
Das wusste ich nicht.
Habe mich dazu belesen und jetzt folgende zwei Methoden, die je vom nested Thread aufgerufen werden:
Code:
	/**
	 * Blendet den Hinweis aus, so einer angezeigt wird.
	 */
	private void showPopup() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				if (popup == null) {
					Point point = observedComponent.getLocationOnScreen();
					PopupFactory factory = PopupFactory.getSharedInstance();
					popup = factory.getPopup(null, popupPanel, point.x, point.y - popupPanel.getHeight());
					popup.show();
				}
			}
		});		
	}
	
	/**
	 * Blendet den Hinweis aus, so einer angezeigt wird.
	 */
	private synchronized void hidePopup() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				if (popup != null) {
					popup.hide();
					popup = null;
				}
			}
		});	
	}
Aber es besteht immernoch das Problem, dass das Panel eine Höhe von 0 liefert.
Panel hat ja leider keine pack() Methode.
Wie kann ich anstoßen, dass Panel seine Dimensionen berechnet?

lg Stephan
 

maximilius

Mitglied
Ich habe weiter gesucht und gelesen, dass ein LayoutManager die Größe eines JPanels berechnet.
Da ich das JPanel erst dem ersten erzeugten Popup hinzufüge, wird erst nach der Erstellung des Popups die Größe des JPanels vom LayoutManager des Popups berechnet.
Da ich aber bereits beim Erzeugen des Popups brauch, gehe ich nun den weg, dass ich gleich nach dem Erstellen des JPanels dieses einem unsichtbaren Hilfsdialog hinzufüge und dessen LayoutManager die größe des JPanels berechnen lasse.

Der folgende Code funktioniert nun, wie gewünscht:

Code:
package net.siedler3.alobby.gui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;

import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;

/**
 * Wird eine Instanz dieser Klasse einer Component als FocusListener
 * hinzugefügt, erzeugt sie über der Component ein Hinweispopup, wenn
 * die Umschalttaste aktiv ist.
 * @author Stephan Bauer
 */
public class CapsLockFocusListener implements FocusListener {

	private Popup popup;
	private Component observedComponent;
	private JPanel popupPanel;
	private PopupShowHideThread thread;
	
	/**
	 * Konstruktor.
	 */
	public CapsLockFocusListener() {
		popupPanel = new JPanel();
		popupPanel.setBackground(new Color(255, 255, 153));
		popupPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		popupPanel.add(new JLabel("Hinweis: Die Umschalttaste ist aktiv!"));
		// Das popupPanel muss einmal von einem Layoutmanager berechnet
		// werden, damit es bei showPopup die richtige Höhe liefert.
		// Es wird in einem HilfeDialog eingebettet, von dessen LayoutManager
		// berechnet und danach der HilfeDialog beseitigt:
		JDialog invisibleHelpDialog = new JDialog();
		invisibleHelpDialog.add(popupPanel);
		invisibleHelpDialog.pack();
		invisibleHelpDialog.dispose();
	}
	
	/**
	 * Startet die Umschalttasten-Überwachung, was zur Folge hat, dass
	 * der Hinweis je nach Zustand ein- oder ausgeblendet ist.
	 */
	@Override
	public void focusGained(FocusEvent e) {
		observedComponent = e.getComponent();
		thread = new PopupShowHideThread();
		thread.start();
	}

	/**
	 * Stoppt die Umschalttasten-Überwachung und blendet den eventuell
	 * angezeigten Hinweis aus.
	 */
	@Override
	public void focusLost(FocusEvent e) {
		if (thread != null) {
			if (thread.isAlive()) {
				thread.interrupt();
			}
			thread = null;
		}
		hidePopup();
	}

	/**
	 * Blendet den Hinweis aus, so einer angezeigt wird.
	 */
	private void showPopup() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				if (popup == null) {
					Point point = observedComponent.getLocationOnScreen();
					PopupFactory factory = PopupFactory.getSharedInstance();
					popup = factory.getPopup(null, popupPanel, point.x, point.y - popupPanel.getHeight());
					popup.show();
				}
			}
		});		
	}
	
	/**
	 * Blendet den Hinweis aus, so einer angezeigt wird.
	 */
	private void hidePopup() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				if (popup != null) {
					popup.hide();
					popup = null;
				}
			}
		});	
	}
	
	/**
	 * Umschalttasten-Überwachung 
	 * @author Stephan Bauer
	 */
	private class PopupShowHideThread extends Thread {
		/**
		 * Prueft im Sekundentakt, ob die Umschalttaste ein- oder aus-
		 * geschaltet ist. Zeigt dementsprechend den Hinweis an oder blendet
		 * ihn aus.
		 */
		public void run() {
			while (!isInterrupted()) {
				if (Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK)) {
					showPopup();
				} else {
					hidePopup();
				}
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					interrupt();
				}
			}
		}
	}
}

lg Stephan
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben