Listener System... "schöner" schreiben?

Hey Leute.
Ich bastel ja seit einer gewissen Zeit an meiner lwjgl library…
und hab mich nun auch mal an einer kleinen gui implementierung versucht.
Im moment gibt es einfach nur ein AGLGBasicObject (AGLG steht immer für AbstractGraphicsLibraryGui)
und ein ziemlich schlechtes listener system.
Folgende Klasse sorgt für die Maus Daten:


import org.lwjgl.input.Mouse;

import de.skysoldier.abstractgl.lib.AGLCaps.AGLMouseButton;

public class AGLGMouseInputModule {
	
	private static int mousex;
	private static int mousey;
	private static int mouseDx;
	private static int mouseDy;	

	protected static void update(){
		mousex = Mouse.getX();
		mousey = Mouse.getY();
		mouseDx = Mouse.getDX();
		mouseDy = Mouse.getDY();
		while(Mouse.next()){
			if(Mouse.getEventButtonState()){
				AGLG.mousePressed(new AGLGMouseEvent(mousex, mousey, mouseDx, mouseDy, AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton()))));
			}
			else{
				if(Mouse.getEventButton() > -1){
					AGLG.mouseReleased(new AGLGMouseEvent(mousex, mousey, mouseDx, mouseDy, AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton()))));
				}
			}
		}
	}
	
	public static int getMousex(){
		return mousex;
	}
	
	public static int getMousey(){
		return mousey;
	}
	
	public static int getMouseDx(){
		return mouseDx;
	}
	
	public static int getMouseDy(){
		return mouseDy;
	}
}

wobei update() jeden frame aufgerufen wird.
die methoden pressed und released in AGLG (das ist die hauptklasse des gui systems)
sehen so aus:

		for(AGLGBasicObject o : g.getObjects()){
			if(o.getMouseListener() != null){
		                if(mouseInObject(o)){
                         		o.getMouseListener().mousePressed(e);
				}
			}
		}
	}```

so mach ich das. primitiv durch alle objects iterieren, auf kollision mit maus überprüfen und entsprechende
methode aufrufen. analog für pressed, moved und so weiter was ich halt alles haben will.

aber.. ist das nicht irgendwie ein bisschen schlecht so? In swing wirds wahrscheinlich nicht so einfach gemacht? ^^
Dort wird der listener zum beispiel auch nicht in der JComponent klasse gespeichert sondern irgendwo ganz anders..
#AWTEvent irgendwas...

Also wie könnte ich das verbessern? Jemand ne idee? 
Danke!

*** Edit ***

Ja und AGLBasicObject sieht dann so aus:

```package de.skysoldier.abstractgl.lib.g;

import de.skysoldier.abstractgl.lib.AGLBasicRenderObject;


public class AGLGBasicObject {

	private AGLBasicRenderObject graphicsInterface;
	private AGLGMouseListener mouseListener;
	
	public AGLGBasicObject(AGLBasicRenderObject o){
		this.graphicsInterface = o;
	}
	
	public void update(){
		graphicsInterface.requestUniformUpdate();
	}
	
	public void addMouseListener(AGLGMouseListener listener){
		this.mouseListener = listener;
	}
	
	public AGLGMouseListener getMouseListener(){
		return mouseListener;
	}
	
	protected AGLBasicRenderObject getGraphicsInterface(){
		return graphicsInterface;
	}
}```

[QUOTE=mymaksimus]

		for(AGLGBasicObject o : g.getObjects()){
			if(o.getMouseListener() != null){
		                if(mouseInObject(o)){
                         		o.getMouseListener().mousePressed(e);
				}
			}
		}
	}```[/QUOTE]
Sieht sehr aufwendig aus: Einmal die dauernde Prüfung, ob ein Objekt überhaupt einen MouseListener besitzt, dann im Falle eines Falles die Weitergabe des MouseEvents an den MouseListener des Objekts, der das Event wiederum verarbeiten muss.
Ich kenne den Kontext nicht, aber benötigen die Objekte wirklich ihren "eigenen" MouseListener? Wäre statt `o.getMouseListener().mousePressed(e);` nicht auch ein `o.mousePressed(e);` ausreichend? Dann könnte man sich die Prüfung auf MouseListener != null schon einmal sparen. Ist das Objekt tatsächlich am MouseEvent interessiert? Reicht nicht der Hinweis pressed, released usw. `o.mousePressed();`?

Grundsätzlich würde ich die Objekte "dumm" halten und die MouseEvents von einer zentralen Instanz (z.B. Controller oder Model denen alle relevanten Objekte bekannt sind) verarbeiten lassen. Hier wird dann entweder ganz stupide über alle Objekte iteriert und auf Relevanz des MouseEvents geprüft. Bei komplexeren "Systemen" mit einer größeren Anzahl von Objekten könnte man mit der Variante noch eine gewisse "Intelligenz" bei der Ermittlung einbauen.

Hm. meine überlegun war ja, das ganze so wie in swing zu gestalten,
das heisst ich habe zB ein JPanel (hier AGLGBasicObject). das reagiert ja
auch nicht auf mouse events, wenn ich nicht addListener aufgerufen hab oder?
Aber stimmt, ich könnte ja sowas wie einen default listener einbauen, der dann einfach nichts macht,
und dann einfach diesen überschreiben.

Aber was meinst du mit (von einer zentralen instanz) verarbeiten lassen?

äh, doch, nicht in JComponent, denn AWT muss ja auch schon funktionieren, sondern in der Component-Klasse

da ist erstmal nichts schlimmes dabei,
zumal du mit 10 Klassen und 10 Tagen Arbeit sicher nicht ein Mega-Projekt wie Swing oder AWT mit aller Komplexität nachbauen willst

ist zunächst auch ok, so oft gibt es Mouse-Events nicht, so viele GUI-Objekte wirst du kaum haben,
innerhalb einer Ebene geht Swing so bestimmt auch vor,
die Verschachtelung könntest du auch irgendwann benutzen:
wenn die gesamte Oberfläche von zwei Teilpanel belegt ist, darin weitere Komponenten, dann Event nur an eines der beiden Panel weiterleiten je nach Position usw.


allgemein ist noch nicht viel (schlechtes) zum System zu sagen, der Code von AGLGMouseInputModule fällt mir dann aber doch übel auf

Mouse ist static, AGLGMouseInputModule ist static?
was bedeutet Mouse.next(), gibt es mehrere Ereignisse mit denselben Koordinaten?

                AGLG.mousePressed(new AGLGMouseEvent(mousex, mousey, mouseDx, mouseDy, AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton()))));
            }
            else{
                if(Mouse.getEventButton() > -1){
                    AGLG.mouseReleased(new AGLGMouseEvent(mousex, mousey, mouseDx, mouseDy, AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton()))));
                }
            }

könnte auch

                AGLG.mousePressed(new AGLGMouseEvent(mousex, mousey, mouseDx, mouseDy, AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton()))));
            }

lauten oder sonstige Hilfsmittel wie boolean-Variable, den extrem langen Event-Konstruktor aber nicht zweimal aufschreiben!!

am schlimmsten aber ist das dreifache Vorkommen der 4 Koordinaten,

  • einmal in Mouse statisch: Mouse.getX()
  • dann in AGLGMouseInputModule getMousex() ohne erkennbare Verwendung
  • und in den Events auch noch,

zweimal sollte doch wohl als Maximum reichen, einmal die Quellklasse Mouse, und dann in Events hinein,
braucht außer ‘AGLGMouse’ eigentlich sonst noch wer ‘Mouse’ direkt? könnte vielleicht zusammenschmelzen

wie immer die Mouse-Klasse an x,y & Co. kommt (von Swing als Grundlage?),
dort vielleicht gar keine statischen Methoden und Attribute nötig, sondern direkt ein Event zur Ablage erzeugen

Das ist tatsächlich ein schwieriges Unterfangen. Sicher arbeitest du mit der LWJGL-Klasse Display und da hast du keinen Zugriff auf den AWTGLCanvas (welcher tatsächlich eine AWT-Klasse ist!) und damit auch keine Möglichkeit dort Listener ala AWT/Swing zu registrieren. Aber was wäre man für ein Programmierer, wenn man dafür nicht eine (oder mehrere) Möglichkeit(en) hätte.

  1. Du könntest dir ein eigenes Eventsystem basteln, das hätte den Nachteil, dass du dir auch sämtliche GUI-Elemente (Buttons usw.) neu erschaffen darfst, die mit diesem System arbeiten.

  2. Viel schöner wäre es also, wenn man LWJGL in einem JFrame als Swing-Komponente hätte.

Sofern LWJGL (getestet mit V2.9.1) installiert ist, sollte sich eines der ersten NeHe-Tuts (glaub es war das 3.) in einem JFrame zeigen.
Für eigene GLPanels muss man nur initGL und paintGL überschreiben, worin man dann wie gewohnt LWJGL (ohne die Display-Methoden!) programmieren kann. Man kann dank der pBufferMap auch mehrere GLPanels in verschiedenen Threads (obwohl… das hätte ich mir hier auch sparen können, zumal in Swing nur ein EDT läuft) laufen und gleichzeitig anzeigen (z.B. verschiedene Perspektiven) lassen. Der Nachteil des Ganzen ist, dass jeder einzelne Panel zunächst in einen ByteBuffer (LWJGL-Framebuffer) gezeichnet, welcher dann in ein BufferedImage kopiert und schließlich per drawImage angezeigt wird.

@SlaterB : Die Mouse ist von LWJGL http://www.lwjgl.org/javadoc/org/lwjgl/input/Mouse.html

Ansonsten ist die Vorgehensweise bei Swing nicht komplett anders: Von “ganz oben” kommt ein MouseEvent. Der wird in den Frame reingeworfen. Und man kann sich dann GANZ GROB vorstellen, dass der wie bei einem http://de.wikipedia.org/wiki/K-d-Baum rekursiv von Component zu Component weitergereicht wird. Im wirklich SEHR vereinfachten Pseudocode:

void processMouseEvent(MouseEvent e, Component c) 
{
    if (c.hasMouseListeners()) 
    { 
        c.sendToAllMouseListeners(e);
    }
    for (Component child : c.getChildren())
    {
        if (child.getBounds().contains(e.getPoint()) 
        {
            MouseEvent childEvent = convertTo(child, e);
            processMouseEvent(childEvent, child);
        }
    }
}

Die MouseEvents werden dabei immer ins “Ziel-Koordinatensystem” konvertiert.

Bei einem GL-Programm ist das natürlich etwas anders: Der Test mouseInObject könnte da etwas aufwändiger sein.

(Ganz nebenbei irritiert mich, dass anscheinend jedes AGLGBasicObject nur EINEN MouseListener haben kann. Das sollte doch eher eine Liste sein, mit add/remove !?)

Mir ist nicht ganz klar, was da am Ende rauskommen soll (und wie die übrige Struktur bisher ist), deswegen sind weiter gehende Tipps etwas schwierig…

[QUOTE=Marco13]@SlaterB : Die Mouse ist von LWJGL http://www.lwjgl.org/javadoc/org/lwjgl/input/Mouse.html
[/quote]
ändern sich bei jedem next() evtl. die Koordinaten? darauf wäre dann zu achten

(Ganz nebenbei irritiert mich, dass anscheinend jedes AGLGBasicObject nur EINEN MouseListener haben kann. Das sollte doch eher eine Liste sein, mit add/remove !?)

Component in Swing hat nebenbei auch nur ein MouseListener-Attribut, da geht der Weg über

        mouseListener = AWTEventMulticaster.add(mouseListener,l);```
wobei evtl. ein AWTEventMulticaster erzeugt wird, der dann zwei MouseListener bzw. eine ganze Kette intern enthält

Mit dem Teil der API von LWJGL bin ich nicht ganz so vertraut (weil ich bisher wenn es ging einen Bogen darum gemacht habe, weil es schon sehr suspekt aussieht. (“suspekt” ist hier nicht direkt wertend gemeint: Mit ist klar, dass das einfach nur sehr nah an der rudimentären API ist, die alle Fenstermanager so bieten)).

Dass die Listener-Verwaltung in Swing etwas … (suspekt? …neee, nicht schon wieder) … seltsam ist, weiß ich auch. Das mit dem Multicaster, und “Listener-Listen”, wo abwechselnd Class-Objekte und Listener drin sind und so… Das wird sicher auch alles seine Rechtfertigung haben (und sie es nur “Legacy”…). Aber dass hier nur this.mouseListener = listener; gemacht wurde, hast du sicher gesehen. Für “neue” Sachen ist

private final List<Listener> listeners = new CopyOnWriteArrayList<Listener>();

IMHO die geeignetste Wahl.

„1. Du könntest dir ein eigenes Eventsystem basteln, das hätte den Nachteil, dass du dir auch sämtliche GUI-Elemente (Buttons usw.) neu erschaffen darfst, die mit diesem System arbeiten.“

Naja… das hatte ich eigentlich vor. Oder auch ja, das will ich machen.

Mouse.next() holt sich nur ein event aus lwjgls event buffer.

Ich glaube ich verbessere es mal anhand eurer vorschlaege. Anschliessend kann ich ja mal die gamze lib posten. Da ist seeeeehr vieles seehr sehr haesslich, wenn jemand lust hat kann er ja mal grob drueber schauen. Bei vielen dingen hattenich auch einfach keine lust, es schoen zu machen… :smiley:

Ich hab mir jetzt noch einmal alles durch gelesen… und bin zu dem
schluss gekommen erst einmal nichts… zu ändern. ^^
die variablen existieren in Mouse, ja, aber das gehört lwjgl also will ichs selber haben.
In den Events soll es gespeichert sein weil ich ja bei verschiedenen events auch verschiedene
koordinaten habe… angenommen man speichert die irgendwo oder so… ka.

Jedenfalls. ich öffne mal einen neuen thread für die lib.

[quote=mymaksimus]die variablen existieren in Mouse, ja, aber das gehört lwjgl also will ichs selber haben.
In den Events soll es gespeichert sein weil ich ja bei verschiedenen events auch verschiedene[/quote]
die beiden Stellen habe ich ja auch am wenigsten kritisiert,
die Hauptfrage ist, ob die 4 Zahlen in AGLGMouseInputModule derart abgelegt werden müssen