Added project files

This commit is contained in:
Zak Yani Star Fenton 2025-06-11 01:00:06 +10:00
commit c4106a0ce5
43 changed files with 3947 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

73
build.xml Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="slgui" default="default" basedir=".">
<description>Builds, tests, and runs the project slgui.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="slgui-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

3
manifest.mf Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

1771
nbproject/build-impl.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
build.xml.data.CRC32=46d6c371
build.xml.script.CRC32=b385f2e2
build.xml.stylesheet.CRC32=f85dc8f2@1.112.0.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=46d6c371
nbproject/build-impl.xml.script.CRC32=9f3656df
nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.112.0.48

View File

@ -0,0 +1,95 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.modulepath=\
${run.modulepath}
debug.test.classpath=\
${run.test.classpath}
debug.test.modulepath=\
${run.test.modulepath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/slgui.jar
dist.javadoc.dir=${dist.dir}/javadoc
dist.jlink.dir=${dist.dir}/jlink
dist.jlink.output=${dist.jlink.dir}/slgui
excludes=
includes=**
jar.compress=false
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.external.vm=true
javac.modulepath=
javac.processormodulepath=
javac.processorpath=\
${javac.classpath}
javac.source=21
javac.target=21
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.modulepath=\
${javac.modulepath}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.html5=false
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
# The jlink additional root modules to resolve
jlink.additionalmodules=
# The jlink additional command line parameters
jlink.additionalparam=
jlink.launcher=true
jlink.launcher.name=slgui
main.class=slgui.demo.Main
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.modulepath=\
${javac.modulepath}
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
run.test.modulepath=\
${javac.test.modulepath}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

15
nbproject/project.xml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>slgui</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@ -0,0 +1,20 @@
package slgui.base;
/** This should be subclassed by applications which use the media API.
*
* @author Zak Fenton
*
*/
public abstract class MediaApp {
protected final MediaAppId appId;
protected final MediaAppDelegate appDelegate;
public MediaApp(MediaSystemDelegate systemDelegate, MediaAppId appId) {
this.appId = appId;
appDelegate = systemDelegate.pairApp(this);
}
public MediaAppId getId() {
return appId;
}
}

View File

@ -0,0 +1,10 @@
package slgui.base;
/** Represents an object which can be used by an application to interact with the world.
*
* @author Zak Fenton
*
*/
public interface MediaAppDelegate {
public MediaSystemDelegate getSystemDelegate();
}

View File

@ -0,0 +1,44 @@
package slgui.base;
/** This should uniquely identify an installed/installable version of an application on the system,
* but without any installation-specific details such as file paths. This can be used for checking versions,
* loading default configurations, for generating default installation paths, (in the future) sending messages
* between apps, and so on. For the most part though, it's just used internally wherever the app name is
* needed. It also helps to ensure that app release ids are reasonably consistent
*
* @author Zak Fenton
*
*/
public final class MediaAppId {
public final String marketId;
public final String developerId;
public final String releaseId;
public final int majorVersion;
public final int minorVersion;
public final String patchId;
public MediaAppId(String marketId, String developerId, String releaseId, int majorVersion, int minorVersion, String patchId) {
this.marketId = checkString(marketId);
this.developerId = checkString(developerId);
this.releaseId = checkString(releaseId);
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.patchId = checkString(patchId);
}
public boolean stringFails(String str) {
if (str.length() < 1) {
return true;
} else {
return str.contains("/") || str.contains("\\") || str.contains("-") || str.contains("*") || str.contains(".");
}
}
public String checkString(String str) {
if (stringFails(str)) {
throw new Error("The string '" + str + "' contains characters forbidden in app ids");
}
return str;
}
}

View File

@ -0,0 +1,23 @@
package slgui.base;
public final class MediaBox {
public final double x;
public final double y;
public final double width;
public final double height;
public MediaBox(double x, double y, double width, double height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public boolean hits(double x, double y) {
return (x >= this.x) && (y >= this.y) && (x <= this.x + this.width) && (y <= this.y + this.height);
}
public boolean hits(MediaPoint p) {
return hits(p.x, p.y);
}
}

View File

@ -0,0 +1,356 @@
package slgui.base;
/** A minimalist font system which can be used directly on top of a MediaPainter
* (or subclass).
*
* @author Zak Fenton
*
*/
public class MediaFont {
public /*final*/ double scale;
public /*final*/ double rscale;
public MediaFont(double scale) {
this.scale = scale;
this.rscale = scale/2;
}
public double glyphWidth(int value) {
switch (value) {
default:
return 8*scale;
}
}
public double glyphXAdvance(int value) {
return glyphWidth(value);
}
public void renderGlyph(MediaPainter p, int value, double x, double y, int pixel) {
if (value >= 'A' && value <= 'Z') {
renderAZ(p, value, x, y, pixel);
} else if (value >= 'a' && value <= 'z') {
renderaz(p, value, x, y, pixel);
} else if (value >= '0' && value <= '9') {
render09(p, value, x, y, pixel);
} else if (value == ' ' || value == '\t' || value == '\n') {
// Empty
} else if (isPunct(value)) {
renderPunct(p, value, x, y, pixel);
} else {
renderTofu(p, value, x, y, pixel);
}
}
public boolean isPunct(int value) {
switch (value) {
case '.':
case '!':
case '?':
case ',':
case ':':
case '\'':
case '\"':
case '/':
case '\\':
return true;
default:
return false;
}
}
public void render(MediaPainter p, String value, double x, double y, int pixel) {
int[] codepoints = value.codePoints().toArray();
for (int i = 0; i < codepoints.length; i++) {
int c = codepoints[i];
renderGlyph(p, c, x, y, pixel);
x += glyphXAdvance(c);
}
}
public void renderTofu(MediaPainter p, int value, double x, double y, int pixel) {
p.line(x, y, x + 7*scale, y, pixel, 1*rscale);
p.line(x, y + 7*scale, x + 7*scale, y + 7*scale, pixel, 1*rscale);
p.line(x, y, x, y + 7*scale, pixel, 1*rscale);
p.line(x + 7*scale, y, x + 7*scale, y + 7*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, 0xFFFF0000, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, 0xFFFF0000, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, 0xFFFF0000, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, 0xFFFF0000, 1*rscale);
}
public void renderPunct(MediaPainter p, int value, double x, double y, int pixel) {
switch(value) {
case '!':
p.line(x+3.5*scale, y + 1*scale, x + 3.5*scale, y + 4*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 6*scale, x + 3.5*scale, y + 6*scale, pixel, 1.5*rscale);
break;
case '?':
p.line(x+3.5*scale, y + 1*scale, x + 5*scale, y + 2.5*scale, pixel, 1*rscale);
p.line(x+5*scale, y + 2.5*scale, x + 3.5*scale, y + 4*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 6*scale, x + 3.5*scale, y + 6*scale, pixel, 1.5*rscale);
break;
case '.':
p.line(x+3.5*scale, y + 6*scale, x + 3.5*scale, y + 6*scale, pixel, 1.5*rscale);
break;
case ':':
p.line(x+3.5*scale, y + 3.5*scale, x + 3.5*scale, y + 3.5*scale, pixel, 1.5*rscale);
p.line(x+3.5*scale, y + 6*scale, x + 3.5*scale, y + 6*scale, pixel, 1.5*rscale);
break;
case ',':
p.line(x+3*scale, y + 6*scale, x + 4*scale, y + 5*scale, pixel, 1.5*rscale);
break;
case '/':
p.line(x+1*scale, y+6*scale, x+6*scale, y+1*scale, pixel, 1*rscale);
break;
case '\\':
p.line(x+6*scale, y+6*scale, x+1*scale, y+1*scale, pixel, 1*rscale);
break;
default:
renderTofu(p, value, x, y, pixel);
}
}
public void render09(MediaPainter p, int value, double x, double y, int pixel) {
switch(value) {
case '0':
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case '1':
p.line(x+3.5*scale, y + 1*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 1*scale, x + 2.25*scale, y + 1.75*scale, pixel, 1*rscale);
break;
case '2':
p.line(x + 2*scale, y + 1*scale, x + 5*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 2.25*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x + 2*scale, y + 1*scale, x + 1*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x + 5*scale, y + 1*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 3.5*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case '3':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case '4':
p.line(x+1*scale, y + 4.25*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 4.25*scale, x + 6*scale, y + 4.25*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case '5':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 2.25*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 2.25*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case '6':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 1*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case '7':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 1*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
break;
case '8':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 1*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 1*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case '9':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
//p.line(x+1*scale, y + 6*scale, x + 1*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 1*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
default:
renderTofu(p, value, x, y, pixel);
}
}
public void renderAZ(MediaPainter p, int value, double x, double y, int pixel) {
switch(value) {
case 'A':
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y+1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 3.5*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'B':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 4.75*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 4.75*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'C':
//p.line(x+6*scale, y + 1*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
//p.line(x+6*scale, y + 6*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'D':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'E':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'F':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'G':
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 3.5*scale, x + 3.5*scale, y + 3.5*scale, pixel, 1*rscale);
break;
case 'H':
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y+1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'I':
p.line(x+3.5*scale, y + 1*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'J':
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 3.5*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'K':
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 1*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 6*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
break;
case 'L':
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'M':
p.line(x+1*scale, y + 1*scale, x + 3.5*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 3.5*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y+1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'N':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y+1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'O':
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'P':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
//p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'Q':
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 3.5*scale, x + 7*scale, y + 7*scale, pixel, 1*rscale);
break;
case 'R':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 2.25*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'S':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 3.5*scale, x + 6*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 1*scale, x + 1*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+6*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'T':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 1*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'U':
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'V':
p.line(x + 1*scale, y + 1*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'W':
p.line(x+1*scale, y + 6*scale, x + 3.5*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 3.5*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y+1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'X':
p.line(x+1*scale, y + 1*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x+1*scale, y + 6*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
break;
case 'Y':
p.line(x+1*scale, y + 1*scale, x + 3.5*scale, y + 3.5*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y + 3.5*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x+3.5*scale, y+3.5*scale, x + 3.5*scale, y + 6*scale, pixel, 1*rscale);
break;
case 'Z':
p.line(x + 1*scale, y + 1*scale, x + 6*scale, y + 1*scale, pixel, 1*rscale);
p.line(x + 1*scale, y + 6*scale, x + 6*scale, y + 6*scale, pixel, 1*rscale);
p.line(x + 6*scale, y + 1*scale, x + 1*scale, y + 6*scale, pixel, 1*rscale);
break;
default:
renderTofu(p, value, x, y, pixel);
}
}
public void renderaz(MediaPainter p, int value, double x, double y, int pixel) {
double oldscale = scale;
double oldrscale = rscale;
switch(value) {
default:
try {
x += 1*scale;
y += 1*scale;
scale *= 0.8;
rscale *= 0.8;
//p.line(x, y, x + 7*scale, y, 0xFFFF0000, 1*scale);
renderAZ(p, ((int)'A') + (value - ((int) 'a')), x, y, pixel);
} finally {
scale = oldscale;
rscale = oldrscale;
}
}
}
}

View File

@ -0,0 +1,86 @@
package slgui.base;
import java.util.HashMap;
public class MediaInputState {
private HashMap<String, MediaPointerState> pointerstates = new HashMap<String, MediaPointerState>();
private HashMap<String, Boolean> keystates = new HashMap<String, Boolean>();
double lastX, lastY;
public MediaInputState() {
// TODO Auto-generated constructor stub
}
public synchronized void resetKeypad() {
keystates = new HashMap<String, Boolean>();
}
public synchronized void resetPointers() {
pointerstates = new HashMap<String, MediaPointerState>();
}
public synchronized MediaPointerState setPointer(String id, double x, double y, int nbuttons, MediaPointerButton... buttons) {
if (nbuttons < 0) {
pointerstates.remove(id);
return null;
}
if (!pointerstates.containsKey(id)) {
pointerstates.put(id, new MediaPointerState());
}
MediaPointerState s = pointerstates.get(id);
s.reset(id);
s.setCoordinates(x, y);
lastX = x;
lastY = y;
s.setButtons(nbuttons, 0);
for (int i = 0; i < buttons.length; i++) {
s.setButton(buttons[i], true);
}
return s;
}
public boolean hoverWithin(double x, double y, double width, double height) {
MediaPointerState[] p = currentPointers();
for (int i = 0; i < p.length; i++) {
if (p[i].hoverWithin(x,y,width,height)) {
return true;
}
}
return false;
}
public boolean downWithin(MediaPointerButton b, double x, double y, double width, double height) {
MediaPointerState[] p = currentPointers();
for (int i = 0; i < p.length; i++) {
if (p[i].hoverWithin(x,y,width,height) && p[i].buttonDown(b)) {
return true;
}
}
return false;
}
public synchronized MediaPointerState[] currentPointers() {
return pointerstates.values().toArray(new MediaPointerState[0]);
}
public synchronized void setKeypad(String value, boolean down) {
keystates.put(value, down);
}
public synchronized boolean keypadDown(String value) {
if (keystates.containsKey(value)) {
return keystates.get(value);
} else {
return false;
}
}
public double lastPointerX() {
return lastX;
}
public double lastPointerY() {
return lastY;
}
}

View File

@ -0,0 +1,77 @@
package slgui.base;
import java.util.Date;
import slgui.themes.KeypadType;
import slgui.themes.Theme;
public class MediaModel {
private int width;
private int height;
MediaFont font = new MediaFont(2);
private MediaInputState input = new MediaInputState();
public MediaModel() {
// TODO Auto-generated constructor stub
}
public void assertScreenSize(int w, int h) {
if (getWidth() != w || getHeight() != h) {
width = w;
height = h;
screenSizeChanged();
}
}
public void setInputState(MediaInputState input) {
this.input = input;
}
public MediaInputState getInputState() {
return input;
}
protected void screenSizeChanged() {
//System.err.println("NOTE: Screen size changed to " + width + "x" + height);
}
public void attachView(MediaViewDelegate v) {
}
public void detachView(MediaViewDelegate v) {
}
public void paintView(MediaViewDelegate v, MediaPainter p) {
/*
p.line(10, 100, getWidth()-10, getHeight()-100, 0xFFFF0000, 10.0);
p.line(getWidth()-100, 10, 100, getHeight()-10, 0xFFFF0000, 1.0);
font.render(p, "Hello, world!", 100, 200, 0xFF50C0FF);
new MediaFont(1).render(p, "Hello, world!", 100, 225, 0xFF50C0FF);
new MediaFont(4).render(p, "Hello, world! 0123456789", 100, 250, 0xFF50C0FF);
new MediaFont(4).render(p, "Hello... world?", 100, 300, 0xFF50C0FF);
new MediaFont(2).render(p, "Sphinx of black quartz, judge my vow.", 100, 350, 0xFF50C0FF);
new MediaFont(1).render(p, "Sphinx of black quartz, judge my vow. 0123456789", 100, 400, 0xFF50C0FF);
Theme t = new Theme();
t.paintDemoWindow(p, "A window", 300, 25, 250, 150);
t.paintDemoDesktop(p, "Demo", new Date().toLocaleString(), 0,0, getWidth(), getHeight(), KeypadType.LATIN_LOWER, Math.sin((System.currentTimeMillis()%3000)/1000.0));
*/
}
public boolean isKeyed() {
return true;
}
public boolean isAnimated() {
return true;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}

View File

@ -0,0 +1,21 @@
package slgui.base;
/* A MediaPainter object represents a minimalist rendering target, i.e. something
* which in theory could be implemented on (robotic) pen & paper, but which should
* be equally convenient for on-screen use. The design focuses on pen-like operations
* for this reason, and does not in itself provide a heap of convenience functions
*/
public abstract class MediaPainter {
/** Returns the given x value, except "snapped" to the centre of the nearest pixel (if applicable). */
public double snapX(double x) {
return x;
}
/** Returns the given y value, except "snapped" to the centre of the nearest pixel (if applicable). */
public double snapY(double y) {
return y;
}
/** Draws a line with the given (ARGB32) pixel value and radius. */
public abstract void line(double x1, double y1, double x2, double y2, int pixel, double radius);
/** Fills a simple box with the given (ARGB32) pixel value. */
public abstract void box(double x, double y, double width, double height, int pixel);
}

View File

@ -0,0 +1,11 @@
package slgui.base;
public final class MediaPoint {
public final double x;
public final double y;
public MediaPoint(double x, double y) {
this.x = x;
this.y = y;
}
}

View File

@ -0,0 +1,10 @@
package slgui.base;
public enum MediaPointerButton {
/** This would usually correspond to the left mouse button, or a regular touch event. */
MAIN,
/** This would usually correspond to the right mouse button (if present). */
ALT,
/** This would usually correspond to the middle mouse button, or scrollwheel being pressed (if either are present). */
MIDDLE
}

View File

@ -0,0 +1,73 @@
package slgui.base;
public class MediaPointerState {
String pointerId;
double x, y;
int buttonFlags;
int nbuttons;
public MediaPointerState() {
reset(null);
}
public synchronized void reset(String pointerId) {
this.pointerId = pointerId;
x = -1;
y = -1;
nbuttons = -1;
buttonFlags = 0;
}
public synchronized void setCoordinates(double x, double y) {
this.x = x;
this.y = y;
}
public synchronized MediaPoint getCoordinates() {
return new MediaPoint(x, y);
}
public synchronized void setButtons(int numberOfButtons, int buttonFlags) {
this.nbuttons = numberOfButtons;
this.buttonFlags = buttonFlags;
}
public synchronized boolean buttonDown(MediaPointerButton b) {
return buttonExists(b) && ((buttonFlags & (1 << b.ordinal())) != 0);
}
public synchronized void setButton(MediaPointerButton b, boolean down) {
if (b != MediaPointerButton.MAIN && nbuttons < b.ordinal()+1) {
nbuttons = b.ordinal()+1;
}
if (down) {
buttonFlags |= (1 << b.ordinal());
} else {
buttonFlags &= ~(1 << b.ordinal());
}
}
public synchronized boolean isTouch() {
return nbuttons == 0;
}
public boolean isValid() {
return pointerId != null;
}
public synchronized boolean buttonExists(MediaPointerButton b) {
if (b == MediaPointerButton.MAIN) {
return true;
} else {
return nbuttons >= b.ordinal()+1;
}
}
public boolean hoverWithin(double x2, double y2, double width, double height) {
if (x >= x2 && x <= x2 + width && y >= y2 && y <= y2 + height) {
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,10 @@
package slgui.base;
/** Represents an object which can be used to launch applications.
*
* @author Zak Fenton
*
*/
public interface MediaSystemDelegate {
MediaAppDelegate pairApp(MediaApp app);
}

View File

@ -0,0 +1,6 @@
package slgui.base;
/** This represents an "outer" view which holds a MediaModel. */
public interface MediaViewDelegate {
}

35
src/slgui/demo/Main.java Normal file
View File

@ -0,0 +1,35 @@
package slgui.demo;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import slgui.base.MediaModel;
import slgui.space.DemoSpaceDelegate;
import slgui.space.SpaceMediaModel;
import slgui.space.SpaceState;
import slgui.swing.GameWindow;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
DemoSpaceDelegate d = new DemoSpaceDelegate();
SpaceState space = new SpaceState(d);
SpaceMediaModel model = new SpaceMediaModel(space);
GameWindow w = new GameWindow("ZGame Demo");
w.setSize(640, 480);
w.setMinimumSize(new Dimension(400,400));
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//w.setModel(new MediaModel());
w.setModel(model);
w.setVisible(true);
}
});
}
}

View File

@ -0,0 +1,86 @@
package slgui.gfx;
import slgui.base.MediaPainter;
/** A Painter object holds a graphics state which can be used for immediate rendering operations.
* Painter objects extend the minimalist MediaPainter class with an internal context stack and
* additional, more-convenient rendering functions.
*
* @author Zak Fenton
*/
public class Painter extends MediaPainter {
private final MediaPainter target;
private final PainterContext[] contexts;
private int stackIndex = -1;
/* The following should have equivalents in the PainterContext class: */
Transformer transformer = Transformer.DIRECT;
public Painter(MediaPainter target, int stackmax) {
this.target = target;
contexts = new PainterContext[stackmax];
}
public PainterContext contextAt(int index) {
if (contexts[index] == null) {
contexts[index] = new PainterContext();
}
return contexts[index];
}
public void save() {
int nsi = stackIndex + 1;
PainterContext ctx = contextAt(nsi);
ctx.save(this, nsi);
stackIndex = nsi;
}
public void restore() {
PainterContext ctx = contextAt(stackIndex);
ctx.restore(this);
ctx.reset();
stackIndex = stackIndex - 1;
}
/** Applies the given transformer (over the top of any already-applied transformers). */
public void transform(Transformer t) {
transformer = t.over(transformer);
}
/** Applies a TranslateTransformer with the given translation offset coordinates (over the top of any already-applied transformers). */
public void translate(double tx, double ty) {
transform(new TranslateTransformer(tx, ty));
}
@Override
public double snapX(double x) {
return transformer.unX(target.snapX(transformer.x(x)));
}
@Override
public double snapY(double y) {
return transformer.unY(target.snapY(transformer.y(y)));
}
@Override
public void line(double x1, double y1, double x2, double y2, int pixel, double radius) {
x1 = transformer.x(x1);
y1 = transformer.y(y1);
x2 = transformer.x(x2);
y2 = transformer.y(y2);
radius = transformer.radius(radius);
target.line(x1, y1, x2, y2, pixel, radius);
}
@Override
public void box(double x, double y, double width, double height, int pixel) {
x = transformer.x(x);
y = transformer.x(x);
width = transformer.width(width);
height = transformer.height(height);
target.box(x, y, width, height, pixel);
}
}

View File

@ -0,0 +1,36 @@
package slgui.gfx;
/** A PainterContext holds a saved, internal state of a painter.
*
* @author Zak Fenton
*
*/
public class PainterContext {
Painter painter;
int stackIndex;
/* The following should have equivalents in the Painter class. */
Transformer transformer;
public PainterContext() {
reset();
}
public void reset() {
painter = null;
stackIndex = -1;
transformer = null;
}
public void save(Painter p, int stackIndex) {
this.painter = p;
this.stackIndex = stackIndex;
this.transformer = p.transformer;
}
public void restore(Painter p) {
p.transformer = this.transformer;
}
}

View File

@ -0,0 +1,57 @@
package slgui.gfx;
/** A transformer represents an operation which applies to the coordinate space
* (i.e. moving the x/y coordinates around in some pattern).
*
* @author Zak Fenton
*
*/
public class Transformer {
/** This is the only non-subclass instance of Transformer which should be used, other than base classes. */
public static Transformer DIRECT = new Transformer();
protected Transformer() {
}
public double x(double x) {
return x;
}
public double y(double y) {
return y;
}
public double unX(double x) {
return x;
}
public double unY(double y) {
return y;
}
public double width(double w) {
return w;
}
public double height(double h) {
return h;
}
public double radius(double r) {
return (width(r) + height(r)) / 2;
}
public Transformer over(Transformer other) {
if (other == DIRECT) {
return this;
} else if (this == DIRECT) {
return other;
} else {
return combinedTransformer(other);
}
}
public Transformer combinedTransformer(Transformer other) {
throw new Error("TODO!");
}
}

View File

@ -0,0 +1,47 @@
package slgui.gfx;
/** A TranslateTransformer only adds a "translation" offset to the x/y coordinates. It has
* no effect on other (width/height-related) coordinates.
*
* @author Zak Fenton
*
*/
public final class TranslateTransformer extends Transformer {
public final double tx;
public final double ty;
public TranslateTransformer(double tx, double ty) {
this.tx = tx;
this.ty = ty;
}
@Override
public double x(double x) {
return tx + x;
}
@Override
public double y(double y) {
return tx + y;
}
@Override
public double unX(double x) {
return x - tx;
}
@Override
public double unY(double y) {
return y - ty;
}
@Override
public Transformer combinedTransformer(Transformer other) {
if (other instanceof TranslateTransformer) {
TranslateTransformer o = (TranslateTransformer) other;
return new TranslateTransformer(this.tx + o.tx, this.ty + o.ty);
} else {
return super.combinedTransformer(other);
}
}
}

View File

@ -0,0 +1,44 @@
package slgui.space;
import java.util.Date;
public class DemoSpaceDelegate implements SpaceDelegate {
public DemoSpaceDelegate() {
// TODO Auto-generated constructor stub
}
@Override
public String getStatus1() {
return "Demo";
}
@Override
public String getStatus2() {
String t = new Date().toLocaleString();
t = t.substring(t.indexOf(' '));
return t;
}
@Override
public long getMilliseconds() {
return System.currentTimeMillis();
}
@Override
public void attachSpace(SpaceState space) {
WindowState w = space.newWindow(new WindowDelegate() {
});
w.setCoordinates(100, 500, 320, 240);
w.setTitle("Hello");
w = space.newWindow(new WindowDelegate() {
});
w.setCoordinates(600, 500, 320, 240);
w.setTitle("World");
}
@Override
public void detachSpace(SpaceState state) {
// No action by default.
}
}

View File

@ -0,0 +1,8 @@
package slgui.space;
public enum SizingState {
MINIMISED,
WINDOWED,
MAXIMISED,
FULLSCREEN
}

View File

@ -0,0 +1,10 @@
package slgui.space;
public interface SpaceDelegate {
public String getStatus1();
public String getStatus2();
public long getMilliseconds();
public void attachSpace(SpaceState state);
public void detachSpace(SpaceState state);
}

View File

@ -0,0 +1,30 @@
package slgui.space;
import slgui.base.MediaInputState;
import slgui.base.MediaModel;
import slgui.base.MediaPainter;
import slgui.base.MediaViewDelegate;
public class SpaceMediaModel extends MediaModel {
SpaceState space;
public SpaceMediaModel(SpaceState space) {
this.space = space;
}
@Override
protected void screenSizeChanged() {
space.setCoordinates(0, 0, getWidth(), getHeight());
}
@Override
public void paintView(MediaViewDelegate v, MediaPainter p) {
space.paint(p);
}
@Override
public void setInputState(MediaInputState input) {
super.setInputState(input);
space.setInputState(input);
}
}

View File

@ -0,0 +1,166 @@
package slgui.space;
import java.util.ArrayList;
import java.util.Date;
import slgui.base.MediaBox;
import slgui.base.MediaFont;
import slgui.base.MediaInputState;
import slgui.base.MediaPainter;
import slgui.base.MediaPointerButton;
import slgui.themes.KeypadType;
import slgui.themes.PanelMode;
import slgui.themes.StatusMode;
import slgui.themes.Theme;
public class SpaceState {
private SpaceDelegate delegate;
private boolean dirty = true;
private ArrayList<WindowState> windows = new ArrayList<WindowState>();
WindowState dragging;
double dragX, dragY;
KeypadType keypad;
KeypadType nextKeypad; /* This value is eventually assigned to "keypad", after animation. */
MediaFont font = new MediaFont(2);
private double x, y, width, height;
private MediaInputState input = new MediaInputState();
public SpaceState(SpaceDelegate delegate) {
this.delegate = delegate;
delegate.attachSpace(this);
}
public boolean isDirty() {
return dirty;
}
public void markDirty(boolean value) {
this.dirty = value;
}
public void markDirty() {
markDirty(true);
}
public void setInputState(MediaInputState input) {
this.input = input;
if (input.downWithin(MediaPointerButton.MAIN, x, y, width, height)) {
if (dragging != null) {
if (dragX > 0) {
System.out.println("Continguing drag");
MediaBox coords = dragging.realCoordinates();
dragging.setCoordinates(input.lastPointerX() - dragX, input.lastPointerY() - dragY, coords.width, coords.height);
} else {
System.out.println("Continguing resize");
MediaBox coords = dragging.realCoordinates();
dragging.setCoordinates(coords.x, coords.y, Math.max(100, input.lastPointerX() - coords.x), Math.max(100, input.lastPointerY() - coords.y));
}
} else {
WindowState[] wins = currentWindows();
for (int i = wins.length - 1; i >= 0; i--) {
MediaBox coords = wins[i].realCoordinates();
// TODO: The drag area should be dictated by the theme
if (input.downWithin(MediaPointerButton.MAIN, coords.x, coords.y, coords.width, coords.height)) {
if (input.downWithin(MediaPointerButton.MAIN, coords.x + 24, coords.y, coords.width-48, 24)) {
System.out.println("Starting drag");
dragging = wins[i];
dragging.bringToFront();
dragX = input.lastPointerX() - coords.x;
dragY = input.lastPointerY() - coords.y;
System.out.println("Dragx=" +dragX + "y=" + dragY);
} else if (input.downWithin(MediaPointerButton.MAIN, coords.x + coords.width-16, coords.y + coords.height-16, 16, 16)) {
System.out.println("Starting resize");
dragging = wins[i];
dragging.bringToFront();
dragX = input.lastPointerX() - (coords.x + coords.width);
dragY = input.lastPointerY() - (coords.y + coords.height);
System.out.println("Dragx=" +dragX + "y=" + dragY);
}
break;
}
}
}
} else {
dragging = null;
}
}
public MediaInputState getInputState() {
return input;
}
public synchronized WindowState newWindow(WindowDelegate delegate) {
WindowState result = new WindowState(this, delegate);
windows.add(result);
markDirty();
return result;
}
public synchronized void bringToFront(WindowState w) {
int winidx = windows.indexOf(w);
if (winidx < 0) {
return;
}
ArrayList<WindowState> newWindows = new ArrayList<WindowState>(windows.size());
for (int i = 0; i < windows.size(); i++) {
if (i < winidx) {
newWindows.add(windows.get(i));
} else if (i == windows.size()-1) {
newWindows.add(w);
} else {
newWindows.add(windows.get(i + 1));
}
}
windows = newWindows;
}
public void setCoordinates(double x, double y, double width, double height) {
if (x != this.x || y != this.y || width != this.width || height != this.height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
markDirty();
}
}
public synchronized WindowState[] currentWindows() {
return windows.toArray(new WindowState[0]);
}
public void paint(MediaPainter p) {
markDirty(false);
p.line(10, 100, width-10, height-100, 0xFFFF0000, 10.0);
p.line(width-100, 10, 100, height-10, 0xFFFF0000, 1.0);
font.render(p, "Hello, world!", 100, 200, 0xFF50C0FF);
new MediaFont(1).render(p, "Hello, world!", 100, 225, 0xFF50C0FF);
new MediaFont(4).render(p, "Hello, world! 0123456789", 100, 250, 0xFF50C0FF);
new MediaFont(4).render(p, "Hello... world?", 100, 300, 0xFF50C0FF);
new MediaFont(2).render(p, "Sphinx of black quartz, judge my vow.", 100, 350, 0xFF50C0FF);
new MediaFont(1).render(p, "Sphinx of black quartz, judge my vow. 0123456789", 100, 400, 0xFF50C0FF);
Theme t = new Theme();
double keyanim = Math.sin((System.currentTimeMillis()%3000)/1000.0);
MediaBox content = t.desktopContentArea(p, 0,0, width, height, KeypadType.LATIN_LOWER, keyanim, StatusMode.TOP, PanelMode.BOTTOM);
WindowState[] wins = currentWindows();
double innerX = content.x;
double innerY = content.y;
double innerWidth = content.width;
double innerHeight = content.height;
for (int i = 0; i < wins.length; i++) {
/* This will, if necessary, resize any windows to appropriate bounds. They may still
* exceed these bounds (or be entirely "off-screen"), but only if the user has some
* way of controlling the window.
*/
wins[i].realiseCoordinates(p, t, innerX, innerY, innerWidth, innerHeight);
}
for (int i = 0; i < wins.length; i++) {
wins[i].paint(p, getInputState(), t);
}
t.paintDemoWindow(p, getInputState(), "A window", 300, 25, 250, 150);
t.paintDemoDesktop(p, getInputState(), delegate.getStatus1(), delegate.getStatus2(), 0,0, width, height, KeypadType.LATIN_LOWER, keyanim, StatusMode.TOP, PanelMode.BOTTOM);
}
}

View File

@ -0,0 +1,5 @@
package slgui.space;
public interface WindowDelegate {
}

View File

@ -0,0 +1,114 @@
package slgui.space;
import slgui.base.MediaBox;
import slgui.base.MediaInputState;
import slgui.base.MediaPainter;
import slgui.themes.Theme;
public class WindowState {
private SpaceState space;
private WindowDelegate delegate;
private boolean dirty = true;
private boolean redraw = true;
private double realX, realY, realWidth, realHeight;
private double configuredX, configuredY, configuredWidth, configuredHeight;
private String title;
private SizingState sizing = SizingState.WINDOWED;
WindowState(SpaceState space, WindowDelegate delegate) {
this.space = space;
this.delegate = delegate;
title = "Untitled";
}
public boolean isDirty() {
return dirty;
}
public void markDirty(boolean value) {
if (value && !dirty) {
space.markDirty();
}
this.dirty = value;
}
public void markDirty() {
markDirty(true);
}
public boolean shouldRedraw() {
return redraw;
}
public void markRedraw(boolean value) {
this.redraw = value;
}
public void markRedraw() {
markRedraw(true);
}
public void setSizingState(SizingState sizing) {
if (sizing != this.sizing) {
this.sizing = sizing;
markDirty();
markRedraw();
}
}
public SizingState getSizingState() {
return this.sizing;
}
public synchronized void realiseCoordinates(MediaPainter p, Theme t, double minX, double minY, double maxWidth, double maxHeight) {
double newX = Math.max(minX, configuredX);
double newY = Math.max(minY, configuredY);
double newWidth = configuredWidth; //Math.min(maxWidth, configuredWidth);
double newHeight = configuredHeight; //Math.min(maxHeight, configuredHeight);
if (newX > minX + maxWidth - 100) {
newX = minX + maxWidth - 100;
}
if (newY > minY + maxHeight - 32) {
newY = minY + maxHeight - 32;
}
if (newX != realX || newY != realY || newWidth != realWidth || newHeight != realHeight) {
realX = newX;
realY = newY;
realWidth = newWidth;
realHeight = newHeight;
markRedraw();
markDirty();
}
}
public synchronized void setCoordinates(double x, double y, double width, double height) {
if (x != configuredX || y != configuredY || width != configuredWidth || height != configuredHeight) {
configuredX = x;
configuredY = y;
configuredWidth = width;
configuredHeight = height;
markRedraw();
markDirty();
}
}
public synchronized MediaBox realCoordinates() {
return new MediaBox(realX, realY, realWidth, realHeight);
}
public void bringToFront() {
space.bringToFront(this);
}
public void paint(MediaPainter p, MediaInputState input, Theme t) {
t.paintDemoWindow(p, input, title, realX, realY, realWidth, realHeight);
}
public void setTitle(String string) {
this.title = string;
markDirty();
}
}

View File

@ -0,0 +1,5 @@
package slgui.space;
public interface WindowVisitor {
public void visitWindow(WindowState state);
}

View File

@ -0,0 +1,58 @@
package slgui.swing;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import slgui.base.MediaPainter;
public class G2DPainter extends MediaPainter {
public final Graphics2D g2d;
int previousPixel;
double previousRadius = -0.0;
public G2DPainter(Graphics2D g2d) {
this.g2d = g2d;
previousPixel = g2d.getColor().getRGB();
setRadius(1.0);
}
private final void setPixel(int x) {
if (x != previousPixel) {
Color c = new Color(x, true);
g2d.setColor(c);
previousPixel = x;
}
}
private final void setRadius(double x) {
if (x != previousPixel) {
BasicStroke stroke = new BasicStroke((float) x*2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g2d.setStroke(stroke);
previousRadius = x;
}
}
@Override
public void line(double x1, double y1, double x2, double y2, int pixel, double radius) {
if (radius > 0) {
x1 -= 0.5;
y1 -= 0.5;
x2 -= 0.5;
y2 -= 0.5;
setPixel(pixel);
setRadius(radius);
g2d.drawLine((int) x1, (int) y1, (int) x2, (int) y2);
}
}
@Override
public void box(double x, double y, double width, double height, int pixel) {
if (width > 0 && height > 0) {
x -= 0.5;
y -= 0.5;
setPixel(pixel);
g2d.fillRect((int) x, (int) y, (int) width, (int) height);
}
}
}

View File

@ -0,0 +1,165 @@
package slgui.swing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.Timer;
import javax.swing.JComponent;
import slgui.base.MediaInputState;
import slgui.base.MediaModel;
import slgui.base.MediaPointerButton;
import slgui.base.MediaViewDelegate;
public class GameComponent extends JComponent implements MediaViewDelegate {
private MediaModel model;
private Timer animationTimer;
private MediaInputState input = new MediaInputState();
public GameComponent() {
// TODO Auto-generated constructor stub
}
public synchronized void stopAnimationTimer() {
if (animationTimer != null) {
animationTimer.stop();
animationTimer = null;
}
}
public synchronized void startAnimationTimer(int millis) {
stopAnimationTimer();
animationTimer = new Timer(millis, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
animationTimer.setRepeats(true);
animationTimer.start();
}
@Override
protected void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.red);
if (model == null) {
g.drawString("No game initialised", getWidth()/2, getHeight()/2);
} else {
model.assertScreenSize(getWidth(), getHeight());
G2DPainter painter = new G2DPainter((Graphics2D) g);
model.paintView(this, painter);
g.drawString("TODO...", getWidth()/2, getHeight()/2);
}
//super.paintComponent(g);
}
boolean leftDown = false;
public void setModel(MediaModel m) {
if (m == model) {
return;
}
if (model != null) {
model.detachView(this);
stopAnimationTimer();
}
model = m;
if (model != null) {
model.attachView(this);
if (model.isAnimated()) {
startAnimationTimer(100);
}
if (model.isKeyed()) {
setFocusable(true);
requestFocusInWindow();
addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent e) {
input.setKeypad(e.getKeyText(e.getKeyCode()), false);
model.setInputState(input);
}
@Override
public void keyPressed(KeyEvent e) {
input.setKeypad(e.getKeyText(e.getKeyCode()), true);
model.setInputState(input);
}
});
}
addMouseListener(new MouseListener() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
leftDown = false;
}
input.setPointer("mouse", e.getX(), e.getY(), 1).setButton(MediaPointerButton.MAIN, leftDown);
model.setInputState(input);
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
leftDown = true;
}
input.setPointer("mouse", e.getX(), e.getY(), 1).setButton(MediaPointerButton.MAIN, leftDown);
model.setInputState(input);
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
});
addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseMoved(MouseEvent e) {
input.setPointer("mouse", e.getX(), e.getY(), 1).setButton(MediaPointerButton.MAIN, leftDown);
model.setInputState(input);
}
@Override
public void mouseDragged(MouseEvent e) {
input.setPointer("mouse", e.getX(), e.getY(), 1).setButton(MediaPointerButton.MAIN, leftDown);
model.setInputState(input);
}
});
}
repaint();
}
}

View File

@ -0,0 +1,41 @@
package slgui.swing;
import java.awt.GraphicsConfiguration;
import java.awt.HeadlessException;
import javax.swing.JFrame;
import slgui.base.MediaModel;
public class GameWindow extends JFrame {
GameComponent gameComponent;
public GameWindow() throws HeadlessException {
initUI();
}
public GameWindow(GraphicsConfiguration gc) {
super(gc);
initUI();
}
public GameWindow(String title) throws HeadlessException {
super(title);
initUI();
}
public GameWindow(String title, GraphicsConfiguration gc) {
super(title, gc);
initUI();
}
private void initUI() {
gameComponent = new GameComponent();
this.getContentPane().add(gameComponent);
pack();
}
public void setModel(MediaModel m) {
gameComponent.setModel(m);
}
}

View File

@ -0,0 +1,7 @@
package slgui.themes;
public enum ButtonState {
INACTIVE,
ACTIVE,
HOVER
}

View File

@ -0,0 +1,8 @@
package slgui.themes;
public enum KeypadType {
NONE,
LATIN_LOWER,
LATIN_UPPER,
NUMBERS
}

View File

@ -0,0 +1,10 @@
package slgui.themes;
public enum PanelMode {
HIDDEN,
AUTO_BOTTOM_LEFT,
AUTO_BOTTOM_RIGHT,
BOTTOM,
LEFT,
RIGHT
}

View File

@ -0,0 +1,7 @@
package slgui.themes;
public enum SpecialButtonType {
BLANK,
CLOSE,
HOME
}

View File

@ -0,0 +1,6 @@
package slgui.themes;
public enum StatusMode {
HIDDEN,
TOP
}

225
src/slgui/themes/Theme.java Normal file
View File

@ -0,0 +1,225 @@
package slgui.themes;
import slgui.base.MediaBox;
import slgui.base.MediaFont;
import slgui.base.MediaInputState;
import slgui.base.MediaPainter;
import slgui.base.MediaPointerButton;
import slgui.gfx.Painter;
public class Theme {
public Theme() {
// TODO Auto-generated constructor stub
}
public MediaFont getFont() {
return new MediaFont(2);
}
public int getBorderPixel() {
return 0xFF808080;
}
public void paintSpecialButton(MediaPainter p, MediaInputState input, SpecialButtonType t, double x, double y, double width, double height, ButtonState overrideState) {
if (overrideState == null) {
overrideState = ButtonState.INACTIVE;
if (input != null) {
if (input.downWithin(MediaPointerButton.MAIN, x, y, width, height)) {
overrideState = ButtonState.ACTIVE;
} else if (input.hoverWithin(x, y, width, height)) {
overrideState = ButtonState.HOVER;
}
}
}
p.box(x, y, width, height, overrideState == ButtonState.ACTIVE ? 0xFFFFC050 : (overrideState == ButtonState.HOVER ? 0xFF50FFC0 : 0xFFFFFFFF));
p.line(x, y, x + width, y, getBorderPixel(), 1.0);
p.line(x, y+height, x + width, y+height, getBorderPixel(), 1.0);
p.line(x, y, x, y + height, getBorderPixel(), 1.0);
p.line(x + width, y, x + width, y + height, getBorderPixel(), 1.0);
switch (t) {
case BLANK:
break;
case CLOSE:
p.line(x, y, x+width,y+height, getBorderPixel(), 1.0);
p.line(x, y+height, x+width,y, getBorderPixel(), 1.0);
break;
default:
p.line(x, y, x+width,y+height, 0xFFFF0000, 1.0);
p.line(x, y+height, x+width,y, 0xFFFF0000, 1.0);
break;
}
}
public void paintButton(MediaPainter p, MediaInputState input, String label, double x, double y, double width, double height) {
paintSpecialButton(p, input, SpecialButtonType.BLANK, x, y, width, height, input.keypadDown(label.toUpperCase()) ? ButtonState.ACTIVE : null);
getFont().render(p, label, x + (width/2) - ((4 + label.length()*16)/2), y + (height/2) - 8, 0xFF0000FF);
}
public void paintWindowBorder(MediaPainter p, MediaInputState input, String title, double x, double y, double width, double height) {
int topcolour;
if (input != null && input.hoverWithin(x, y, width, 24)) {
topcolour = 0xF0FFC050;
} else {
topcolour = 0xE0E0FFE0;
}
p.box(x, y, width, 24, topcolour);
p.line(x, y, x + width, y, getBorderPixel(), 1.0);
p.line(x, y+24, x + width, y+24, getBorderPixel(), 1.0);
p.line(x, y+height, x + width, y+height, getBorderPixel(), 1.0);
p.line(x, y, x, y + height, getBorderPixel(), 1.0);
p.line(x + width, y, x + width, y + height, getBorderPixel(), 1.0);
p.line(x + width, y + height - 16, x + width - 16, y + height, getBorderPixel(), 1.0);
getFont().render(p, title, x + 24, y + 4, 0xFF0000FF);
}
public void paintDemoWindow(MediaPainter p, MediaInputState input, String title, double x, double y, double width, double height) {
paintWindowBorder(p, input, title, x, y, width, height);
paintSpecialButton(p, input, SpecialButtonType.BLANK, x + 4, y + 4, 16, 16, null);
paintSpecialButton(p, input, SpecialButtonType.CLOSE, x + width - 20, y + 4, 16, 16, null);
}
public MediaBox desktopContentArea(MediaPainter p, double x, double y, double width, double height, KeypadType keypad, double keypadratio, StatusMode statusMode, PanelMode panelMode) {
if (keypadratio < 0) {
keypadratio = 0;
} else if (keypadratio > 1) {
keypadratio = 1;
}
if (keypad != KeypadType.NONE) {
double kph = height/3;
double kpw = 2*width/3;
double kpx = x + (width/2) - (kpw/2);
double kpy = y+height-40-(kph+1) + ((1.0-keypadratio)*(kph+100));
//paintKeypad(p, keypad, kpx, kpy, kpw, kph, 100);
return new MediaBox(x+0, y+24, width, Math.min(kpy-(y+24), height-(24+40)));
} else {
return new MediaBox(x+0, y+24, width, height-(24+40));
}
}
public void paintDesktopControls(MediaPainter p, MediaInputState input, String status1, String status2, double x, double y, double width, double height, KeypadType keypad, double keypadratio, StatusMode statusMode, PanelMode panelMode) {
p.box(x, y, width, 24, 0xDDFFFFFF);
//p.line(x, y, x + width, y, getBorderPixel(), 1.0);
p.line(x, y+24, x + width, y+24, getBorderPixel(), 1.0);
//p.line(x, y+height, x + width, y+height, getBorderPixel(), 1.0);
//p.line(x, y, x, y + height, getBorderPixel(), 1.0);
for (int i = 0; i*2 < 40; i++) {
p.box(x, y+height-(40-i*2), width, 42-i*2, 0x40000000);//0x40FFFFFF);
}
p.line(x, y+height-40, x + width, y+height-40, getBorderPixel(), 1.0);
//p.line(x + width, y, x + width, y + height, getBorderPixel(), 1.0);
getFont().render(p, status1, x + 24, y + 4, 0xFF0000FF);
getFont().render(p, status2, x + width - (4 + status2.length()*16), y + 4, 0xFF0000FF);
}
public void paintDemoDesktop(MediaPainter p, MediaInputState input, String status1, String status2, double x, double y, double width, double height, KeypadType keypad, double keypadratio, StatusMode statusMode, PanelMode panelMode) {
if (keypadratio < 0) {
keypadratio = 0;
} else if (keypadratio > 1) {
keypadratio = 1;
}
if (keypad != KeypadType.NONE) {
double kph = height/3;
double kpw = 2*width/3;
double kpx = x + (width/2) - (kpw/2);
double kpy = y+height-40-(kph+1) + ((1.0-keypadratio)*(kph+100));
paintKeypad(p, input, keypad, kpx, kpy, kpw, kph, 100);
}
paintDesktopControls(p, input, status1, status2, x, y, width, height, keypad, keypadratio, statusMode, panelMode);
paintSpecialButton(p, input, SpecialButtonType.BLANK, x + 4, y + 4, 16, 16, null);
paintSpecialButton(p, input, SpecialButtonType.HOME, x + (width/2) - 16, y + height - 36, 32, 32, null);
paintSpecialButton(p, input, SpecialButtonType.HOME, x + (width/2) - 56, y + height - 36, 32, 32, null);
paintSpecialButton(p, input, SpecialButtonType.HOME, x + (width/2) + 24, y + height - 36, 32, 32, null);
}
public void paintKeypad(MediaPainter p, MediaInputState input, KeypadType t, double x, double y, double width, double height, double xheight) {
p.box(x, y, width, height+xheight, 0xDDFFFFFF);
p.line(x, y, x + width, y, getBorderPixel(), 1.0);
p.line(x, y+height+xheight, x + width, y+height+xheight, getBorderPixel(), 1.0);
p.line(x, y, x, y + height+xheight, getBorderPixel(), 1.0);
p.line(x + width, y, x + width, y + height+xheight, getBorderPixel(), 1.0);
switch(t) {
case NUMBERS:
paintNumpad(p, input, x, y, width, height);
break;
case LATIN_UPPER:
paintLatinKeypad(p, input, x, y, width, height, true);
break;
case LATIN_LOWER:
paintLatinKeypad(p, input, x, y, width, height, false);
break;
}
}
public void paintNumpad(MediaPainter p, MediaInputState input, double x, double y, double width, double height) {
double spc = (width+height)/50;
double w = (width - spc*4)/3;
double h = (height - spc*5)/4;
paintButton(p, input, "1", x+spc, y+spc, w, h);
paintButton(p, input, "2", x+spc+w+spc, y+spc, w, h);
paintButton(p, input, "3", x+spc+(w+spc)*2, y+spc, w, h);
paintButton(p, input, "4", x+spc, y+spc+(h+spc), w, h);
paintButton(p, input, "5", x+spc+w+spc, y+spc+(h+spc), w, h);
paintButton(p, input, "6", x+spc+(w+spc)*2, y+spc+(h+spc), w, h);
paintButton(p, input, "7", x+spc, y+spc+(h+spc)*2, w, h);
paintButton(p, input, "8", x+spc+w+spc, y+spc+(h+spc)*2, w, h);
paintButton(p, input, "9", x+spc+(w+spc)*2, y+spc+(h+spc)*2, w, h);
//paintButton(p, "7", x+spc, y+spc+(h+spc)*3, w, h);
paintButton(p, input, "0", x+spc+w+spc, y+spc+(h+spc)*3, w, h);
//paintButton(p, "9", x+spc+(w+spc)*2, y+spc+(h+spc)*3, w, h);
}
public String recase(String input, boolean upcase) {
if (upcase) {
return input.toUpperCase();
} else {
return input.toLowerCase();
}
}
public void paintKeypadRow(MediaPainter p, MediaInputState input, double x, double y, double width, double height, boolean upcase, double spc, double bw, double bh, String...strings) {
x = (x + width/2) - ((strings.length*bw + (strings.length-1)*spc)/2);
for (int i = 0; i < strings.length; i++) {
String s = recase(strings[i], upcase);
double bx = x + (i * (bw + spc));
double by = y + spc;
paintButton(p, input, s, bx, by, bw, bh);
}
}
public void paintLatinKeypad(MediaPainter p, MediaInputState input, double x, double y, double width, double height, boolean upcase) {
double spc = 8;//(width+height)/50;
double w = (width - spc*11)/10;
double h = (height - spc*5)/4;
paintKeypadRow(p, input, x, y, width, height, upcase, spc, w, h, "q", "w", "e", "r", "t", "y", "u", "i", "o", "p");
paintKeypadRow(p, input, x, y+(h+spc), width, height, upcase, spc, w, h, "a", "s", "d", "f", "g", "h", "j", "k", "l");
paintKeypadRow(p, input, x, y+(h+spc)*2, width, height, upcase, spc, w, h, "z", "x", "c", "v", "b", "n", "m");
double spcw = w*3+spc*2;
paintButton(p, input, " ", x + width/2 - spcw/2, y+spc+(h+spc)*3, spcw, h);
/*paintButton(p, "1", x+spc, y+spc, w, h);
paintButton(p, "2", x+spc+w+spc, y+spc, w, h);
paintButton(p, "3", x+spc+(w+spc)*2, y+spc, w, h);
paintButton(p, "4", x+spc, y+spc+(h+spc), w, h);
paintButton(p, "5", x+spc+w+spc, y+spc+(h+spc), w, h);
paintButton(p, "6", x+spc+(w+spc)*2, y+spc+(h+spc), w, h);
paintButton(p, "7", x+spc, y+spc+(h+spc)*2, w, h);
paintButton(p, "8", x+spc+w+spc, y+spc+(h+spc)*2, w, h);
paintButton(p, "9", x+spc+(w+spc)*2, y+spc+(h+spc)*2, w, h);
//paintButton(p, "7", x+spc, y+spc+(h+spc)*3, w, h);
paintButton(p, "0", x+spc+w+spc, y+spc+(h+spc)*3, w, h);
//paintButton(p, "9", x+spc+(w+spc)*2, y+spc+(h+spc)*3, w, h);
*/
}
}

64
src/slgui/views/View.java Normal file
View File

@ -0,0 +1,64 @@
package slgui.views;
import slgui.gfx.Painter;
import slgui.themes.Theme;
public abstract class View {
private View outer;
private View[] inners = null;
public abstract void paint(Painter painter, Theme theme);
public View outer() {
return outer;
}
public View inner(int i) {
return inners[i];
}
public int innerIndex(View v) {
for (int i = 0; i < inners(); i++) {
if (inner(i) == v) {
return i;
}
}
return -1;
}
public int inners() {
return inners.length;
}
public void addView(View v) {
View[] newInners = new View[inners() + 1];
for (int i = 0; i < newInners.length; i++) {
if (i < inners()) {
newInners[i] = inner(i);
} else {
newInners[i] = v;
}
}
inners = newInners;
}
public void removeView(View v) {
int idx = innerIndex(v);
if (idx >= 0) {
View[] newInners = new View[inners() - 1];
for (int i = 0; i < newInners.length; i++) {
if (i < idx) {
newInners[i] = inner(i);
} else {
newInners[i] = inner(i+1);
}
}
inners = newInners;
}
}
}