From 5b4f3cede7270100734809758153ce96d1f523e4 Mon Sep 17 00:00:00 2001 From: austin Date: Mon, 2 May 2022 21:43:04 -0400 Subject: [PATCH] connecting to character create menu doesn't crash --- docs/NetworkFlow.drawio | 2 +- .../electrosphere/engine/LoadingThread.java | 31 +- .../creature/type/CreatureTypeLoader.java | 19 +- .../electrosphere/menu/MenuGenerators.java | 7 +- .../menu/MenuGeneratorsMultiplayer.java | 63 ++-- .../net/client/ClientNetworking.java | 29 +- .../net/client/protocol/AuthProtocol.java | 7 +- .../net/client/protocol/ClientProtocol.java | 4 + .../net/client/protocol/LoreProtocol.java | 32 ++ .../renderer/ui/ValueElement.java | 19 + .../renderer/ui/elements/Slider.java | 30 +- .../renderer/ui/elements/StringCarousel.java | 326 ++++++++++++++++++ .../renderer/ui/events/ValueChangeEvent.java | 36 ++ 13 files changed, 531 insertions(+), 74 deletions(-) create mode 100644 src/main/java/electrosphere/net/client/protocol/LoreProtocol.java create mode 100644 src/main/java/electrosphere/renderer/ui/ValueElement.java create mode 100644 src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java create mode 100644 src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java diff --git a/docs/NetworkFlow.drawio b/docs/NetworkFlow.drawio index aed00645..06959372 100644 --- a/docs/NetworkFlow.drawio +++ b/docs/NetworkFlow.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/src/main/java/electrosphere/engine/LoadingThread.java b/src/main/java/electrosphere/engine/LoadingThread.java index 1746ade6..1406ffd5 100644 --- a/src/main/java/electrosphere/engine/LoadingThread.java +++ b/src/main/java/electrosphere/engine/LoadingThread.java @@ -40,6 +40,7 @@ import electrosphere.game.simulation.MicroSimulation; import electrosphere.logger.LoggerInterface; import electrosphere.main.Globals; import electrosphere.menu.MenuGenerators; +import electrosphere.menu.MenuGeneratorsMultiplayer; import electrosphere.menu.WindowStrings; import electrosphere.menu.WindowUtils; import electrosphere.net.NetUtils; @@ -64,6 +65,7 @@ import electrosphere.game.server.unit.UnitUtils; import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.WidgetUtils; import electrosphere.renderer.ui.Window; +import electrosphere.util.Utilities; import java.util.LinkedList; import java.util.List; @@ -161,7 +163,7 @@ public class LoadingThread extends Thread { //set simulations to ready if they exist setSimulationsToReady(); //log - LoggerInterface.loggerEngine.INFO("[Server]Finished loading"); + LoggerInterface.loggerEngine.INFO("[Server]Finished loading main world"); break; @@ -196,7 +198,7 @@ public class LoadingThread extends Thread { creatingRandomEntities(); //set simulations to ready if they exist setSimulationsToReady(); - LoggerInterface.loggerEngine.INFO("[Server]Finished loading"); + LoggerInterface.loggerEngine.INFO("[Server]Finished loading arena world"); break; case LOAD_CHARACTER_SERVER: @@ -207,25 +209,26 @@ public class LoadingThread extends Thread { Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT); //initialize the client thread (client) initClientThread(); + //while we don't know what races are playable, wait + while(Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces().size() == 0){ + try { + TimeUnit.MILLISECONDS.sleep(5); + } catch (InterruptedException ex) {} + } + //once we have them, bring up the character creation interface //init character creation window - WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); + //eventually should replace with at ui to select an already created character or create a new one + WindowUtils.replaceMainMenuContents(MenuGeneratorsMultiplayer.createMultiplayerCharacterCreationWindow()); //make loading dialog disappear loadingWindow.setVisible(false); //make character creation window visible WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), true); //recapture window Globals.controlHandler.setShouldRecapture(true); - //set rendering flags - Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true; - Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = true; - Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = true; - Globals.RENDER_FLAG_RENDER_UI = true; - Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false; - Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; //log - LoggerInterface.loggerEngine.INFO("[Client]Finished loading"); - //set controls state - Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME); + LoggerInterface.loggerEngine.INFO("[Client]Finished loading character creation menu"); + //set menu controls again + Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.TITLE_MENU); break; case LOAD_CLIENT_WORLD: @@ -265,7 +268,7 @@ public class LoadingThread extends Thread { Globals.RENDER_FLAG_RENDER_UI = true; Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false; Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; - LoggerInterface.loggerEngine.INFO("[Client]Finished loading"); + LoggerInterface.loggerEngine.INFO("[Client]Finished loading main game"); //set controls state Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME); break; diff --git a/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java b/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java index 24e5cb3f..6a91255a 100644 --- a/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java +++ b/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.Semaphore; public class CreatureTypeLoader { @@ -11,6 +12,8 @@ public class CreatureTypeLoader { List playableRaceNames = new LinkedList(); + Semaphore playableRaceLock = new Semaphore(1); + public void putCreature(String name, CreatureType type){ creatureMap.put(name,type); } @@ -24,7 +27,21 @@ public class CreatureTypeLoader { } public List getPlayableRaces(){ - return playableRaceNames; + List races = null; + playableRaceLock.acquireUninterruptibly(); + races = playableRaceNames; + playableRaceLock.release(); + return races; + } + + public void clearPlayableRaces(){ + playableRaceNames.clear(); + } + + public void loadPlayableRaces(List races){ + playableRaceLock.acquireUninterruptibly(); + playableRaceNames = races; + playableRaceLock.release(); } diff --git a/src/main/java/electrosphere/menu/MenuGenerators.java b/src/main/java/electrosphere/menu/MenuGenerators.java index bf3286cd..b4828338 100644 --- a/src/main/java/electrosphere/menu/MenuGenerators.java +++ b/src/main/java/electrosphere/menu/MenuGenerators.java @@ -33,6 +33,7 @@ import electrosphere.renderer.ui.ClickableElement.ClickEventCallback; import electrosphere.renderer.ui.DraggableElement.DragEventCallback; import electrosphere.renderer.ui.FocusableElement.FocusEventCallback; import electrosphere.renderer.ui.NavigableElement.NavigationEventCallback; +import electrosphere.renderer.ui.ValueElement.ValueChangeEventCallback; import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.Div; import electrosphere.renderer.ui.elements.ImagePanel; @@ -44,6 +45,7 @@ import electrosphere.renderer.ui.events.ClickEvent; import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.FocusEvent; import electrosphere.renderer.ui.events.NavigationEvent; +import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.form.FormElement; import java.util.List; @@ -100,7 +102,7 @@ public class MenuGenerators { arenaButton.addChild(arenaLabel); rVal.addChild(arenaButton); arenaButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ - LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CLIENT_WORLD); + LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER); Globals.loadingThreadsList.add(clientThread); LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_ARENA); Globals.loadingThreadsList.add(serverThread); @@ -778,7 +780,8 @@ public class MenuGenerators { int posY = offset * 350 + 100; if(attribute.getType().equals("bone")){ Slider attributeSlider = new Slider(50,posY,400,100,new Vector3f(0.1f,0.1f,0.1f),new Vector3f(1.0f,0,0)); - attributeSlider.setOnValueChange(new Slider.OnSliderChangeCallback() {public void onChange(float value) { + attributeSlider.setOnValueChangeCallback(new ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { + float value = event.getAsFloat(); float minVal = attribute.getMinValue(); float range = attribute.getMaxValue() - minVal; float actualValue = minVal + range * value; diff --git a/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java b/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java index 8bc3ed87..ef48ef33 100644 --- a/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java +++ b/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java @@ -1,51 +1,52 @@ package electrosphere.menu; +import electrosphere.main.Globals; +import electrosphere.renderer.ui.ClickableElement; import electrosphere.renderer.ui.Element; +import electrosphere.renderer.ui.elements.Button; +import electrosphere.renderer.ui.elements.Label; +import electrosphere.renderer.ui.elements.StringCarousel; +import electrosphere.renderer.ui.events.ClickEvent; import electrosphere.renderer.ui.form.FormElement; public class MenuGeneratorsMultiplayer { + public static Element createMultiplayerCharacterSelectionWindow(){ + FormElement rVal = new FormElement(); + // int screenTop = Globals.WINDOW_HEIGHT - 150; + int verticalPosition = 125; + + //button (create) + Button createButton = new Button(); + Label createLabel = new Label(100,125 + verticalPosition + 200,1.0f); + createLabel.setText("Create Character"); + createButton.addChild(createLabel); + rVal.addChild(createButton); + createButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldCreationMenu()); + return false; + }}); + + return rVal; + } + public static Element createMultiplayerCharacterCreationWindow(){ FormElement rVal = new FormElement(); // int screenTop = Globals.WINDOW_HEIGHT - 150; - List saveNames = SaveUtils.getSaves(); int verticalPosition = 125; - for(String saveName : saveNames){ - if(!saveName.startsWith(".")){ - //button (select save) - Button selectButton = new Button(); - Label selectLabel = new Label(100,125 + verticalPosition,1.0f); - selectLabel.setText(saveName.toUpperCase()); - selectButton.addChild(selectLabel); - rVal.addChild(selectButton); - selectButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ - if(SaveUtils.worldHasSave(saveName.toLowerCase())){ - LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER); - Globals.loadingThreadsList.add(clientThread); - LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_MAIN_GAME); - Globals.loadingThreadsList.add(serverThread); - Globals.RUN_CLIENT = true; - Globals.RUN_SERVER = true; - clientThread.start(); - serverThread.start(); - } else { - Globals.currentSaveName = saveName.toLowerCase(); - SaveUtils.loadTerrainAndCreateWorldData(); - WindowUtils.replaceMainMenuContents(MenuGenerators.createSaveCreationMenu()); - } - return false; - }}); - - verticalPosition = verticalPosition + 75; - } + //select race + StringCarousel raceCarousel = new StringCarousel(100, 125, 1.0f); + for(String raceName : Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces()){ + raceCarousel.addOption(raceName); } + rVal.addChild(raceCarousel); //button (create) Button createButton = new Button(); - Label createLabel = new Label(100,125 + verticalPosition + 200,1.0f); - createLabel.setText("Create World"); + Label createLabel = new Label(100,275,1.0f); + createLabel.setText("Create Character"); createButton.addChild(createLabel); rVal.addChild(createButton); createButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ diff --git a/src/main/java/electrosphere/net/client/ClientNetworking.java b/src/main/java/electrosphere/net/client/ClientNetworking.java index 02901401..69aac6aa 100644 --- a/src/main/java/electrosphere/net/client/ClientNetworking.java +++ b/src/main/java/electrosphere/net/client/ClientNetworking.java @@ -22,6 +22,7 @@ import java.util.Properties; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.spec.SecretKeySpec; @@ -44,14 +45,30 @@ public class ClientNetworking implements Runnable{ NetworkParser parser; ClientProtocol clientProtocol = new ClientProtocol(); + + static final int MAX_CONNECTION_ATTEMPTS = 10; public ClientNetworking(String address, int port){ - try { - this.socket = new Socket(address,port); - } catch (IOException ex) { - System.err.println("Failed to connect!"); - ex.printStackTrace(); - System.exit(1); + int connectionAttempts = 0; + boolean connected = false; + while(!connected){ + try { + this.socket = new Socket(address,port); + } catch (IOException ex) { + LoggerInterface.loggerNetworking.WARNING("Client failed to connect!"); + } finally { + connected = true; + } + if(!connected){ + try { + TimeUnit.MILLISECONDS.sleep(50); + } catch (InterruptedException e) {} + connectionAttempts++; + } + if(connectionAttempts > MAX_CONNECTION_ATTEMPTS){ + LoggerInterface.loggerNetworking.ERROR("Max client connection attempts!", new Exception()); + System.exit(1); + } } } diff --git a/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java b/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java index 4accaddb..38bff5e5 100644 --- a/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java @@ -4,6 +4,7 @@ import electrosphere.logger.LoggerInterface; import electrosphere.main.Globals; import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.CharacterMessage; +import electrosphere.net.parser.net.message.LoreMessage; import electrosphere.net.parser.net.message.TerrainMessage; public class AuthProtocol { @@ -17,8 +18,10 @@ public class AuthProtocol { break; case AUTHSUCCESS: //trigger request to spawn character - Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage()); - Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage()); + // Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage()); + // Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage()); + //request playable races + Globals.clientConnection.queueOutgoingMessage(LoreMessage.constructRequestRacesMessage()); break; case AUTHFAILURE: //TODO: handle better diff --git a/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java b/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java index 283a1284..f8cb0524 100644 --- a/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java @@ -11,6 +11,7 @@ import electrosphere.main.Globals; import electrosphere.main.Main; import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.EntityMessage; +import electrosphere.net.parser.net.message.LoreMessage; import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.PlayerMessage; import electrosphere.net.parser.net.message.ServerMessage; @@ -41,6 +42,9 @@ public class ClientProtocol { case SERVER_MESSAGE: ServerProtocol.handleServerMessage((ServerMessage)message); break; + case LORE_MESSAGE: + LoreProtocol.handleLoreMessage((LoreMessage)message); + break; } } diff --git a/src/main/java/electrosphere/net/client/protocol/LoreProtocol.java b/src/main/java/electrosphere/net/client/protocol/LoreProtocol.java new file mode 100644 index 00000000..8f8f2ad0 --- /dev/null +++ b/src/main/java/electrosphere/net/client/protocol/LoreProtocol.java @@ -0,0 +1,32 @@ +package electrosphere.net.client.protocol; + +import java.util.List; + +import com.google.gson.Gson; + +import electrosphere.main.Globals; +import electrosphere.net.parser.net.message.LoreMessage; + +public class LoreProtocol { + + protected static void handleLoreMessage(LoreMessage message){ + switch(message.getMessageSubtype()){ + case RESPONSEDATA: + break; + case RESPONSERACES: + //we get back the race list as a json array, deserialize, and push into type loader + List playableRaces = new Gson().fromJson(message.getdata(), List.class); + Globals.gameConfigCurrent.getCreatureTypeLoader().loadPlayableRaces(playableRaces); + break; + case RESPONSERACEDATA: + break; + + case REQUESTDATA: + case REQUESTRACEDATA: + case REQUESTRACES: + //silently ignore + break; + } + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/ValueElement.java b/src/main/java/electrosphere/renderer/ui/ValueElement.java new file mode 100644 index 00000000..91cb84a3 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/ValueElement.java @@ -0,0 +1,19 @@ +package electrosphere.renderer.ui; + +import electrosphere.renderer.ui.events.ValueChangeEvent; + +/** + * Describes an element that contains a changeable value which will be used by the program somewhere + * IE a carousel, text input, radio dial, etc + */ +public interface ValueElement extends Element { + + public void setOnValueChangeCallback(ValueChangeEventCallback callback); + + public interface ValueChangeEventCallback { + + public void execute(ValueChangeEvent event); + + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/elements/Slider.java b/src/main/java/electrosphere/renderer/ui/elements/Slider.java index 5f740071..9badcfcd 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Slider.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Slider.java @@ -11,6 +11,7 @@ import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.FocusableElement; import electrosphere.renderer.ui.KeyEventElement; import electrosphere.renderer.ui.MenuEventElement; +import electrosphere.renderer.ui.ValueElement; import electrosphere.renderer.ui.events.ClickEvent; import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.Event; @@ -18,9 +19,10 @@ import electrosphere.renderer.ui.events.FocusEvent; import electrosphere.renderer.ui.events.KeyboardEvent; import electrosphere.renderer.ui.events.MenuEvent; import electrosphere.renderer.ui.events.MouseEvent; +import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.events.DragEvent.DragEventType; -public class Slider implements ClickableElement, DraggableElement, FocusableElement, DrawableElement, MenuEventElement { +public class Slider implements ClickableElement, DraggableElement, FocusableElement, DrawableElement, MenuEventElement, ValueElement { public int width = 1; public int height = 1; @@ -41,7 +43,7 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem DragEventCallback onDragRelease; ClickEventCallback onClick; MenuEventCallback onMenuEvent; - OnSliderChangeCallback onChangeValue; + ValueChangeEventCallback onValueChange; float min = 0.0f; float max = 1.0f; @@ -238,8 +240,9 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem onMenuEvent = callback; } - public void setOnValueChange(OnSliderChangeCallback callback){ - this.onChangeValue = callback; + @Override + public void setOnValueChangeCallback(ValueChangeEventCallback callback) { + onValueChange = callback; } @@ -272,15 +275,15 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem switch(menuEvent.getType()){ case INCREMENT: value = Math.min(value + ((max - min) * 0.01f),max); - if(onChangeValue != null){ - onChangeValue.onChange(value); + if(onValueChange != null){ + onValueChange.execute(new ValueChangeEvent(value)); } propagate = false; break; case DECREMENT: value = Math.max(value - ((max - min) * 0.01f),min); - if(onChangeValue != null){ - onChangeValue.onChange(value); + if(onValueChange != null){ + onValueChange.execute(new ValueChangeEvent(value)); } propagate = false; break; @@ -320,8 +323,8 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem int percentage = clickEvent.getCurrentX() - positionX; int max = width; value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); - if(onChangeValue != null){ - onChangeValue.onChange(value); + if(onValueChange != null){ + onValueChange.execute(new ValueChangeEvent(value)); } propagate = false; } @@ -329,12 +332,5 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem } return propagate; } - - - public interface OnSliderChangeCallback { - - public void onChange(float value); - - } } diff --git a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java new file mode 100644 index 00000000..1b837e0e --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java @@ -0,0 +1,326 @@ +package electrosphere.renderer.ui.elements; + +import java.util.LinkedList; +import java.util.List; + +import org.joml.Vector3f; + +import electrosphere.renderer.ui.DrawableElement; +import electrosphere.renderer.ui.Element; +import electrosphere.renderer.ui.FocusableElement; +import electrosphere.renderer.ui.MenuEventElement; +import electrosphere.renderer.ui.ValueElement; +import electrosphere.renderer.ui.events.Event; +import electrosphere.renderer.ui.events.FocusEvent; +import electrosphere.renderer.ui.events.MenuEvent; +import electrosphere.renderer.ui.events.ValueChangeEvent; +import electrosphere.renderer.ui.events.MenuEvent.MenuEventType; +import electrosphere.renderer.ui.font.FontUtils; +import electrosphere.renderer.ui.font.bitmapchar.BitmapCharacter; + +public class StringCarousel implements DrawableElement, MenuEventElement, FocusableElement, ValueElement { + + public int width = 1; + public int height = 1; + + public int positionX = 0; + public int positionY = 0; + + public int parentWidth = 1; + public int parentHeight = 1; + + public boolean visible = false; + + MenuEventCallback onMenuEventCallback; + ValueChangeEventCallback onValueChange; + + boolean focused = false; + FocusEventCallback onFocusCallback; + FocusEventCallback onLoseFocusCallback; + + List options = new LinkedList(); + int currentOption = -1; + String textCurrent = ""; + int textPixelWidth = 0; + + float fontSize = 1.0f; + + List childrenElements = new LinkedList(); + + public StringCarousel(int x, int y, float fontSize){ + this.positionX = x; + this.positionY = y; + this.width = 0; + this.height = (int)(FontUtils.getFontHeight() * fontSize); + this.fontSize = fontSize; + } + + public void addOption(String option){ + options.add(option); + if(currentOption == -1){ + currentOption = 0; + setText(option); + if(onValueChange != null){ + onValueChange.execute(new ValueChangeEvent(option)); + } + } + } + + public List getOptions(){ + return options; + } + + public void removeOption(String option){ + options.remove(option); + if(currentOption > options.size() - 1){ + currentOption = options.size() - 1; + } + } + + void generateLetters(){ + childrenElements.clear(); + int rollingOffset = 0; + for(int i = 0; i < textCurrent.length(); i++){ + char toDraw = textCurrent.charAt(i); + Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(toDraw); + BitmapCharacter newLetter = new BitmapCharacter((int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw); + rollingOffset += (int)bitMapDimension.x; + childrenElements.add(newLetter); + } + } + + public void setText(String text){ + this.textCurrent = text; + textPixelWidth = 0; + for(int i = 0; i < text.length(); i++){ + Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(text.charAt(i)); + textPixelWidth = textPixelWidth + (int)bitMapDimension.x; + } + generateLetters(); + if(focused){ + setColor(new Vector3f(1,0,0)); + } + } + + public void setColor(Vector3f color){ + for(BitmapCharacter character : childrenElements){ + character.setColor(color); + } + } + + public String getText(){ + return textCurrent; + } + + @Override + public void draw(int parentFramebufferPointer, int parentWidth, int parentHeight) { + for(DrawableElement child : childrenElements){ + child.draw(parentFramebufferPointer, parentWidth, parentHeight); + } + } + + public int getWidth() { + int minX = -1; + int maxX = -1; + for(BitmapCharacter child : childrenElements){ + if(minX == -1){ + minX = child.getPositionX(); + } else if(child.getPositionX() < minX){ + minX = child.getPositionX(); + } + if(maxX == -1){ + maxX = child.getPositionX() + child.getWidth(); + } else if(child.getPositionX() + child.getWidth() > maxX){ + maxX = child.getPositionX() + child.getWidth(); + } + } + if(minX == -1){ + minX = 0; + } + if(maxX == -1){ + maxX = 0; + } + return maxX - minX; + } + + public int getHeight() { + int minY = -1; + int maxY = -1; + for(BitmapCharacter child : childrenElements){ + if(minY == -1){ + minY = child.getPositionY(); + } else if(child.getPositionY() < minY){ + minY = child.getPositionY(); + } + if(maxY == -1){ + maxY = child.getPositionY() + child.getHeight(); + } else if(child.getPositionY() + child.getHeight() > maxY){ + maxY = child.getPositionY() + child.getHeight(); + } + } + if(minY == -1){ + minY = 0; + } + if(maxY == -1){ + maxY = 0; + } + return maxY - minY; + } + + public int getPositionX() { + int minX = -1; + for(BitmapCharacter child : childrenElements){ + if(minX == -1){ + minX = child.getPositionX(); + } else if(child.getPositionX() < minX){ + minX = child.getPositionX(); + } + } + if(minX == -1){ + minX = 0; + } + return minX; + } + + public int getPositionY() { + int minY = -1; + for(BitmapCharacter child : childrenElements){ + if(minY == -1){ + minY = child.getPositionY(); + } else if(child.getPositionY() < minY){ + minY = child.getPositionY(); + } + } + if(minY == -1){ + minY = 0; + } + return minY; + } + + public boolean getVisible() { + return visible; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setPositionX(int posX) { + int deltaX = posX - this.positionX; + this.positionX = posX; + for(Element child : childrenElements){ + child.setPositionX(child.getPositionX() + deltaX); + } + } + + public void setPositionY(int posY) { + int deltaY = posY - this.positionY; + this.positionY = posY; + for(Element child : childrenElements){ + child.setPositionY(child.getPositionY() + deltaY); + } + } + + public void setVisible(boolean draw) { + this.visible = draw; + } + + public void setParentWidth(int width){ + parentWidth = width; + } + + public void setParentHeight(int height){ + this.parentHeight = height; + } + + public boolean handleEvent(Event event){ + boolean propagate = true; + if(event instanceof MenuEvent){ + MenuEvent menuEvent = (MenuEvent)event; + if(onMenuEventCallback != null){ + propagate = onMenuEventCallback.execute(menuEvent); + } else { + //default behavior + if(menuEvent.getType() == MenuEventType.INCREMENT){ + propagate = false; + if(options.size() > 0){ + currentOption++; + if(currentOption > options.size() - 1){ + currentOption = 0; + } + String newOption = options.get(currentOption); + setText(newOption); + if(onValueChange != null){ + onValueChange.execute(new ValueChangeEvent(newOption)); + } + } + } else if(menuEvent.getType() == MenuEventType.DECREMENT){ + propagate = false; + if(options.size() > 0){ + currentOption--; + if(currentOption < 0){ + currentOption = options.size() - 1; + } + String newOption = options.get(currentOption); + setText(newOption); + if(onValueChange != null){ + onValueChange.execute(new ValueChangeEvent(newOption)); + } + } + } + } + } else if(event instanceof FocusEvent){ + FocusEvent focusEvent = (FocusEvent) event; + if(focusEvent.isFocused()){ + this.focused = true; + if(onFocusCallback != null){ + propagate = onFocusCallback.execute(focusEvent); + } else { + //default behavior + propagate = false; + setColor(new Vector3f(1,0,0)); + } + } else { + this.focused = false; + if(onLoseFocusCallback != null){ + propagate = onLoseFocusCallback.execute(focusEvent); + } else { + //default behavior + propagate = false; + setColor(new Vector3f(1,1,1)); + } + } + } + return propagate; + } + + @Override + public void setOnMenuEventCallback(MenuEventCallback callback) { + onMenuEventCallback = callback; + } + + @Override + public void setOnValueChangeCallback(ValueChangeEventCallback callback) { + onValueChange = callback; + } + + @Override + public boolean isFocused() { + return focused; + } + + @Override + public void setOnFocus(FocusEventCallback callback) { + onFocusCallback = callback; + } + + @Override + public void setOnLoseFocus(FocusEventCallback callback) { + onLoseFocusCallback = callback; + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java b/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java new file mode 100644 index 00000000..85580588 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java @@ -0,0 +1,36 @@ +package electrosphere.renderer.ui.events; + +public class ValueChangeEvent { + + public static enum ValueType { + STRING, + FLOAT, + } + + String valueString; + float valueFloat; + ValueType valueType; + + public ValueChangeEvent(String value){ + valueString = value; + valueType = ValueType.STRING; + } + + public ValueChangeEvent(float value){ + valueFloat = value; + valueType = ValueType.FLOAT; + } + + public ValueType getType(){ + return valueType; + } + + public float getAsFloat(){ + return valueFloat; + } + + public String getAsString(){ + return valueString; + } + +}