2 * Copyright (c) 2008-2010, Matthias Mann
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Matthias Mann nor the names of its contributors may
15 * be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 import de.matthiasmann.twl.DesktopArea;
33 import de.matthiasmann.twl.DialogLayout;
34 import de.matthiasmann.twl.EditField;
35 import de.matthiasmann.twl.Event;
36 import de.matthiasmann.twl.FPSCounter;
37 import de.matthiasmann.twl.GUI;
38 import de.matthiasmann.twl.ResizableFrame;
39 import de.matthiasmann.twl.ScrollPane;
40 import de.matthiasmann.twl.TextArea;
41 import de.matthiasmann.twl.textarea.HTMLTextAreaModel;
42 import de.matthiasmann.twl.renderer.lwjgl.LWJGLRenderer;
43 import de.matthiasmann.twl.theme.ThemeManager;
45 import org.lwjgl.opengl.Display;
46 import org.lwjgl.opengl.DisplayMode;
47 import org.lwjgl.opengl.GL11;
48 import test.TestUtils;
53 * This class also acts as root pane
55 * @author Matthias Mann
57 public class ChatDemo extends DesktopArea {
59 public static void main(String[] args) {
61 Display.setDisplayMode(new DisplayMode(800, 600));
63 Display.setTitle("TWL Chat Demo");
64 Display.setVSyncEnabled(true);
66 LWJGLRenderer renderer = new LWJGLRenderer();
67 ChatDemo chat = new ChatDemo();
68 GUI gui = new GUI(chat, renderer);
70 ThemeManager theme = ThemeManager.createThemeManager(
71 ChatDemo.class.getResource("chat.xml"), renderer);
72 gui.applyTheme(theme);
74 while(!Display.isCloseRequested() && !chat.quit) {
75 GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
79 TestUtils.reduceInputLag();
84 } catch (Exception ex) {
85 TestUtils.showErrMsg(ex);
90 private final FPSCounter fpsCounter;
91 private final ChatFrame chatFrame;
96 fpsCounter = new FPSCounter();
99 chatFrame = new ChatFrame();
102 chatFrame.setSize(400, 200);
103 chatFrame.setPosition(10, 350);
107 protected void layout() {
110 // fpsCounter is bottom right
111 fpsCounter.adjustSize();
112 fpsCounter.setPosition(
113 getInnerWidth() - fpsCounter.getWidth(),
114 getInnerHeight() - fpsCounter.getHeight());
118 protected boolean handleEvent(Event evt) {
119 if(super.handleEvent(evt)) {
122 switch (evt.getType()) {
124 switch (evt.getKeyCode()) {
125 case Event.KEY_ESCAPE:
133 static class ChatFrame extends ResizableFrame {
134 private final StringBuilder sb;
135 private final HTMLTextAreaModel textAreaModel;
136 private final TextArea textArea;
137 private final EditField editField;
138 private final ScrollPane scrollPane;
139 private int curColor;
144 this.sb = new StringBuilder();
145 this.textAreaModel = new HTMLTextAreaModel();
146 this.textArea = new TextArea(textAreaModel);
147 this.editField = new EditField();
149 editField.addCallback(new EditField.Callback() {
150 public void callback(int key) {
151 if(key == Event.KEY_RETURN) {
152 // cycle through 3 different colors/font styles
153 appendRow("color"+curColor, editField.getText());
154 editField.setText("");
155 curColor = (curColor + 1) % 3;
160 textArea.addCallback(new TextArea.Callback() {
161 public void handleLinkClicked(String href) {
166 scrollPane = new ScrollPane(textArea);
167 scrollPane.setFixed(ScrollPane.Fixed.HORIZONTAL);
169 DialogLayout l = new DialogLayout();
170 l.setTheme("content");
171 l.setHorizontalGroup(l.createParallelGroup(scrollPane, editField));
172 l.setVerticalGroup(l.createSequentialGroup(scrollPane, editField));
176 appendRow("default", "Welcome to the chat demo. Type your messages below :)");
179 private void appendRow(String font, String text) {
180 sb.append("<div style=\"word-wrap: break-word; font-family: ").append(font).append("; \">");
181 // not efficient but simple
182 for(int i=0,l=text.length() ; i<l ; i++) {
183 char ch = text.charAt(i);
185 case '<': sb.append("<"); break;
186 case '>': sb.append(">"); break;
187 case '&': sb.append("&"); break;
188 case '"': sb.append("""); break;
190 if(text.startsWith(":)", i)) {
191 sb.append("<img src=\"smiley\" alt=\":)\"/>");
192 i += 1; // skip one less because of i++ in the for loop
198 if(text.startsWith("http://", i)) {
200 while(end < l && isURLChar(text.charAt(end))) {
203 String href = text.substring(i, end);
204 sb.append("<a style=\"font: link\" href=\"").append(href)
205 .append("\" >").append(href)
207 i = end - 1; // skip one less because of i++ in the for loop
217 boolean isAtEnd = scrollPane.getMaxScrollPosY() == scrollPane.getScrollPositionY();
219 textAreaModel.setHtml(sb.toString());
222 scrollPane.validateLayout();
223 scrollPane.setScrollPositionY(scrollPane.getMaxScrollPosY());
227 private boolean isURLChar(char ch) {
228 return (ch == '.') || (ch == '/') || (ch == '%') ||
229 (ch >= '0' && ch <= '9') ||
230 (ch >= 'a' && ch <= 'z') ||
231 (ch >= 'A' && ch <= 'Z');