commit c4106a0ce53bf9a871e8085a487c3b1cae587dec Author: Zak Yani Star Fenton Date: Wed Jun 11 01:00:06 2025 +1000 Added project files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84c048a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..9e71454 --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project slgui. + + + diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..27963b4 --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1771 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..db74a6c --- /dev/null +++ b/nbproject/genfiles.properties @@ -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 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..e6b9ac3 --- /dev/null +++ b/nbproject/project.properties @@ -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 diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..8f45d58 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + slgui + + + + + + + + + diff --git a/src/slgui/base/MediaApp.java b/src/slgui/base/MediaApp.java new file mode 100644 index 0000000..19db053 --- /dev/null +++ b/src/slgui/base/MediaApp.java @@ -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; + } +} diff --git a/src/slgui/base/MediaAppDelegate.java b/src/slgui/base/MediaAppDelegate.java new file mode 100644 index 0000000..bddc5a3 --- /dev/null +++ b/src/slgui/base/MediaAppDelegate.java @@ -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(); +} diff --git a/src/slgui/base/MediaAppId.java b/src/slgui/base/MediaAppId.java new file mode 100644 index 0000000..bad6420 --- /dev/null +++ b/src/slgui/base/MediaAppId.java @@ -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; + } + +} diff --git a/src/slgui/base/MediaBox.java b/src/slgui/base/MediaBox.java new file mode 100644 index 0000000..909d5e1 --- /dev/null +++ b/src/slgui/base/MediaBox.java @@ -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); + } +} diff --git a/src/slgui/base/MediaFont.java b/src/slgui/base/MediaFont.java new file mode 100644 index 0000000..6ad7086 --- /dev/null +++ b/src/slgui/base/MediaFont.java @@ -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; + } + } + } +} diff --git a/src/slgui/base/MediaInputState.java b/src/slgui/base/MediaInputState.java new file mode 100644 index 0000000..45cf588 --- /dev/null +++ b/src/slgui/base/MediaInputState.java @@ -0,0 +1,86 @@ +package slgui.base; + +import java.util.HashMap; + +public class MediaInputState { + private HashMap pointerstates = new HashMap(); + private HashMap keystates = new HashMap(); + double lastX, lastY; + + public MediaInputState() { + // TODO Auto-generated constructor stub + } + + public synchronized void resetKeypad() { + keystates = new HashMap(); + } + + public synchronized void resetPointers() { + pointerstates = new HashMap(); + } + + 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; + } + +} diff --git a/src/slgui/base/MediaModel.java b/src/slgui/base/MediaModel.java new file mode 100644 index 0000000..56e8e3f --- /dev/null +++ b/src/slgui/base/MediaModel.java @@ -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; + } +} diff --git a/src/slgui/base/MediaPainter.java b/src/slgui/base/MediaPainter.java new file mode 100644 index 0000000..1cc24ec --- /dev/null +++ b/src/slgui/base/MediaPainter.java @@ -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); +} diff --git a/src/slgui/base/MediaPoint.java b/src/slgui/base/MediaPoint.java new file mode 100644 index 0000000..df7e733 --- /dev/null +++ b/src/slgui/base/MediaPoint.java @@ -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; + } +} diff --git a/src/slgui/base/MediaPointerButton.java b/src/slgui/base/MediaPointerButton.java new file mode 100644 index 0000000..19421a5 --- /dev/null +++ b/src/slgui/base/MediaPointerButton.java @@ -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 +} diff --git a/src/slgui/base/MediaPointerState.java b/src/slgui/base/MediaPointerState.java new file mode 100644 index 0000000..95a89ec --- /dev/null +++ b/src/slgui/base/MediaPointerState.java @@ -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; + } + } +} diff --git a/src/slgui/base/MediaSystemDelegate.java b/src/slgui/base/MediaSystemDelegate.java new file mode 100644 index 0000000..9af6c7c --- /dev/null +++ b/src/slgui/base/MediaSystemDelegate.java @@ -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); +} diff --git a/src/slgui/base/MediaViewDelegate.java b/src/slgui/base/MediaViewDelegate.java new file mode 100644 index 0000000..ec87bb7 --- /dev/null +++ b/src/slgui/base/MediaViewDelegate.java @@ -0,0 +1,6 @@ +package slgui.base; + +/** This represents an "outer" view which holds a MediaModel. */ +public interface MediaViewDelegate { + +} diff --git a/src/slgui/demo/Main.java b/src/slgui/demo/Main.java new file mode 100644 index 0000000..a10bc4f --- /dev/null +++ b/src/slgui/demo/Main.java @@ -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); + } + }); + } + +} diff --git a/src/slgui/gfx/Painter.java b/src/slgui/gfx/Painter.java new file mode 100644 index 0000000..6c5fcab --- /dev/null +++ b/src/slgui/gfx/Painter.java @@ -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); + } + +} diff --git a/src/slgui/gfx/PainterContext.java b/src/slgui/gfx/PainterContext.java new file mode 100644 index 0000000..446fa46 --- /dev/null +++ b/src/slgui/gfx/PainterContext.java @@ -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; + } +} diff --git a/src/slgui/gfx/Transformer.java b/src/slgui/gfx/Transformer.java new file mode 100644 index 0000000..a60295e --- /dev/null +++ b/src/slgui/gfx/Transformer.java @@ -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!"); + } +} diff --git a/src/slgui/gfx/TranslateTransformer.java b/src/slgui/gfx/TranslateTransformer.java new file mode 100644 index 0000000..ffc176b --- /dev/null +++ b/src/slgui/gfx/TranslateTransformer.java @@ -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); + } + } +} diff --git a/src/slgui/space/DemoSpaceDelegate.java b/src/slgui/space/DemoSpaceDelegate.java new file mode 100644 index 0000000..8089cf2 --- /dev/null +++ b/src/slgui/space/DemoSpaceDelegate.java @@ -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. + } +} diff --git a/src/slgui/space/SizingState.java b/src/slgui/space/SizingState.java new file mode 100644 index 0000000..5bde92b --- /dev/null +++ b/src/slgui/space/SizingState.java @@ -0,0 +1,8 @@ +package slgui.space; + +public enum SizingState { + MINIMISED, + WINDOWED, + MAXIMISED, + FULLSCREEN +} diff --git a/src/slgui/space/SpaceDelegate.java b/src/slgui/space/SpaceDelegate.java new file mode 100644 index 0000000..6382ee7 --- /dev/null +++ b/src/slgui/space/SpaceDelegate.java @@ -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); +} diff --git a/src/slgui/space/SpaceMediaModel.java b/src/slgui/space/SpaceMediaModel.java new file mode 100644 index 0000000..4415ee0 --- /dev/null +++ b/src/slgui/space/SpaceMediaModel.java @@ -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); + } +} diff --git a/src/slgui/space/SpaceState.java b/src/slgui/space/SpaceState.java new file mode 100644 index 0000000..31f49fd --- /dev/null +++ b/src/slgui/space/SpaceState.java @@ -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 windows = new ArrayList(); + 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 newWindows = new ArrayList(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); + } +} diff --git a/src/slgui/space/WindowDelegate.java b/src/slgui/space/WindowDelegate.java new file mode 100644 index 0000000..f93f72b --- /dev/null +++ b/src/slgui/space/WindowDelegate.java @@ -0,0 +1,5 @@ +package slgui.space; + +public interface WindowDelegate { + +} diff --git a/src/slgui/space/WindowState.java b/src/slgui/space/WindowState.java new file mode 100644 index 0000000..8173e0c --- /dev/null +++ b/src/slgui/space/WindowState.java @@ -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(); + } +} diff --git a/src/slgui/space/WindowVisitor.java b/src/slgui/space/WindowVisitor.java new file mode 100644 index 0000000..f5e585a --- /dev/null +++ b/src/slgui/space/WindowVisitor.java @@ -0,0 +1,5 @@ +package slgui.space; + +public interface WindowVisitor { + public void visitWindow(WindowState state); +} diff --git a/src/slgui/swing/G2DPainter.java b/src/slgui/swing/G2DPainter.java new file mode 100644 index 0000000..8152258 --- /dev/null +++ b/src/slgui/swing/G2DPainter.java @@ -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); + } + } +} diff --git a/src/slgui/swing/GameComponent.java b/src/slgui/swing/GameComponent.java new file mode 100644 index 0000000..ddbb7cf --- /dev/null +++ b/src/slgui/swing/GameComponent.java @@ -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(); + } +} diff --git a/src/slgui/swing/GameWindow.java b/src/slgui/swing/GameWindow.java new file mode 100644 index 0000000..6acc2ae --- /dev/null +++ b/src/slgui/swing/GameWindow.java @@ -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); + } +} diff --git a/src/slgui/themes/ButtonState.java b/src/slgui/themes/ButtonState.java new file mode 100644 index 0000000..9e8c692 --- /dev/null +++ b/src/slgui/themes/ButtonState.java @@ -0,0 +1,7 @@ +package slgui.themes; + +public enum ButtonState { + INACTIVE, + ACTIVE, + HOVER +} diff --git a/src/slgui/themes/KeypadType.java b/src/slgui/themes/KeypadType.java new file mode 100644 index 0000000..a7e4495 --- /dev/null +++ b/src/slgui/themes/KeypadType.java @@ -0,0 +1,8 @@ +package slgui.themes; + +public enum KeypadType { + NONE, + LATIN_LOWER, + LATIN_UPPER, + NUMBERS +} diff --git a/src/slgui/themes/PanelMode.java b/src/slgui/themes/PanelMode.java new file mode 100644 index 0000000..a6c7268 --- /dev/null +++ b/src/slgui/themes/PanelMode.java @@ -0,0 +1,10 @@ +package slgui.themes; + +public enum PanelMode { + HIDDEN, + AUTO_BOTTOM_LEFT, + AUTO_BOTTOM_RIGHT, + BOTTOM, + LEFT, + RIGHT +} diff --git a/src/slgui/themes/SpecialButtonType.java b/src/slgui/themes/SpecialButtonType.java new file mode 100644 index 0000000..43c0bc2 --- /dev/null +++ b/src/slgui/themes/SpecialButtonType.java @@ -0,0 +1,7 @@ +package slgui.themes; + +public enum SpecialButtonType { + BLANK, + CLOSE, + HOME +} diff --git a/src/slgui/themes/StatusMode.java b/src/slgui/themes/StatusMode.java new file mode 100644 index 0000000..620fcb6 --- /dev/null +++ b/src/slgui/themes/StatusMode.java @@ -0,0 +1,6 @@ +package slgui.themes; + +public enum StatusMode { + HIDDEN, + TOP +} diff --git a/src/slgui/themes/Theme.java b/src/slgui/themes/Theme.java new file mode 100644 index 0000000..537be86 --- /dev/null +++ b/src/slgui/themes/Theme.java @@ -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); + */ + } +} diff --git a/src/slgui/views/View.java b/src/slgui/views/View.java new file mode 100644 index 0000000..b7bbac7 --- /dev/null +++ b/src/slgui/views/View.java @@ -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; + } + } +}