Browse Source

Committing WIP on the IDE

Stan Hebben 6 years ago
parent
commit
ea5fca4e25
100 changed files with 6898 additions and 0 deletions
  1. 24
    0
      DrawableGui/build.gradle
  2. 90
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/BaseComponentGroup.java
  3. 74
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DCanvas.java
  4. 21
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DColorableIcon.java
  5. 50
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DComponent.java
  6. 39
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DDimensionPreferences.java
  7. 18
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DDrawable.java
  8. 35
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DDrawingContext.java
  9. 47
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DEmptyView.java
  10. 70
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DFont.java
  11. 15
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DFontFamily.java
  12. 22
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DFontMetrics.java
  13. 24
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DIRectangle.java
  14. 211
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DKeyEvent.java
  15. 51
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DMouseEvent.java
  16. 14
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DPath.java
  17. 14
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DPathCommand.java
  18. 22
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DPathTracer.java
  19. 14
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DPreparedPath.java
  20. 29
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DRectangle.java
  21. 240
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DSideLayout.java
  22. 38
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/DTransform2D.java
  23. 25
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/border/DBorder.java
  24. 42
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/border/DEmptyBorder.java
  25. 55
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/border/DLineBorder.java
  26. 28
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/listeners/DummyListenerHandle.java
  27. 27
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/listeners/ListenerHandle.java
  28. 165
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/listeners/ListenerList.java
  29. 36
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/ImmutableLiveObject.java
  30. 80
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveArrayList.java
  31. 40
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveBool.java
  32. 155
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveConcatList.java
  33. 73
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveEmptyList.java
  34. 24
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveInt.java
  35. 44
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveList.java
  36. 111
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveMappedList.java
  37. 24
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveObject.java
  38. 124
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/LivePrefixedList.java
  39. 35
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/SimpleLiveInt.java
  40. 39
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/live/SimpleLiveObject.java
  41. 163
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollBar.java
  42. 33
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollBarStyle.java
  43. 269
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollPane.java
  44. 27
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollPaneStyle.java
  45. 146
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingCanvas.java
  46. 49
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingFontMetrics.java
  47. 137
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingGraphicsContext.java
  48. 539
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingRoot.java
  49. 30
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingWindow.java
  50. 35
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/CollapsedArrow.java
  51. 39
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/ColoredCollapsedArrow.java
  52. 29
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/DTreeNode.java
  53. 221
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/DTreeView.java
  54. 52
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/DTreeViewStyle.java
  55. 35
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/ExpandedArrow.java
  56. 39
    0
      DrawableGui/src/main/java/org/openzen/drawablegui/tree/ExpandedColoredArrow.java
  57. 24
    0
      DrawableGuiIconConverter/build.gradle
  58. 275
    0
      DrawableGuiIconConverter/src/main/java/org/openzen/drawablegui/iconconverter/Main.java
  59. 19
    0
      IDE/build.gradle
  60. 35
    0
      IDE/src/main/java/org/openzen/zenscript/ide/Main.java
  61. 20
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/DevelopmentHost.java
  62. 18
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/IDELibrary.java
  63. 16
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/IDEModule.java
  64. 20
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/IDEPackage.java
  65. 21
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/IDESourceFile.java
  66. 39
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalLibrary.java
  67. 32
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalModule.java
  68. 57
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalPackage.java
  69. 53
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalProjectDevelopmentHost.java
  70. 54
    0
      IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalSourceFile.java
  71. 56
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEAspectBar.java
  72. 24
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEAspectBarControl.java
  73. 71
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEAspectToolbar.java
  74. 18
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEDockWindow.java
  75. 21
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEDrawable.java
  76. 35
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEStatusBar.java
  77. 18
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEStyle.java
  78. 34
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEWindow.java
  79. 42
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ChevronRight.java
  80. 31
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/CodeIcon.java
  81. 51
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableCodeIcon.java
  82. 47
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableFolderIcon.java
  83. 59
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableLibraryIcon.java
  84. 53
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableModuleIcon.java
  85. 60
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableProjectIcon.java
  86. 31
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/FolderIcon.java
  87. 32
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/LibraryIcon.java
  88. 32
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ModuleIcon.java
  89. 32
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ProjectIcon.java
  90. 52
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/StatusBarView.java
  91. 41
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/WindowView.java
  92. 53
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/aspectbar/AspectBarView.java
  93. 858
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditor.java
  94. 33
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditorStyle.java
  95. 111
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenReparser.java
  96. 53
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/LibraryTreeNode.java
  97. 44
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/ModuleTreeNode.java
  98. 57
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/PackageTreeNode.java
  99. 34
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/ProjectOverviewNode.java
  100. 0
    0
      IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/ProjectTreeNode.java

+ 24
- 0
DrawableGui/build.gradle View File

@@ -0,0 +1,24 @@
1
+// Note: "common.gradle" in the root project contains additional initialization
2
+//   for this project. This initialization is applied in the "build.gradle"
3
+//   of the root project.
4
+
5
+// NetBeans will automatically add "run" and "debug" tasks relying on the
6
+// "mainClass" property. You may however define the property prior executing
7
+// tasks by passing a "-PmainClass=<QUALIFIED_CLASS_NAME>" argument.
8
+//
9
+// Note however, that you may define your own "run" and "debug" task if you
10
+// prefer. In this case NetBeans will not add these tasks but you may rely on
11
+// your own implementation.
12
+if (!hasProperty('mainClass')) {
13
+    ext.mainClass = ''
14
+}
15
+
16
+dependencies {
17
+    // TODO: Add dependencies here
18
+    //   but note that JUnit should have already been added in parent.gradle.
19
+    //   By default, only the Maven Central Repository is specified in
20
+    //   parent.gradle.
21
+    //
22
+    // You can read more about how to add dependency here:
23
+    //   http://www.gradle.org/docs/current/userguide/dependency_management.html#sec:how_to_declare_your_dependencies
24
+}

+ 90
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/BaseComponentGroup.java View File

@@ -0,0 +1,90 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import java.awt.event.MouseWheelEvent;
9
+import java.util.function.Consumer;
10
+import java.util.function.Predicate;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public abstract class BaseComponentGroup implements DComponent {
17
+	private DComponent hovering = null;
18
+	
19
+	protected abstract void forEachChild(Consumer<DComponent> children);
20
+	
21
+	protected abstract DComponent findChild(Predicate<DComponent> predicate);
22
+	
23
+	@Override
24
+	public void onMouseEnter(DMouseEvent e) {
25
+		DComponent target = getComponent(e.x, e.y);
26
+		setHovering(target, e);
27
+	}
28
+	
29
+	@Override
30
+	public void onMouseExit(DMouseEvent e) {
31
+		setHovering(null, e);
32
+	}
33
+	
34
+	@Override
35
+	public void onMouseMove(DMouseEvent e) {
36
+		DComponent target = getComponent(e.x, e.y);
37
+		if (target == hovering) {
38
+			if (target != null)
39
+				target.onMouseMove(e);
40
+		} else {
41
+			setHovering(target, e);
42
+		}
43
+	}
44
+	
45
+	@Override
46
+	public void onMouseDrag(DMouseEvent e) {
47
+		if (hovering != null)
48
+			hovering.onMouseDrag(e);
49
+	}
50
+	
51
+	@Override
52
+	public void onMouseClick(DMouseEvent e) {
53
+		if (hovering != null)
54
+			hovering.onMouseClick(e);
55
+	}
56
+	
57
+	@Override
58
+	public void onMouseDown(DMouseEvent e) {
59
+		if (hovering != null)
60
+			hovering.onMouseDown(e);
61
+	}
62
+	
63
+	@Override
64
+	public void onMouseRelease(DMouseEvent e) {
65
+		if (hovering != null)
66
+			hovering.onMouseRelease(e);
67
+		onMouseMove(e);
68
+	}
69
+	
70
+	@Override
71
+	public void onMouseScroll(DMouseEvent e) {
72
+		if (hovering != null)
73
+			hovering.onMouseScroll(e);
74
+	}
75
+	
76
+	private void setHovering(DComponent component, DMouseEvent e) {
77
+		if (component == hovering)
78
+			return;
79
+		
80
+		if (hovering != null)
81
+			hovering.onMouseExit(e);
82
+		hovering = component;
83
+		if (hovering != null)
84
+			hovering.onMouseEnter(e);
85
+	}
86
+	
87
+	private DComponent getComponent(int x, int y) {
88
+		return findChild(child -> child.getBounds().contains(x, y));
89
+	}
90
+}

+ 74
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DCanvas.java View File

@@ -0,0 +1,74 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DCanvas {
13
+	void pushBounds(DIRectangle bounds);
14
+	
15
+	void popBounds();
16
+	
17
+	void pushOffset(int x, int y);
18
+	
19
+	void popOffset();
20
+	
21
+	DIRectangle getBounds();
22
+	
23
+	DDrawingContext getContext();
24
+	
25
+	void drawText(DFont font, int color, float x, float y, String text);
26
+	
27
+	default float measureTextLength(DFont font, String text) {
28
+		return measureTextLength(font, text, 0, text.length());
29
+	}
30
+	
31
+	float measureTextLength(DFont font, String text, int offset, int length);
32
+	
33
+	/**
34
+	 * Strokes a given path.
35
+	 * 
36
+	 * @param path path to be stroked
37
+	 * @param transform path transform
38
+	 * @param color path color
39
+	 * @param lineWidth path line width
40
+	 */
41
+	void strokePath(DPath path, DTransform2D transform, int color, float lineWidth);
42
+	
43
+	/**
44
+	 * Fills a given path.
45
+	 * 
46
+	 * @param path path to be filled
47
+	 * @param transform 
48
+	 * @param color
49
+	 */
50
+	void fillPath(DPath path, DTransform2D transform, int color);
51
+	
52
+	/**
53
+	 * Draws the shadow for a given path.
54
+	 * 
55
+	 * @param path
56
+	 * @param transform
57
+	 * @param color
58
+	 * @param dx
59
+	 * @param dy
60
+	 * @param radius 
61
+	 */
62
+	void shadowPath(DPath path, DTransform2D transform, int color, float dx, float dy, float radius);
63
+	
64
+	/**
65
+	 * Fills a rectangle.
66
+	 * 
67
+	 * @param x
68
+	 * @param y
69
+	 * @param width
70
+	 * @param height
71
+	 * @param color 
72
+	 */
73
+	void fillRectangle(int x, int y, int width, int height, int color);
74
+}

+ 21
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DColorableIcon.java View File

@@ -0,0 +1,21 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DTransform2D;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public interface DColorableIcon {
16
+	void draw(DCanvas canvas, DTransform2D transform, int color);
17
+	
18
+	float getNominalWidth();
19
+	
20
+	float getNominalHeight();
21
+}

+ 50
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DComponent.java View File

@@ -0,0 +1,50 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import org.openzen.drawablegui.live.LiveObject;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface DComponent {
15
+	void setContext(DDrawingContext context);
16
+	
17
+	LiveObject<DDimensionPreferences> getDimensionPreferences();
18
+	
19
+	DRectangle getBounds();
20
+	
21
+	void setBounds(DRectangle bounds);
22
+	
23
+	void paint(DCanvas canvas);
24
+	
25
+	default void onMouseEnter(DMouseEvent e) {}
26
+	
27
+	default void onMouseExit(DMouseEvent e) {}
28
+	
29
+	default void onMouseMove(DMouseEvent e) {}
30
+	
31
+	default void onMouseDrag(DMouseEvent e) {}
32
+	
33
+	default void onMouseClick(DMouseEvent e) {}
34
+	
35
+	default void onMouseDown(DMouseEvent e) {}
36
+	
37
+	default void onMouseRelease(DMouseEvent e) {}
38
+	
39
+	default void onMouseScroll(DMouseEvent e) {}
40
+	
41
+	default void onFocusLost() {}
42
+	
43
+	default void onFocusGained() {}
44
+	
45
+	default void onKeyTyped(DKeyEvent e) {}
46
+	
47
+	default void onKeyPressed(DKeyEvent e) {}
48
+	
49
+	default void onKeyReleased(DKeyEvent e) {}
50
+}

+ 39
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DDimensionPreferences.java View File

@@ -0,0 +1,39 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class DDimensionPreferences {
13
+	public static final DDimensionPreferences EMPTY = new DDimensionPreferences(0, 0);
14
+	
15
+	public final int minimumWidth;
16
+	public final int minimumHeight;
17
+	public final int preferredWidth;
18
+	public final int preferredHeight;
19
+	public final int maximumWidth;
20
+	public final int maximumHeight;
21
+	
22
+	public DDimensionPreferences(int preferredWidth, int preferredHeight) {
23
+		this.minimumWidth = 0;
24
+		this.minimumHeight = 0;
25
+		this.preferredWidth = preferredWidth;
26
+		this.preferredHeight = preferredHeight;
27
+		this.maximumWidth = 1000000;
28
+		this.maximumHeight = 1000000;
29
+	}
30
+	
31
+	public DDimensionPreferences(int minimumWidth, int minimumHeight, int preferredWidth, int preferredHeight, int maximumWidth, int maximumHeight) {
32
+		this.minimumWidth = minimumWidth;
33
+		this.minimumHeight = minimumHeight;
34
+		this.preferredWidth = preferredWidth;
35
+		this.preferredHeight = preferredHeight;
36
+		this.maximumWidth = maximumWidth;
37
+		this.maximumHeight = maximumHeight;
38
+	}
39
+}

+ 18
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DDrawable.java View File

@@ -0,0 +1,18 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DDrawable {
13
+	void draw(DCanvas canvas, DTransform2D transform);
14
+	
15
+	float getNominalWidth();
16
+	
17
+	float getNominalHeight();
18
+}

+ 35
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DDrawingContext.java View File

@@ -0,0 +1,35 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DDrawingContext {
13
+	float getScale();
14
+	
15
+	void repaint(int x, int y, int width, int height);
16
+	
17
+	void setCursor(Cursor cursor);
18
+	
19
+	void focus(DComponent component);
20
+	
21
+	void scrollInView(int x, int y, int width, int height);
22
+	
23
+	DFontMetrics getFontMetrics(DFont font);
24
+	
25
+	enum Cursor {
26
+		NORMAL,
27
+		HAND,
28
+		MOVE,
29
+		TEXT,
30
+		E_RESIZE,
31
+		S_RESIZE,
32
+		NE_RESIZE,
33
+		NW_RESIZE,
34
+	}
35
+}

+ 47
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DEmptyView.java View File

@@ -0,0 +1,47 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import org.openzen.drawablegui.live.ImmutableLiveObject;
9
+import org.openzen.drawablegui.live.LiveObject;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class DEmptyView implements DComponent {
16
+	public static final DEmptyView INSTANCE = new DEmptyView();
17
+	
18
+	private static final LiveObject<DDimensionPreferences> DIMENSION = new ImmutableLiveObject(new DDimensionPreferences(0, 0));
19
+	private static final DRectangle NO_BOUNDS = new DRectangle(0, 0, 0, 0);
20
+	
21
+	private DEmptyView() {}
22
+
23
+	@Override
24
+	public void setContext(DDrawingContext context) {
25
+		
26
+	}
27
+	
28
+	@Override
29
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
30
+		return DIMENSION;
31
+	}
32
+	
33
+	@Override
34
+	public DRectangle getBounds() {
35
+		return NO_BOUNDS;
36
+	}
37
+
38
+	@Override
39
+	public void setBounds(DRectangle bounds) {
40
+		
41
+	}
42
+
43
+	@Override
44
+	public void paint(DCanvas canvas) {
45
+		
46
+	}
47
+}

+ 70
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DFont.java View File

@@ -0,0 +1,70 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import java.util.Objects;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public class DFont {
15
+	public final DFontFamily family;
16
+	public final boolean bold;
17
+	public final boolean italic;
18
+	public final boolean underline;
19
+	public final int size;
20
+	
21
+	public Object cached;
22
+	
23
+	public DFont(DFontFamily family, boolean bold, boolean italic, boolean underline, int size) {
24
+		this.family = family;
25
+		this.bold = bold;
26
+		this.italic = italic;
27
+		this.underline = underline;
28
+		this.size = size;
29
+	}
30
+
31
+	@Override
32
+	public int hashCode() {
33
+		int hash = 3;
34
+		hash = 29 * hash + Objects.hashCode(this.family);
35
+		hash = 29 * hash + (this.bold ? 1 : 0);
36
+		hash = 29 * hash + (this.italic ? 1 : 0);
37
+		hash = 29 * hash + (this.underline ? 1 : 0);
38
+		hash = 29 * hash + Float.floatToIntBits(this.size);
39
+		return hash;
40
+	}
41
+
42
+	@Override
43
+	public boolean equals(Object obj) {
44
+		if (this == obj) {
45
+			return true;
46
+		}
47
+		if (obj == null) {
48
+			return false;
49
+		}
50
+		if (getClass() != obj.getClass()) {
51
+			return false;
52
+		}
53
+		final DFont other = (DFont) obj;
54
+		if (this.bold != other.bold) {
55
+			return false;
56
+		}
57
+		if (this.italic != other.italic) {
58
+			return false;
59
+		}
60
+		if (this.underline != other.underline) {
61
+			return false;
62
+		}
63
+		if (this.size != other.size)
64
+			return false;
65
+		if (this.family != other.family) {
66
+			return false;
67
+		}
68
+		return true;
69
+	}
70
+}

+ 15
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DFontFamily.java View File

@@ -0,0 +1,15 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public enum DFontFamily {
13
+	UI,
14
+	CODE
15
+}

+ 22
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DFontMetrics.java View File

@@ -0,0 +1,22 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DFontMetrics {
13
+	int getAscent();
14
+	
15
+	int getDescent();
16
+	
17
+	int getLeading();
18
+	
19
+	int getWidth(String str);
20
+	
21
+	int getWidth(String str, int offset, int length);
22
+}

+ 24
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DIRectangle.java View File

@@ -0,0 +1,24 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class DIRectangle {
13
+	public final int x;
14
+	public final int y;
15
+	public final int width;
16
+	public final int height;
17
+	
18
+	public DIRectangle(int x, int y, int width, int height) {
19
+		this.x = x;
20
+		this.y = y;
21
+		this.width = width;
22
+		this.height = height;
23
+	}
24
+}

+ 211
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DKeyEvent.java View File

@@ -0,0 +1,211 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class DKeyEvent {
13
+	public static final char CHAR_UNDEFINED = 65535;
14
+	
15
+	public static final int BUTTON1 = 1;
16
+	public static final int BUTTON2 = 2;
17
+	public static final int BUTTON3 = 4;
18
+	public static final int ALT = 256;
19
+	public static final int CTRL = 512;
20
+	public static final int SHIFT = 1024;
21
+	public static final int META = 2048;
22
+	public static final int ALT_GRAPH = 4096;
23
+	
24
+	public final char character;
25
+	public final KeyCode keyCode;
26
+	public final int modifiers;
27
+	
28
+	public DKeyEvent(char character, KeyCode keyCode, int modifiers) {
29
+		this.character = character;
30
+		this.keyCode = keyCode;
31
+		this.modifiers = modifiers;
32
+	}
33
+	
34
+	public boolean has(int modifiers) {
35
+		return (this.modifiers & modifiers) == modifiers;
36
+	}
37
+	
38
+	public enum KeyCode {
39
+		ENTER,
40
+		BACKSPACE,
41
+		TAB,
42
+		CANCEL,
43
+		CLEAR,
44
+		SHIFT,
45
+		CONTROL,
46
+		ALT,
47
+		PAUSE,
48
+		CAPS_LOCK,
49
+		ESCAPE,
50
+		SPACE,
51
+		PAGE_UP,
52
+		PAGE_DOWN,
53
+		END,
54
+		HOME,
55
+		LEFT,
56
+		UP,
57
+		RIGHT,
58
+		DOWN,
59
+		COMMA,
60
+		MINUS,
61
+		PERIOD,
62
+		SLASH,
63
+		NUM0,
64
+		NUM1,
65
+		NUM2,
66
+		NUM3,
67
+		NUM4,
68
+		NUM5,
69
+		NUM6,
70
+		NUM7,
71
+		NUM8,
72
+		NUM9,
73
+		SEMICOLON,
74
+		EQUALS,
75
+		A,
76
+		B,
77
+		C,
78
+		D,
79
+		E,
80
+		F,
81
+		G,
82
+		H,
83
+		I,
84
+		J,
85
+		K,
86
+		L,
87
+		M,
88
+		N,
89
+		O,
90
+		P,
91
+		Q,
92
+		R,
93
+		S,
94
+		T,
95
+		U,
96
+		V,
97
+		W,
98
+		X,
99
+		Y,
100
+		Z,
101
+		OPEN_BRACKET,
102
+		BACKSLASH,
103
+		CLOSE_BRACKET,
104
+		NUMPAD0,
105
+		NUMPAD1,
106
+		NUMPAD2,
107
+		NUMPAD3,
108
+		NUMPAD4,
109
+		NUMPAD5,
110
+		NUMPAD6,
111
+		NUMPAD7,
112
+		NUMPAD8,
113
+		NUMPAD9,
114
+		MULTIPLY,
115
+		ADD,
116
+		SEPARATOR,
117
+		SUBTRACT,
118
+		DECIMAL,
119
+		DIVIDE,
120
+		DELETE,
121
+		NUM_LOCK,
122
+		SCROLL_LOCK,
123
+		F1,
124
+		F2,
125
+		F3,
126
+		F4,
127
+		F5,
128
+		F6,
129
+		F7,
130
+		F8,
131
+		F9,
132
+		F10,
133
+		F11,
134
+		F12,
135
+		F13,
136
+		F14,
137
+		F15,
138
+		F16,
139
+		F17,
140
+		F18,
141
+		F19,
142
+		F20,
143
+		F21,
144
+		F22,
145
+		F23,
146
+		F24,
147
+		PRINTSCREEN,
148
+		INSERT,
149
+		HELP,
150
+		META,
151
+		BACKQUOTE,
152
+		QUOTE,
153
+		KEYPAD_UP,
154
+		KEYPAD_DOWN,
155
+		KEYPAD_LEFT,
156
+		KEYPAD_RIGHT,
157
+		AMPERSAND,
158
+		ASTERISK,
159
+		QUOTEDBL,
160
+		LESS,
161
+		GREATER,
162
+		BRACELEFT,
163
+		BRACERIGHT,
164
+		AT,
165
+		COLON,
166
+		CIRCUMFLEX,
167
+		DOLLAR,
168
+		EURO_SIGN,
169
+		EXCLAMATION_MARK,
170
+		INVERTED_EXCLAMATION_MARK,
171
+		LEFT_PARENTHESIS,
172
+		NUMBER_SIGN,
173
+		PLUS,
174
+		RIGHT_PARENTHESIS,
175
+		UNDERSCORE,
176
+		WINDOWS,
177
+		CONTEXT_MENU,
178
+		FINAL,
179
+		CONVERT,
180
+		NONCONVERT,
181
+		ACCEPT,
182
+		KANA,
183
+		KANJI,
184
+		ALPHANUMERIC,
185
+		KATAKANA,
186
+		HIRAGANA,
187
+		FULL_WIDTH,
188
+		HALF_WIDTH,
189
+		ROMAN_CHARACTERS,
190
+		ALL_CANDIDATES,
191
+		PREVIOUS_CANDIDATE,
192
+		CODE_INPUT,
193
+		JAPANESE_KATAKANA,
194
+		JAPANESE_HIRAGANA,
195
+		JAPANESE_ROMAN,
196
+		KANA_LOCK,
197
+		INPUT_METHOD_ON_OFF,
198
+		CUT,
199
+		COPY,
200
+		PASTE,
201
+		UNDO,
202
+		AGAIN,
203
+		FIND,
204
+		PROPS,
205
+		STOP,
206
+		COMPOSE,
207
+		ALT_GRAPH,
208
+		BEGIN,
209
+		UNKNOWN
210
+	}
211
+}

+ 51
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DMouseEvent.java View File

@@ -0,0 +1,51 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class DMouseEvent {
13
+	public static final int BUTTON1 = 1;
14
+	public static final int BUTTON2 = 2;
15
+	public static final int BUTTON3 = 4;
16
+	public static final int ALT = 256;
17
+	public static final int CTRL = 512;
18
+	public static final int SHIFT = 1024;
19
+	public static final int META = 2048;
20
+	public static final int ALT_GRAPH = 4096;
21
+	
22
+	public final int x;
23
+	public final int y;
24
+	public final int modifiers;
25
+	public final int deltaZ;
26
+	public final int clickCount;
27
+	
28
+	public DMouseEvent(int x, int y, int modifiers, int deltaZ, int clickCount) {
29
+		this.x = x;
30
+		this.y = y;
31
+		this.modifiers = modifiers;
32
+		this.deltaZ = deltaZ;
33
+		this.clickCount = clickCount;
34
+	}
35
+	
36
+	public boolean isSingleClick() {
37
+		return clickCount == 1;
38
+	}
39
+	
40
+	public boolean isDoubleClick() {
41
+		return clickCount == 2;
42
+	}
43
+	
44
+	public boolean isTripleClick() {
45
+		return clickCount == 3;
46
+	}
47
+	
48
+	public boolean has(int modifiers) {
49
+		return (this.modifiers & modifiers) == modifiers;
50
+	}
51
+}

+ 14
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DPath.java View File

@@ -0,0 +1,14 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DPath {
13
+	void trace(DPathTracer tracer);
14
+}

+ 14
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DPathCommand.java View File

@@ -0,0 +1,14 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DPathCommand {
13
+	public void accept(DPathTracer tracer);
14
+}

+ 22
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DPathTracer.java View File

@@ -0,0 +1,22 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DPathTracer {
13
+	public void moveTo(float x, float y);
14
+	
15
+	public void lineTo(float x, float y);
16
+	
17
+	public void bezierCubic(float x1, float y1, float x2, float y2, float x3, float y3);
18
+	
19
+	public void bezierQuadratic(float x1, float y1, float x2, float y2);
20
+	
21
+	public void close();
22
+}

+ 14
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DPreparedPath.java View File

@@ -0,0 +1,14 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DPreparedPath {
13
+	
14
+}

+ 29
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DRectangle.java View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public final class DRectangle {
13
+	public final int x;
14
+	public final int y;
15
+	public final int width;
16
+	public final int height;
17
+	
18
+	public DRectangle(int x, int y, int width, int height) {
19
+		this.x = x;
20
+		this.y = y;
21
+		this.width = width;
22
+		this.height = height;
23
+	}
24
+	
25
+	public boolean contains(int x, int y) {
26
+		return x >= this.x && x < (this.x + this.width)
27
+				&& y >= this.y && y < (this.y + this.height);
28
+	}
29
+}

+ 240
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DSideLayout.java View File

@@ -0,0 +1,240 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import java.io.Closeable;
9
+import java.util.ArrayList;
10
+import java.util.List;
11
+import java.util.function.Consumer;
12
+import java.util.function.Predicate;
13
+import org.openzen.drawablegui.listeners.ListenerHandle;
14
+import org.openzen.drawablegui.live.LiveObject;
15
+import org.openzen.drawablegui.live.SimpleLiveObject;
16
+
17
+/**
18
+ *
19
+ * @author Hoofdgebruiker
20
+ */
21
+public class DSideLayout extends BaseComponentGroup {
22
+	private DComponent main;
23
+	private final List<SideComponent> sides = new ArrayList<>();
24
+	private final LiveObject<DDimensionPreferences> dimensionPreferences = new SimpleLiveObject<>(DDimensionPreferences.EMPTY);
25
+	
26
+	private DDrawingContext context;
27
+	private DRectangle bounds;
28
+	
29
+	public DSideLayout(DComponent main) {
30
+		this.main = main;
31
+	}
32
+	
33
+	public void add(Side side, DComponent component) {
34
+		if (context != null)
35
+			component.setContext(context);
36
+		
37
+		sides.add(new SideComponent(side, component));
38
+	}
39
+	
40
+	public void setMain(DComponent component) {
41
+		this.main = component;
42
+		main.setContext(context);
43
+		setBounds(bounds);
44
+		context.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
45
+	}
46
+
47
+	@Override
48
+	public void setContext(DDrawingContext context) {
49
+		this.context = context;
50
+		main.setContext(context);
51
+		for (SideComponent side : sides)
52
+			side.component.setContext(context);
53
+	}
54
+	
55
+	@Override
56
+	public DRectangle getBounds() {
57
+		return bounds;
58
+	}
59
+
60
+	@Override
61
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
62
+		return dimensionPreferences;
63
+	}
64
+
65
+	@Override
66
+	public void setBounds(DRectangle bounds) {
67
+		this.bounds = bounds;
68
+		
69
+		int left = bounds.x;
70
+		int right = bounds.x + bounds.width;
71
+		int top = bounds.y;
72
+		int bottom = bounds.y + bounds.height;
73
+		for (int i = sides.size() - 1; i >= 0; i--) {
74
+			SideComponent side = sides.get(i);
75
+			DDimensionPreferences preferences = side.component.getDimensionPreferences().getValue();
76
+			
77
+			switch (side.side) {
78
+				case TOP: {
79
+					int componentWidth = right - left;
80
+					int componentHeight = preferences.preferredHeight;
81
+					int componentX = left;
82
+					int componentY = top;
83
+					
84
+					if (componentWidth > preferences.maximumWidth) {
85
+						componentX += (componentWidth - preferences.maximumWidth) / 2;
86
+						componentWidth = preferences.maximumWidth;
87
+					}
88
+					
89
+					side.component.setBounds(new DRectangle(componentX, componentY, componentWidth, componentHeight));
90
+					top += componentHeight;
91
+					break;
92
+				}
93
+				case BOTTOM: {
94
+					int componentWidth = right - left;
95
+					int componentHeight = preferences.preferredHeight;
96
+					bottom -= componentHeight;
97
+					int componentX = left;
98
+					int componentY = bottom;
99
+					
100
+					if (componentWidth > preferences.maximumWidth) {
101
+						componentX += (componentWidth - preferences.maximumWidth) / 2;
102
+						componentWidth = preferences.maximumWidth;
103
+					}
104
+					
105
+					side.component.setBounds(new DRectangle(componentX, componentY, componentWidth, componentHeight));
106
+					break;
107
+				}
108
+				case LEFT: {
109
+					int componentWidth = preferences.preferredWidth;
110
+					int componentHeight = bottom - top;
111
+					int componentX = left;
112
+					int componentY = top;
113
+					
114
+					if (componentHeight > preferences.maximumHeight) {
115
+						componentY += (componentHeight - preferences.maximumHeight) / 2;
116
+						componentHeight = preferences.maximumHeight;
117
+					}
118
+					
119
+					side.component.setBounds(new DRectangle(componentX, componentY, componentWidth, componentHeight));
120
+					left += componentWidth;
121
+					break;
122
+				}
123
+				case RIGHT: {
124
+					int componentWidth = preferences.preferredWidth;
125
+					int componentHeight = bottom - top;
126
+					right -= componentWidth;
127
+					int componentX = right;
128
+					int componentY = top;
129
+					
130
+					if (componentHeight > preferences.maximumHeight) {
131
+						componentY += (componentHeight - preferences.maximumHeight) / 2;
132
+						componentHeight = preferences.maximumHeight;
133
+					}
134
+					
135
+					side.component.setBounds(new DRectangle(componentX, componentY, componentWidth, componentHeight));
136
+					break;
137
+				}
138
+			}
139
+		}
140
+		
141
+		main.setBounds(new DRectangle(left, top, right - left, bottom - top));
142
+	}
143
+
144
+	@Override
145
+	public void paint(DCanvas canvas) {
146
+		main.paint(canvas);
147
+		
148
+		for (SideComponent component : sides)
149
+			component.component.paint(canvas);
150
+	}
151
+	
152
+	private void recalculateSize() {
153
+		DDimensionPreferences mainPreferences = main.getDimensionPreferences().getValue();
154
+		int minimumWidth = mainPreferences.minimumWidth;
155
+		int minimumHeight = mainPreferences.minimumHeight;
156
+		int preferredWidth = mainPreferences.preferredWidth;
157
+		int preferredHeight = mainPreferences.preferredHeight;
158
+		int maximumWidth = mainPreferences.maximumWidth;
159
+		int maximumHeight = mainPreferences.maximumHeight;
160
+		
161
+		for (SideComponent side : sides) {
162
+			DDimensionPreferences sidePreferences = side.component.getDimensionPreferences().getValue();
163
+			switch (side.side) {
164
+				case LEFT:
165
+				case RIGHT:
166
+					minimumWidth += sidePreferences.preferredWidth;
167
+					preferredWidth += sidePreferences.preferredWidth;
168
+					maximumWidth += sidePreferences.preferredWidth;
169
+					
170
+					minimumHeight = Math.max(minimumHeight, sidePreferences.minimumHeight);
171
+					preferredHeight = Math.max(maximumHeight, sidePreferences.preferredHeight);
172
+					break;
173
+				case BOTTOM:
174
+				case TOP:
175
+					minimumHeight += sidePreferences.preferredHeight;
176
+					preferredHeight += sidePreferences.preferredHeight;
177
+					maximumHeight += sidePreferences.preferredHeight;
178
+					
179
+					minimumWidth = Math.max(minimumWidth, sidePreferences.minimumWidth);
180
+					preferredWidth = Math.max(preferredWidth, sidePreferences.preferredWidth);
181
+					break;
182
+			}
183
+		}
184
+		
185
+		dimensionPreferences.setValue(new DDimensionPreferences(
186
+				minimumWidth,
187
+				minimumHeight,
188
+				preferredWidth,
189
+				preferredHeight,
190
+				maximumWidth,
191
+				maximumHeight));
192
+	}
193
+
194
+	@Override
195
+	protected void forEachChild(Consumer<DComponent> children) {
196
+		children.accept(main);
197
+		for (SideComponent side : sides)
198
+			children.accept(side.component);
199
+	}
200
+	
201
+	@Override
202
+	public DComponent findChild(Predicate<DComponent> predicate) {
203
+		if (predicate.test(main))
204
+			return main;
205
+		for (SideComponent side : sides)
206
+			if (predicate.test(side.component))
207
+				return side.component;
208
+		
209
+		return null;
210
+	}
211
+	
212
+	public class SideComponent implements Closeable, LiveObject.Listener<DDimensionPreferences> {
213
+		public final Side side;
214
+		public final DComponent component;
215
+		public final ListenerHandle<LiveObject.Listener<DDimensionPreferences>> listenerHandle;
216
+
217
+		public SideComponent(Side side, DComponent component) {
218
+			this.side = side;
219
+			this.component = component;
220
+			listenerHandle = component.getDimensionPreferences().addListener(this);
221
+		}
222
+		
223
+		@Override
224
+		public void close() {
225
+			listenerHandle.close();
226
+		}
227
+
228
+		@Override
229
+		public void onUpdated(DDimensionPreferences oldValue, DDimensionPreferences newValue) {
230
+			recalculateSize();
231
+		}
232
+	}
233
+	
234
+	public enum Side {
235
+		LEFT,
236
+		RIGHT,
237
+		TOP,
238
+		BOTTOM
239
+	}
240
+}

+ 38
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DTransform2D.java View File

@@ -0,0 +1,38 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public final class DTransform2D {
13
+	public static final DTransform2D IDENTITY = new DTransform2D(1, 0, 0, 1, 0, 0);
14
+	
15
+	public static DTransform2D translate(float x, float y) {
16
+		return new DTransform2D(1, 0, 0, 1, x, y);
17
+	}
18
+	
19
+	public static DTransform2D scale(float scale) {
20
+		return new DTransform2D(scale, 0, 0, scale, 0, 0);
21
+	}
22
+	
23
+	public final float xx;
24
+	public final float xy;
25
+	public final float yx;
26
+	public final float yy;
27
+	public final float dx;
28
+	public final float dy;
29
+	
30
+	public DTransform2D(float xx, float xy, float yx, float yy, float dx, float dy) {
31
+		this.xx = xx;
32
+		this.xy = xy;
33
+		this.yx = yx;
34
+		this.yy = yy;
35
+		this.dx = dx;
36
+		this.dy = dy;
37
+	}
38
+}

+ 25
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/border/DBorder.java View File

@@ -0,0 +1,25 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.border;
7
+
8
+import org.openzen.drawablegui.DRectangle;
9
+import org.openzen.drawablegui.DCanvas;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public interface DBorder {
16
+	public void paint(DCanvas canvas, DRectangle bounds);
17
+	
18
+	public int getPaddingLeft();
19
+	
20
+	public int getPaddingRight();
21
+	
22
+	public int getPaddingTop();
23
+	
24
+	public int getPaddingBottom();
25
+}

+ 42
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/border/DEmptyBorder.java View File

@@ -0,0 +1,42 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.border;
7
+
8
+import org.openzen.drawablegui.DRectangle;
9
+import org.openzen.drawablegui.DCanvas;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class DEmptyBorder implements DBorder {
16
+	public static final DEmptyBorder INSTANCE = new DEmptyBorder();
17
+
18
+	@Override
19
+	public void paint(DCanvas canvas, DRectangle bounds) {
20
+		
21
+	}
22
+
23
+	@Override
24
+	public int getPaddingLeft() {
25
+		return 0;
26
+	}
27
+
28
+	@Override
29
+	public int getPaddingRight() {
30
+		return 0;
31
+	}
32
+
33
+	@Override
34
+	public int getPaddingTop() {
35
+		return 0;
36
+	}
37
+
38
+	@Override
39
+	public int getPaddingBottom() {
40
+		return 0;
41
+	}
42
+}

+ 55
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/border/DLineBorder.java View File

@@ -0,0 +1,55 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.border;
7
+
8
+import org.openzen.drawablegui.DRectangle;
9
+import org.openzen.drawablegui.DCanvas;
10
+import org.openzen.drawablegui.DTransform2D;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class DLineBorder implements DBorder {
17
+	private final int color;
18
+	private final int borderWidth;
19
+	
20
+	public DLineBorder(int color, int borderWidth) {
21
+		this.color = color;
22
+		this.borderWidth = borderWidth;
23
+	}
24
+
25
+	@Override
26
+	public void paint(DCanvas canvas, DRectangle bounds) {
27
+		canvas.strokePath(tracer -> {
28
+				tracer.moveTo(bounds.x, bounds.y);
29
+				tracer.lineTo(bounds.x + bounds.width - borderWidth, bounds.y);
30
+				tracer.lineTo(bounds.x + bounds.width - borderWidth, bounds.y + bounds.height - borderWidth);
31
+				tracer.lineTo(bounds.x, bounds.y + bounds.height - borderWidth);
32
+				tracer.close();
33
+			}, DTransform2D.IDENTITY, color, borderWidth);
34
+	}
35
+
36
+	@Override
37
+	public int getPaddingLeft() {
38
+		return borderWidth;
39
+	}
40
+
41
+	@Override
42
+	public int getPaddingRight() {
43
+		return borderWidth;
44
+	}
45
+
46
+	@Override
47
+	public int getPaddingTop() {
48
+		return borderWidth;
49
+	}
50
+
51
+	@Override
52
+	public int getPaddingBottom() {
53
+		return borderWidth;
54
+	}
55
+}

+ 28
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/listeners/DummyListenerHandle.java View File

@@ -0,0 +1,28 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.listeners;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class DummyListenerHandle<T> implements ListenerHandle<T> {
13
+	private final T listener;
14
+
15
+	public DummyListenerHandle(T listener) {
16
+		this.listener = listener;
17
+	}
18
+
19
+	@Override
20
+	public T getListener() {
21
+		return listener;
22
+	}
23
+
24
+	@Override
25
+	public void close() {
26
+
27
+	}
28
+}

+ 27
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/listeners/ListenerHandle.java View File

@@ -0,0 +1,27 @@
1
+/* Licensed under GPLv3 - https://opensource.org/licenses/GPL-3.0 */
2
+package org.openzen.drawablegui.listeners;
3
+
4
+import java.io.Closeable;
5
+
6
+/**
7
+ * An EvenListenerHandle is returned when an EventListener is registered in an
8
+ * EventListenerList, which can then be used to unregister the event listener.
9
+ *
10
+ * @param <T> Event type
11
+ * @author Stan Hebben
12
+ */
13
+public interface ListenerHandle<T> extends Closeable
14
+{
15
+	/**
16
+	 * Gets this handle's listener.
17
+	 *
18
+	 * @return listener
19
+	 */
20
+	public T getListener();
21
+
22
+	/**
23
+	 * Closes (unregisters) the event listener from the list it was registered to.
24
+	 */
25
+	@Override
26
+	public void close();
27
+}

+ 165
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/listeners/ListenerList.java View File

@@ -0,0 +1,165 @@
1
+/* Licensed under GPLv3 - https://opensource.org/licenses/GPL-3.0 */
2
+package org.openzen.drawablegui.listeners;
3
+
4
+import java.util.function.Consumer;
5
+
6
+/**
7
+ * Implements a list of event listeners. This class is thread-safe and listeners
8
+ * can be added or removed concurrently, no external locking is ever needed.
9
+ * Also, it's very lightweight.
10
+ *
11
+ * @param <T> event type
12
+ * @author Stan Hebben
13
+ */
14
+public class ListenerList<T>
15
+{
16
+    public static final int PRIORITY_HIGH = 100;
17
+    public static final int PRIORITY_DEFAULT = 0;
18
+    public static final int PRIORITY_LOW = -100;
19
+	
20
+	// implements a linked list of nodes
21
+	protected volatile EventListenerNode first = null;
22
+	protected EventListenerNode last = null;
23
+
24
+	/**
25
+	 * Adds an IEventListener to the list.
26
+	 *
27
+	 * @param listener event listener
28
+	 * @return event listener's handle
29
+	 */
30
+	public ListenerHandle<T> add(T listener)
31
+	{
32
+        return add(listener, PRIORITY_DEFAULT);
33
+    }
34
+
35
+    public ListenerHandle<T> add(T listener, int priority)
36
+	{
37
+		EventListenerNode node = new EventListenerNode(listener, priority);
38
+
39
+		synchronized (this) {
40
+			if (first == null) {
41
+				first = last = node;
42
+			} else {
43
+                // prioritized list: where to insert?
44
+                EventListenerNode previousNode = last;
45
+                while (previousNode != null && priority > previousNode.priority) {
46
+                    previousNode = previousNode.prev;
47
+                }
48
+
49
+                if (previousNode == null) {
50
+                    node.next = first;
51
+                    first.prev = previousNode;
52
+                    first = node;
53
+                } else {
54
+                    if (previousNode.next == null) {
55
+                        last = node;
56
+                    } else {
57
+                        previousNode.next.prev = node;
58
+                    }
59
+
60
+                    previousNode.next = node;
61
+                    node.prev = previousNode;
62
+                }
63
+            }
64
+		}
65
+
66
+		return node;
67
+	}
68
+
69
+	/**
70
+	 * Removes an IEventListener from the list.
71
+	 *
72
+	 * @param listener listener to be removed
73
+	 * @return true if the listener was removed, false it it wasn't there
74
+	 */
75
+	public synchronized boolean remove(T listener)
76
+	{
77
+		EventListenerNode current = first;
78
+
79
+		while (current != null) {
80
+			if (current.listener == listener) {
81
+				current.close();
82
+				return true;
83
+			}
84
+
85
+            current = current.next;
86
+		}
87
+
88
+		return false;
89
+	}
90
+	
91
+	/**
92
+	 * Clears this listener list.
93
+	 */
94
+	public synchronized void clear()
95
+	{
96
+		first = last = null;
97
+	}
98
+	
99
+	/**
100
+	 * Invokes the given consumer for every listener in the list.
101
+	 * 
102
+	 * @param consumer consumer to be called for each listener
103
+	 */
104
+	public void accept(Consumer<T> consumer)
105
+	{
106
+		EventListenerNode current = first;
107
+		
108
+		while (current != null) {
109
+			consumer.accept(current.listener);
110
+			current = current.next;
111
+		}
112
+	}
113
+
114
+	/**
115
+	 * Checks if there are any listeners in this list.
116
+	 *
117
+	 * @return true if empty
118
+	 */
119
+	public boolean isEmpty()
120
+	{
121
+		return first == null;
122
+	}
123
+
124
+	// #######################
125
+	// ### Private classes ###
126
+	// #######################
127
+
128
+	protected class EventListenerNode implements ListenerHandle<T>
129
+	{
130
+		protected final T listener;
131
+        protected final int priority;
132
+		protected EventListenerNode next = null;
133
+		protected EventListenerNode prev = null;
134
+
135
+		public EventListenerNode(T handler, int priority)
136
+		{
137
+			this.listener = handler;
138
+            this.priority = priority;
139
+		}
140
+
141
+		@Override
142
+		public T getListener()
143
+		{
144
+			return listener;
145
+		}
146
+
147
+		@Override
148
+		public void close()
149
+		{
150
+			synchronized (ListenerList.this) {
151
+				if (prev == null) {
152
+					first = next;
153
+				} else {
154
+					prev.next = next;
155
+				}
156
+
157
+				if (next == null) {
158
+					last = prev;
159
+				} else {
160
+					next.prev = prev;
161
+				}
162
+			}
163
+		}
164
+	}
165
+}

+ 36
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/ImmutableLiveObject.java View File

@@ -0,0 +1,36 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import org.openzen.drawablegui.listeners.DummyListenerHandle;
9
+import org.openzen.drawablegui.listeners.ListenerHandle;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class ImmutableLiveObject<T> implements LiveObject<T> {
16
+	private final T value;
17
+	
18
+	public ImmutableLiveObject(T value) {
19
+		this.value = value;
20
+	}
21
+	
22
+	@Override
23
+	public T getValue() {
24
+		return value;
25
+	}
26
+
27
+	@Override
28
+	public void setValue(T value) {
29
+		throw new UnsupportedOperationException("Immutable value");
30
+	}
31
+
32
+	@Override
33
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener) {
34
+		return new DummyListenerHandle<>(listener);
35
+	}
36
+}

+ 80
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveArrayList.java View File

@@ -0,0 +1,80 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import java.util.ArrayList;
9
+import java.util.Iterator;
10
+import java.util.List;
11
+import org.openzen.drawablegui.listeners.ListenerHandle;
12
+import org.openzen.drawablegui.listeners.ListenerList;
13
+
14
+/**
15
+ *
16
+ * @author Hoofdgebruiker
17
+ */
18
+public class LiveArrayList<T> implements LiveList<T> {
19
+	private final List<T> values = new ArrayList<>();
20
+	private final ListenerList<Listener<T>> listeners = new ListenerList<>();
21
+
22
+	@Override
23
+	public void add(T value) {
24
+		int index = values.size();
25
+		values.add(value);
26
+		listeners.accept(listener -> listener.onInserted(index, value));
27
+	}
28
+
29
+	@Override
30
+	public void add(int index, T value) {
31
+		values.add(index, value);
32
+		listeners.accept(listener -> listener.onInserted(index, value));
33
+	}
34
+
35
+	@Override
36
+	public void set(int index, T value) {
37
+		T oldValue = values.set(index, value);
38
+		listeners.accept(listener -> listener.onChanged(index, oldValue, value));
39
+	}
40
+
41
+	@Override
42
+	public void remove(int index) {
43
+		T oldValue = values.remove(index);
44
+		listeners.accept(listener -> listener.onRemoved(index, oldValue));
45
+	}
46
+
47
+	@Override
48
+	public void remove(T value) {
49
+		int index = indexOf(value);
50
+		if (index < 0)
51
+			return;
52
+		
53
+		remove(index);
54
+	}
55
+
56
+	@Override
57
+	public int indexOf(T value) {
58
+		return values.indexOf(value);
59
+	}
60
+	
61
+	@Override
62
+	public int size() {
63
+		return values.size();
64
+	}
65
+
66
+	@Override
67
+	public T get(int index) {
68
+		return values.get(index);
69
+	}
70
+
71
+	@Override
72
+	public Iterator<T> iterator() {
73
+		return values.iterator();
74
+	}
75
+
76
+	@Override
77
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener) {
78
+		return listeners.add(listener);
79
+	}
80
+}

+ 40
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveBool.java View File

@@ -0,0 +1,40 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+import org.openzen.drawablegui.listeners.ListenerList;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class LiveBool {
16
+	private final ListenerList<Listener> listeners = new ListenerList<>();
17
+	private boolean value;
18
+	
19
+	public boolean getValue() {
20
+		return value;
21
+	}
22
+	
23
+	public void setValue(boolean value) {
24
+		boolean oldValue = this.value;
25
+		this.value = value;
26
+		listeners.accept(listener -> listener.onChanged(oldValue, value));
27
+	}
28
+	
29
+	public void toggle() {
30
+		setValue(!value);
31
+	}
32
+	
33
+	public ListenerHandle<Listener> addListener(Listener listener) {
34
+		return listeners.add(listener);
35
+	}
36
+	
37
+	public interface Listener {
38
+		void onChanged(boolean oldValue, boolean newValue);
39
+	}
40
+}

+ 155
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveConcatList.java View File

@@ -0,0 +1,155 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import java.io.Closeable;
9
+import java.util.Iterator;
10
+import org.openzen.drawablegui.listeners.ListenerHandle;
11
+import org.openzen.drawablegui.listeners.ListenerList;
12
+
13
+/**
14
+ *
15
+ * @author Hoofdgebruiker
16
+ */
17
+public class LiveConcatList<T> implements Closeable, LiveList<T> {
18
+	private final ListenerList<Listener<T>> listeners = new ListenerList<>();
19
+	private final LiveList<T> a;
20
+	private final LiveList<T> b;
21
+	
22
+	private final ListenerHandle<Listener<T>> aListener;
23
+	private final ListenerHandle<Listener<T>> bListener;
24
+	
25
+	public LiveConcatList(LiveList<T> a, LiveList<T> b) {
26
+		this.a = a;
27
+		this.b = b;
28
+		
29
+		aListener = a.addListener(new FirstListListener());
30
+		bListener = b.addListener(new SecondListListener());
31
+	}
32
+	
33
+	@Override
34
+	public void close() {
35
+		aListener.close();
36
+		bListener.close();
37
+	}
38
+
39
+	@Override
40
+	public void add(T value) {
41
+		throw new UnsupportedOperationException("Cannot modify a concat list");
42
+	}
43
+
44
+	@Override
45
+	public void add(int index, T value) {
46
+		throw new UnsupportedOperationException("Cannot modify a concat list");
47
+	}
48
+
49
+	@Override
50
+	public void set(int index, T value) {
51
+		throw new UnsupportedOperationException("Cannot modify a concat list");
52
+	}
53
+
54
+	@Override
55
+	public void remove(int index) {
56
+		throw new UnsupportedOperationException("Cannot modify a concat list");
57
+	}
58
+
59
+	@Override
60
+	public void remove(T value) {
61
+		throw new UnsupportedOperationException("Cannot modify a concat list");
62
+	}
63
+
64
+	@Override
65
+	public int indexOf(T value) {
66
+		int result = a.indexOf(value);
67
+		if (result >= 0)
68
+			return result;
69
+		
70
+		result = b.indexOf(value);
71
+		return result < 0 ? -1 : result + a.size();
72
+	}
73
+
74
+	@Override
75
+	public int size() {
76
+		return a.size() + b.size();
77
+	}
78
+
79
+	@Override
80
+	public T get(int index) {
81
+		return index < a.size() ? a.get(index) : b.get(index - a.size());
82
+	}
83
+
84
+	@Override
85
+	public Iterator<T> iterator() {
86
+		return new ConcatIterator();
87
+	}
88
+
89
+	@Override
90
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener) {
91
+		return listeners.add(listener);
92
+	}
93
+	
94
+	private class ConcatIterator implements Iterator<T> {
95
+		private boolean firstList = true;
96
+		private Iterator<T> iterator;
97
+
98
+		public ConcatIterator() {
99
+			iterator = a.iterator();
100
+			if (!iterator.hasNext()) {
101
+				firstList = false;
102
+				iterator = b.iterator();
103
+			}
104
+		}
105
+		
106
+		@Override
107
+		public boolean hasNext() {
108
+			return iterator.hasNext();
109
+		}
110
+
111
+		@Override
112
+		public T next() {
113
+			T result = iterator.next();
114
+			if (firstList && !iterator.hasNext()) {
115
+				firstList = false;
116
+				iterator = b.iterator();
117
+			}
118
+			return result;
119
+		}
120
+	}
121
+	
122
+	private class FirstListListener implements Listener<T> {
123
+		@Override
124
+		public void onInserted(int index, T value) {
125
+			listeners.accept(listener -> listener.onInserted(index, value));
126
+		}
127
+
128
+		@Override
129
+		public void onChanged(int index, T oldValue, T newValue) {
130
+			listeners.accept(listener -> listener.onChanged(index, oldValue, newValue));
131
+		}
132
+
133
+		@Override
134
+		public void onRemoved(int index, T oldValue) {
135
+			listeners.accept(listener -> listener.onRemoved(index, oldValue));
136
+		}
137
+	}
138
+	
139
+	private class SecondListListener implements Listener<T> {
140
+		@Override
141
+		public void onInserted(int index, T value) {
142
+			listeners.accept(listener -> listener.onInserted(a.size() + index, value));
143
+		}
144
+
145
+		@Override
146
+		public void onChanged(int index, T oldValue, T newValue) {
147
+			listeners.accept(listener -> listener.onChanged(a.size() + index, oldValue, newValue));
148
+		}
149
+
150
+		@Override
151
+		public void onRemoved(int index, T oldValue) {
152
+			listeners.accept(listener -> listener.onRemoved(a.size() + index, oldValue));
153
+		}
154
+	}
155
+}

+ 73
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveEmptyList.java View File

@@ -0,0 +1,73 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import java.util.Collections;
9
+import java.util.Iterator;
10
+import org.openzen.drawablegui.listeners.DummyListenerHandle;
11
+import org.openzen.drawablegui.listeners.ListenerHandle;
12
+
13
+/**
14
+ *
15
+ * @author Hoofdgebruiker
16
+ */
17
+public class LiveEmptyList<T> implements LiveList<T> {
18
+	private static final LiveEmptyList INSTANCE = new LiveEmptyList();
19
+	
20
+	public static <T> LiveEmptyList<T> get() {
21
+		return INSTANCE;
22
+	}
23
+
24
+	@Override
25
+	public void add(T value) {
26
+		throw new UnsupportedOperationException("Immutable list");
27
+	}
28
+
29
+	@Override
30
+	public void add(int index, T value) {
31
+		throw new UnsupportedOperationException("Immutable list");
32
+	}
33
+
34
+	@Override
35
+	public void set(int index, T value) {
36
+		throw new UnsupportedOperationException("Immutable list");
37
+	}
38
+
39
+	@Override
40
+	public void remove(int index) {
41
+		throw new UnsupportedOperationException("Immutable list");
42
+	}
43
+
44
+	@Override
45
+	public void remove(T value) {
46
+		throw new UnsupportedOperationException("Immutable list");
47
+	}
48
+
49
+	@Override
50
+	public int indexOf(T value) {
51
+		return -1;
52
+	}
53
+	
54
+	@Override
55
+	public int size() {
56
+		return 0;
57
+	}
58
+	
59
+	@Override
60
+	public T get(int index) {
61
+		throw new ArrayIndexOutOfBoundsException(index);
62
+	}
63
+
64
+	@Override
65
+	public Iterator<T> iterator() {
66
+		return Collections.emptyIterator();
67
+	}
68
+
69
+	@Override
70
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener) {
71
+		return new DummyListenerHandle<>(listener);
72
+	}
73
+}

+ 24
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveInt.java View File

@@ -0,0 +1,24 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface LiveInt {
15
+	int getValue();
16
+	
17
+	void setValue(int value);
18
+	
19
+	ListenerHandle<Listener> addListener(Listener listener);
20
+	
21
+	interface Listener {
22
+		void onChanged(int oldValue, int newValue);
23
+	}
24
+}

+ 44
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveList.java View File

@@ -0,0 +1,44 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import java.util.Iterator;
9
+import org.openzen.drawablegui.listeners.ListenerHandle;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public interface LiveList<T> extends Iterable<T> {
16
+	void add(T value);
17
+	
18
+	void add(int index, T value);
19
+	
20
+	void set(int index, T value);
21
+	
22
+	void remove(int index);
23
+	
24
+	void remove(T value);
25
+	
26
+	int indexOf(T value);
27
+	
28
+	int size();
29
+	
30
+	T get(int index);
31
+	
32
+	@Override
33
+	Iterator<T> iterator();
34
+	
35
+	ListenerHandle<Listener<T>> addListener(Listener<T> listener);
36
+	
37
+	interface Listener<T> {
38
+		void onInserted(int index, T value);
39
+		
40
+		void onChanged(int index, T oldValue, T newValue);
41
+		
42
+		void onRemoved(int index, T oldValue);
43
+	}
44
+}

+ 111
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveMappedList.java View File

@@ -0,0 +1,111 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import java.io.Closeable;
9
+import java.util.ArrayList;
10
+import java.util.Iterator;
11
+import java.util.List;
12
+import java.util.function.Function;
13
+import org.openzen.drawablegui.listeners.ListenerHandle;
14
+import org.openzen.drawablegui.listeners.ListenerList;
15
+
16
+/**
17
+ *
18
+ * @author Hoofdgebruiker
19
+ */
20
+public class LiveMappedList<T, U> implements Closeable, LiveList<U> {
21
+	private final ListenerList<Listener<U>> listeners = new ListenerList<>();
22
+	private final Function<T, U> projection;
23
+	private final List<U> mapped;
24
+	private final ListenerHandle<Listener<T>> mappingListenerHandle;
25
+	
26
+	public LiveMappedList(LiveList<T> original, Function<T, U> projection) {
27
+		this.projection = projection;
28
+		mappingListenerHandle = original.addListener(new MappingListener());
29
+		
30
+		mapped = new ArrayList<>();
31
+		for (T originalItem : original)
32
+			mapped.add(projection.apply(originalItem));
33
+	}
34
+	
35
+	@Override
36
+	public void close() {
37
+		mappingListenerHandle.close();
38
+	}
39
+
40
+	@Override
41
+	public void add(U value) {
42
+		throw new UnsupportedOperationException("Cannot modify a mapped list");
43
+	}
44
+
45
+	@Override
46
+	public void add(int index, U value) {
47
+		throw new UnsupportedOperationException("Cannot modify a mapped list");
48
+	}
49
+
50
+	@Override
51
+	public void set(int index, U value) {
52
+		throw new UnsupportedOperationException("Cannot modify a mapped list");
53
+	}
54
+
55
+	@Override
56
+	public void remove(int index) {
57
+		throw new UnsupportedOperationException("Cannot modify a mapped list");
58
+	}
59
+
60
+	@Override
61
+	public void remove(U value) {
62
+		throw new UnsupportedOperationException("Cannot modify a mapped list");
63
+	}
64
+
65
+	@Override
66
+	public int indexOf(U value) {
67
+		return mapped.indexOf(value);
68
+	}
69
+	
70
+	@Override
71
+	public int size() {
72
+		return mapped.size();
73
+	}
74
+
75
+	@Override
76
+	public U get(int index) {
77
+		return mapped.get(index);
78
+	}
79
+
80
+	@Override
81
+	public Iterator<U> iterator() {
82
+		return mapped.iterator();
83
+	}
84
+
85
+	@Override
86
+	public ListenerHandle<Listener<U>> addListener(Listener<U> listener) {
87
+		return listeners.add(listener);
88
+	}
89
+	
90
+	private class MappingListener implements Listener<T> {
91
+		@Override
92
+		public void onInserted(int index, T value) {
93
+			U mappedValue = projection.apply(value);
94
+			mapped.add(index, mappedValue);
95
+			listeners.accept(listener -> listener.onInserted(index, mappedValue));
96
+		}
97
+
98
+		@Override
99
+		public void onChanged(int index, T oldValue, T newValue) {
100
+			U mappedNewValue = projection.apply(newValue);
101
+			U mappedOldValue = mapped.set(index, mappedNewValue);
102
+			listeners.accept(listener -> listener.onChanged(index, mappedOldValue, mappedNewValue));
103
+		}
104
+
105
+		@Override
106
+		public void onRemoved(int index, T oldValue) {
107
+			U oldMappedValue = mapped.remove(index);
108
+			listeners.accept(listener -> listener.onRemoved(index, oldMappedValue));
109
+		}
110
+	}
111
+}

+ 24
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LiveObject.java View File

@@ -0,0 +1,24 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface LiveObject<T> {
15
+	public T getValue();
16
+	
17
+	public void setValue(T value);
18
+	
19
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener);
20
+	
21
+	public interface Listener<T> {
22
+		void onUpdated(T oldValue, T newValue);
23
+	}
24
+}

+ 124
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/LivePrefixedList.java View File

@@ -0,0 +1,124 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import java.util.Iterator;
9
+import org.openzen.drawablegui.listeners.ListenerHandle;
10
+import org.openzen.drawablegui.listeners.ListenerList;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class LivePrefixedList<T> implements LiveList<T> {
17
+	private final ListenerList<Listener<T>> listeners = new ListenerList<>();
18
+	private final T prefix;
19
+	private final LiveList<T> values;
20
+	
21
+	public LivePrefixedList(T prefix, LiveList<T> values) {
22
+		this.prefix = prefix;
23
+		this.values = values;
24
+	}
25
+
26
+	@Override
27
+	public void add(T value) {
28
+		values.add(value);
29
+	}
30
+
31
+	@Override
32
+	public void add(int index, T value) {
33
+		if (index == 0)
34
+			throw new UnsupportedOperationException("Cannot add an item at the beginning of a prefixed list");
35
+		values.add(index - 1, value);
36
+	}
37
+
38
+	@Override
39
+	public void set(int index, T value) {
40
+		if (index == 0)
41
+			throw new UnsupportedOperationException("Cannot change the item at the beginning of a prefixed list");
42
+		
43
+		values.set(index - 1, value);
44
+	}
45
+
46
+	@Override
47
+	public void remove(int index) {
48
+		if (index == 0)
49
+			throw new UnsupportedOperationException("Cannot remote the item at the beginning of a prefixed list");
50
+		
51
+		values.remove(index - 1);
52
+	}
53
+
54
+	@Override
55
+	public void remove(T value) {
56
+		values.remove(value);
57
+	}
58
+
59
+	@Override
60
+	public int indexOf(T value) {
61
+		if (value.equals(prefix))
62
+			return 0;
63
+		
64
+		int base = values.indexOf(value);
65
+		return base < 0 ? base : base - 1;
66
+	}
67
+
68
+	@Override
69
+	public int size() {
70
+		return values.size() + 1;
71
+	}
72
+	
73
+	@Override
74
+	public T get(int index) {
75
+		return index == 0 ? prefix : values.get(index);
76
+	}
77
+
78
+	@Override
79
+	public Iterator<T> iterator() {
80
+		return new PrefixIterator();
81
+	}
82
+
83
+	@Override
84
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener) {
85
+		return listeners.add(listener);
86
+	}
87
+	
88
+	private class PrefixIterator implements Iterator<T> {
89
+		private boolean first = true;
90
+		private Iterator<T> others = values.iterator();
91
+
92
+		@Override
93
+		public boolean hasNext() {
94
+			return first || others.hasNext();
95
+		}
96
+
97
+		@Override
98
+		public T next() {
99
+			if (first) {
100
+				first = false;
101
+				return prefix;
102
+			} else {
103
+				return others.next();
104
+			}
105
+		}
106
+	}
107
+	
108
+	private class BaseListener implements Listener<T> {
109
+		@Override
110
+		public void onInserted(int index, T value) {
111
+			listeners.accept(listener -> listener.onInserted(index + 1, value));
112
+		}
113
+
114
+		@Override
115
+		public void onChanged(int index, T oldValue, T newValue) {
116
+			listeners.accept(listener -> listener.onChanged(index + 1, oldValue, newValue));
117
+		}
118
+
119
+		@Override
120
+		public void onRemoved(int index, T oldValue) {
121
+			listeners.accept(listener -> listener.onRemoved(index + 1, oldValue));
122
+		}
123
+	}
124
+}

+ 35
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/SimpleLiveInt.java View File

@@ -0,0 +1,35 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+import org.openzen.drawablegui.listeners.ListenerList;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class SimpleLiveInt implements LiveInt {
16
+	private final ListenerList<Listener> listeners = new ListenerList<>();
17
+	private int value = 0;
18
+
19
+	@Override
20
+	public int getValue() {
21
+		return value;
22
+	}
23
+
24
+	@Override
25
+	public void setValue(int value) {
26
+		int oldValue = this.value;
27
+		this.value = value;
28
+		listeners.accept(listener -> listener.onChanged(oldValue, value));
29
+	}
30
+
31
+	@Override
32
+	public ListenerHandle<Listener> addListener(Listener listener) {
33
+		return listeners.add(listener);
34
+	}
35
+}

+ 39
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/live/SimpleLiveObject.java View File

@@ -0,0 +1,39 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.live;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+import org.openzen.drawablegui.listeners.ListenerList;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class SimpleLiveObject<T> implements LiveObject<T> {
16
+	private final ListenerList<Listener<T>> listeners = new ListenerList<>();
17
+	private T value;
18
+	
19
+	public SimpleLiveObject(T value) {
20
+		this.value = value;
21
+	}
22
+	
23
+	@Override
24
+	public T getValue() {
25
+		return value;
26
+	}
27
+	
28
+	@Override
29
+	public void setValue(T value) {
30
+		T oldValue = this.value;
31
+		this.value = value;
32
+		listeners.accept(listener -> listener.onUpdated(oldValue, value));
33
+	}
34
+	
35
+	@Override
36
+	public ListenerHandle<Listener<T>> addListener(Listener<T> listener) {
37
+		return listeners.add(listener);
38
+	}
39
+}

+ 163
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollBar.java View File

@@ -0,0 +1,163 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.scroll;
7
+
8
+import org.openzen.drawablegui.DRectangle;
9
+import org.openzen.drawablegui.DCanvas;
10
+import org.openzen.drawablegui.DComponent;
11
+import org.openzen.drawablegui.DDimensionPreferences;
12
+import org.openzen.drawablegui.DDrawingContext;
13
+import org.openzen.drawablegui.DMouseEvent;
14
+import org.openzen.drawablegui.listeners.ListenerHandle;
15
+import org.openzen.drawablegui.live.ImmutableLiveObject;
16
+import org.openzen.drawablegui.live.LiveInt;
17
+import org.openzen.drawablegui.live.LiveObject;
18
+
19
+/**
20
+ *
21
+ * @author Hoofdgebruiker
22
+ */
23
+public class DScrollBar implements DComponent {
24
+	private final DScrollBarStyle style;
25
+	private final LiveObject<DDimensionPreferences> preferences;
26
+	
27
+	private final LiveInt targetHeight;
28
+	private final LiveInt offset;
29
+	
30
+	private final ListenerHandle<LiveInt.Listener> targetHeightListener;
31
+	private final ListenerHandle<LiveInt.Listener> offsetListener;
32
+	
33
+	private DDrawingContext context;
34
+	private DRectangle bounds;
35
+	
36
+	private int fromY = 0;
37
+	private int toY = 0;
38
+	private boolean hovering = false;
39
+	private boolean dragging = false;
40
+	private int dragStartOffset;
41
+	private int dragStartY;
42
+	
43
+	public DScrollBar(DScrollBarStyle style, LiveInt targetHeight, LiveInt offset) {
44
+		this.style = style;
45
+		this.targetHeight = targetHeight;
46
+		this.offset = offset;
47
+		this.preferences = new ImmutableLiveObject<>(new DDimensionPreferences(style.width, 0));
48
+		
49
+		targetHeightListener = targetHeight.addListener(new ScrollListener());
50
+		offsetListener = offset.addListener(new ScrollListener());
51
+	}
52
+
53
+	@Override
54
+	public void setContext(DDrawingContext context) {
55
+		this.context = context;
56
+	}
57
+
58
+	@Override
59
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
60
+		return preferences;
61
+	}
62
+
63
+	@Override
64
+	public DRectangle getBounds() {
65
+		return bounds;
66
+	}
67
+
68
+	@Override
69
+	public void setBounds(DRectangle bounds) {
70
+		this.bounds = bounds;
71
+		recalculate();
72
+	}
73
+
74
+	@Override
75
+	public void paint(DCanvas canvas) {
76
+		if (targetHeight.getValue() <= this.bounds.height)
77
+			return; // no scrollbar
78
+		
79
+		canvas.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height, style.scrollBarBackgroundColor);
80
+		
81
+		int color = style.scrollBarNormalColor;
82
+		if (hovering)
83
+			color = style.scrollBarHoverColor;
84
+		if (dragging)
85
+			color = style.scrollBarPressColor;
86
+		
87
+		canvas.fillRectangle(bounds.x, bounds.y + fromY, this.bounds.width, toY - fromY, color);
88
+	}
89
+	
90
+	@Override
91
+	public void onMouseEnter(DMouseEvent e) {
92
+		checkHover(e);
93
+	}
94
+	
95
+	@Override
96
+	public void onMouseExit(DMouseEvent e) {
97
+		setHovering(false);
98
+		setDragging(false);
99
+	}
100
+	
101
+	@Override
102
+	public void onMouseMove(DMouseEvent e) {
103
+		checkHover(e);
104
+	}
105
+	
106
+	@Override
107
+	public void onMouseDrag(DMouseEvent e) {
108
+		if (dragging) {
109
+			int deltaY = e.y - dragStartY;
110
+			int offsetForDelta = deltaY * targetHeight.getValue() / this.bounds.height;
111
+			offset.setValue(dragStartOffset + offsetForDelta);
112
+		}
113
+	}
114
+	
115
+	@Override
116
+	public void onMouseDown(DMouseEvent e) {
117
+		if (e.y >= fromY && e.y < toY) {
118
+			dragStartOffset = offset.getValue();
119
+			dragStartY = e.y;
120
+			setDragging(true);
121
+		}
122
+	}
123
+	
124
+	@Override
125
+	public void onMouseRelease(DMouseEvent e) {
126
+		setDragging(false);
127
+	}
128
+	
129
+	private void checkHover(DMouseEvent e) {
130
+		setHovering(e.y >= fromY && e.y < toY);
131
+	}
132
+	
133
+	private void setHovering(boolean hovering) {
134
+		if (hovering == this.hovering)
135
+			return;
136
+		
137
+		this.hovering = hovering;
138
+		context.repaint(bounds.x, fromY, bounds.width, toY - fromY);
139
+	}
140
+	
141
+	private void setDragging(boolean dragging) {
142
+		if (dragging == this.dragging)
143
+			return;
144
+		
145
+		this.dragging = dragging;
146
+		context.repaint(bounds.x, fromY, bounds.width, toY - fromY);
147
+	}
148
+	
149
+	private void recalculate() {
150
+		if (targetHeight.getValue() == 0)
151
+			return;
152
+		
153
+		fromY = this.bounds.height * offset.getValue() / targetHeight.getValue();
154
+		toY = this.bounds.height * (offset.getValue() + this.bounds.height) / targetHeight.getValue();
155
+	}
156
+	
157
+	private class ScrollListener implements LiveInt.Listener {
158
+		@Override
159
+		public void onChanged(int oldValue, int newValue) {
160
+			recalculate();
161
+		}
162
+	}
163
+}

+ 33
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollBarStyle.java View File

@@ -0,0 +1,33 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.scroll;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class DScrollBarStyle {
13
+	public static final DScrollBarStyle DEFAULT = new DScrollBarStyle(0xFFF0F0F0, 0xFFCDCDCD, 0xFF888888, 0xFF666666, 20);
14
+	
15
+	public final int scrollBarBackgroundColor;
16
+	public final int scrollBarNormalColor;
17
+	public final int scrollBarHoverColor;
18
+	public final int scrollBarPressColor;
19
+	public final int width;
20
+	
21
+	public DScrollBarStyle(
22
+			int scrollBarBackgroundColor,
23
+			int scrollBarNormalColor,
24
+			int scrollBarHoverColor,
25
+			int scrollBarPressColor,
26
+			int width) {
27
+		this.scrollBarBackgroundColor = scrollBarBackgroundColor;
28
+		this.scrollBarNormalColor = scrollBarNormalColor;
29
+		this.scrollBarHoverColor = scrollBarHoverColor;
30
+		this.scrollBarPressColor = scrollBarPressColor;
31
+		this.width = width;
32
+	}
33
+}

+ 269
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollPane.java View File

@@ -0,0 +1,269 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.scroll;
7
+
8
+import org.openzen.drawablegui.DRectangle;
9
+import org.openzen.drawablegui.DCanvas;
10
+import org.openzen.drawablegui.DComponent;
11
+import org.openzen.drawablegui.DDimensionPreferences;
12
+import org.openzen.drawablegui.DDrawingContext;
13
+import org.openzen.drawablegui.DFont;
14
+import org.openzen.drawablegui.DFontMetrics;
15
+import org.openzen.drawablegui.DIRectangle;
16
+import org.openzen.drawablegui.DMouseEvent;
17
+import org.openzen.drawablegui.listeners.ListenerHandle;
18
+import org.openzen.drawablegui.live.LiveInt;
19
+import org.openzen.drawablegui.live.LiveObject;
20
+import org.openzen.drawablegui.live.SimpleLiveInt;
21
+
22
+/**
23
+ *
24
+ * @author Hoofdgebruiker
25
+ */
26
+public class DScrollPane implements DComponent {
27
+	private final DScrollPaneStyle style;
28
+	private final DComponent contents;
29
+	private final DScrollBar scrollBar;
30
+	private DDrawingContext context;
31
+	private DRectangle bounds;
32
+	
33
+	private final LiveInt contentsHeight;
34
+	private final LiveInt offsetX;
35
+	private final LiveInt offsetY;
36
+	
37
+	private final ListenerHandle<LiveInt.Listener> contentsHeightListener;
38
+	private final ListenerHandle<LiveInt.Listener> offsetXListener;
39
+	private final ListenerHandle<LiveInt.Listener> offsetYListener;
40
+	
41
+	private DComponent hovering = null;
42
+	
43
+	public DScrollPane(DScrollPaneStyle style, DComponent contents) {
44
+		this.style = style;
45
+		this.contents = contents;
46
+		
47
+		contentsHeight = new SimpleLiveInt();
48
+		offsetX = new SimpleLiveInt();
49
+		offsetY = new SimpleLiveInt();
50
+		
51
+		scrollBar = new DScrollBar(style.scrollBarStyle, contentsHeight, offsetY);
52
+		
53
+		contentsHeightListener = contentsHeight.addListener(new ScrollListener());
54
+		offsetXListener = offsetX.addListener(new ScrollListener());
55
+		offsetYListener = offsetY.addListener(new ScrollListener());
56
+		
57
+		contents.getDimensionPreferences().addListener((oldPreferences, newPreferences) -> {
58
+			if (bounds == null)
59
+				return;
60
+			
61
+			contents.setBounds(new DRectangle(0, 0, bounds.width, Math.max(bounds.height, newPreferences.preferredHeight)));
62
+			contentsHeight.setValue(newPreferences.preferredHeight);
63
+		});
64
+	}
65
+
66
+	@Override
67
+	public void setContext(DDrawingContext context) {
68
+		this.context = context;
69
+		contents.setContext(new TranslatedContext());
70
+		scrollBar.setContext(context);
71
+	}
72
+
73
+	@Override
74
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
75
+		return contents.getDimensionPreferences(); // TODO: derived preferences
76
+	}
77
+
78
+	@Override
79
+	public DRectangle getBounds() {
80
+		return bounds;
81
+	}
82
+
83
+	@Override
84
+	public void setBounds(DRectangle bounds) {
85
+		this.bounds = bounds;
86
+		int height = Math.max(
87
+				bounds.height - style.border.getPaddingTop() - style.border.getPaddingBottom(),
88
+				contents.getDimensionPreferences().getValue().preferredHeight);
89
+		int scrollBarWidth = scrollBar.getDimensionPreferences().getValue().preferredWidth;
90
+		scrollBar.setBounds(new DRectangle(
91
+				bounds.x + bounds.width - scrollBarWidth - style.border.getPaddingRight(),
92
+				bounds.y + style.border.getPaddingTop(),
93
+				scrollBarWidth,
94
+				bounds.height - style.border.getPaddingTop() - style.border.getPaddingBottom()));
95
+		contents.setBounds(new DRectangle(0, 0, bounds.width - scrollBar.getBounds().width, height));
96
+		contentsHeight.setValue(height);
97
+	}
98
+
99
+	@Override
100
+	public void paint(DCanvas canvas) {
101
+		if (bounds == null)
102
+			return;
103
+		
104
+		style.border.paint(canvas, bounds);
105
+		scrollBar.paint(canvas);
106
+		
107
+		canvas.pushBounds(new DIRectangle(
108
+				bounds.x + style.border.getPaddingLeft(),
109
+				bounds.y + style.border.getPaddingTop(),
110
+				bounds.width - style.border.getPaddingLeft() - style.border.getPaddingTop() - scrollBar.getBounds().width,
111
+				bounds.height - style.border.getPaddingTop() - style.border.getPaddingBottom()));
112
+		canvas.pushOffset(bounds.x - offsetX.getValue(), bounds.y - offsetY.getValue());
113
+		contents.paint(canvas);
114
+		canvas.popOffset();
115
+		canvas.popBounds();
116
+	}
117
+	
118
+	@Override
119
+	public void onMouseScroll(DMouseEvent e) {
120
+		offsetY.setValue(offsetY.getValue() + e.deltaZ * 50);
121
+	}
122
+	
123
+	@Override
124
+	public void onMouseEnter(DMouseEvent e) {
125
+		if (e.x >= scrollBar.getBounds().x) {
126
+			setHovering(scrollBar, e);
127
+		} else {
128
+			setHovering(contents, e);
129
+		}
130
+	}
131
+	
132
+	@Override
133
+	public void onMouseExit(DMouseEvent e) {
134
+		setHovering(null, e);
135
+	}
136
+	
137
+	@Override
138
+	public void onMouseMove(DMouseEvent e) {
139
+		if (e.x >= scrollBar.getBounds().x) {
140
+			if (hovering != scrollBar) {
141
+				setHovering(scrollBar, e);
142
+			} else {
143
+				scrollBar.onMouseMove(e);
144
+			}
145
+		} else {
146
+			if (hovering != contents) {
147
+				setHovering(contents, e);
148
+			} else {
149
+				contents.onMouseMove(translateMouseEvent(e));
150
+			}
151
+		}
152
+	}
153
+	
154
+	@Override
155
+	public void onMouseDrag(DMouseEvent e) {
156
+		if (hovering != null)
157
+			hovering.onMouseDrag(forward(hovering, e));
158
+	}
159
+	
160
+	@Override
161
+	public void onMouseClick(DMouseEvent e) {
162
+		if (hovering != null)
163
+			hovering.onMouseClick(forward(hovering, e));
164
+	}
165
+	
166
+	@Override
167
+	public void onMouseDown(DMouseEvent e) {
168
+		if (hovering != null)
169
+			hovering.onMouseDown(forward(hovering, e));
170
+	}
171
+	
172
+	@Override
173
+	public void onMouseRelease(DMouseEvent e) {
174
+		if (hovering != null)
175
+			hovering.onMouseRelease(forward(hovering, e));
176
+		
177
+		onMouseMove(e);
178
+	}
179
+	
180
+	private DMouseEvent forward(DComponent target, DMouseEvent e) {
181
+		return target == contents ? translateMouseEvent(e) : e;
182
+	}
183
+	
184
+	private void setHovering(DComponent target, DMouseEvent e) {
185
+		if (target == hovering)
186
+			return;
187
+		
188
+		if (hovering != null)
189
+			hovering.onMouseExit(forward(hovering, e));
190
+		this.hovering = target;
191
+		if (hovering != null)
192
+			hovering.onMouseEnter(forward(hovering, e));
193
+	}
194
+	
195
+	private DMouseEvent translateMouseEvent(DMouseEvent e) {
196
+		return new DMouseEvent(
197
+				e.x - bounds.x + offsetX.getValue(),
198
+				e.y - bounds.y + offsetY.getValue(),
199
+				e.modifiers,
200
+				e.deltaZ,
201
+				e.clickCount);
202
+	}
203
+	
204
+	private class TranslatedContext implements DDrawingContext {
205
+
206
+		@Override
207
+		public float getScale() {
208
+			return context.getScale();
209
+		}
210
+
211
+		@Override
212
+		public void repaint(int x, int y, int width, int height) {
213
+			int left = x + bounds.x - offsetX.getValue();
214
+			int top = y + bounds.y - offsetY.getValue();
215
+			int right = left + width;
216
+			int bottom = top + height;
217
+			
218
+			left = Math.max(bounds.x, left);
219
+			top = Math.max(bounds.y, top);
220
+			right = Math.min(bounds.x + bounds.width, right);
221
+			bottom = Math.min(bounds.y + bounds.height, bottom);
222
+			
223
+			if (left >= right || top >= bottom)
224
+				return;
225
+			
226
+			context.repaint(left, top, right - left, bottom - top);
227
+		}
228
+
229
+		@Override
230
+		public void setCursor(Cursor cursor) {
231
+			context.setCursor(cursor);
232
+		}
233
+
234
+		@Override
235
+		public DFontMetrics getFontMetrics(DFont font) {
236
+			return context.getFontMetrics(font);
237
+		}
238
+
239
+		@Override
240
+		public void focus(DComponent component) {
241
+			context.focus(component);
242
+		}
243
+
244
+		@Override
245
+		public void scrollInView(int x, int y, int width, int height) {
246
+			if (y < offsetY.getValue())
247
+				offsetY.setValue(y);
248
+			if (y + height > offsetY.getValue() + bounds.height)
249
+				offsetY.setValue(y + height - bounds.height);
250
+		}
251
+	}
252
+	
253
+	private class ScrollListener implements LiveInt.Listener {
254
+
255
+		@Override
256
+		public void onChanged(int oldValue, int newValue) {
257
+			int value = offsetY.getValue();
258
+			if (value > contentsHeight.getValue() - bounds.height)
259
+				value = contentsHeight.getValue() - bounds.height;
260
+			if (value < 0)
261
+				value = 0;
262
+			
263
+			if (value != offsetY.getValue())
264
+				offsetY.setValue(value);
265
+			if (context != null && bounds != null)
266
+				context.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
267
+		}
268
+	}
269
+}

+ 27
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollPaneStyle.java View File

@@ -0,0 +1,27 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.scroll;
7
+
8
+import org.openzen.drawablegui.border.DBorder;
9
+import org.openzen.drawablegui.border.DLineBorder;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class DScrollPaneStyle {
16
+	public static final DScrollPaneStyle DEFAULT = new DScrollPaneStyle(new DLineBorder(0xFF888888, 1), DScrollBarStyle.DEFAULT);
17
+	
18
+	public final DBorder border;
19
+	public final DScrollBarStyle scrollBarStyle;
20
+	
21
+	public DScrollPaneStyle(
22
+			DBorder border,
23
+			DScrollBarStyle scrollBarStyle) {
24
+		this.border = border;
25
+		this.scrollBarStyle = scrollBarStyle;
26
+	}
27
+}

+ 146
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingCanvas.java View File

@@ -0,0 +1,146 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.swing;
7
+
8
+import java.awt.BasicStroke;
9
+import java.awt.Color;
10
+import java.awt.Font;
11
+import java.awt.Graphics2D;
12
+import java.awt.Rectangle;
13
+import java.awt.RenderingHints;
14
+import java.awt.geom.AffineTransform;
15
+import java.awt.geom.GeneralPath;
16
+import java.awt.geom.Rectangle2D;
17
+import java.util.Stack;
18
+import org.openzen.drawablegui.DCanvas;
19
+import org.openzen.drawablegui.DFont;
20
+import org.openzen.drawablegui.DFontFamily;
21
+import org.openzen.drawablegui.DTransform2D;
22
+import org.openzen.drawablegui.DDrawingContext;
23
+import org.openzen.drawablegui.DIRectangle;
24
+import org.openzen.drawablegui.DPath;
25
+
26
+/**
27
+ *
28
+ * @author Hoofdgebruiker
29
+ */
30
+public class SwingCanvas implements DCanvas {
31
+	private final Graphics2D g;
32
+	private final SwingGraphicsContext context;
33
+	private final Stack<Rectangle> bounds = new Stack<>();
34
+	private final Stack<AffineTransform> transformStack = new Stack<>();
35
+	
36
+	public SwingCanvas(Graphics2D g, SwingGraphicsContext context, DIRectangle bounds) {
37
+		this.g = g;
38
+		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
39
+		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
40
+		
41
+		this.context = context;
42
+		
43
+		if (bounds != null)
44
+			pushBounds(new DIRectangle(bounds.x, bounds.y, bounds.width, bounds.height));
45
+	}
46
+	
47
+	@Override
48
+	public void pushBounds(DIRectangle bounds) {
49
+		this.bounds.push(g.getClipBounds());
50
+		g.setClip(bounds.x, bounds.y, bounds.width, bounds.height);
51
+	}
52
+	
53
+	@Override
54
+	public void popBounds() {
55
+		Rectangle bounds = this.bounds.pop();
56
+		g.setClip(bounds.x, bounds.y, bounds.width, bounds.height);
57
+	}
58
+	
59
+	@Override
60
+	public void pushOffset(int x, int y) {
61
+		transformStack.push(g.getTransform());
62
+		g.transform(AffineTransform.getTranslateInstance(x, y));
63
+	}
64
+	
65
+	@Override
66
+	public void popOffset() {
67
+		g.setTransform(transformStack.pop());
68
+	}
69
+	
70
+	@Override
71
+	public DIRectangle getBounds() {
72
+		Rectangle bounds = g.getClipBounds();
73
+		return new DIRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
74
+	}
75
+
76
+	@Override
77
+	public void drawText(DFont font, int color, float x, float y, String text) {
78
+		prepare(font);
79
+		g.setColor(new Color(color, true));
80
+		g.setFont((Font) font.cached);
81
+		g.drawString(text, x, y);
82
+	}
83
+
84
+	@Override
85
+	public float measureTextLength(DFont font, String text, int offset, int length) {
86
+		prepare(font);
87
+		g.setFont((Font) font.cached);
88
+		Rectangle2D rect = g.getFontMetrics().getStringBounds(text, offset, length, g);
89
+		return (float) rect.getWidth();
90
+	}
91
+
92
+	@Override
93
+	public DDrawingContext getContext() {
94
+		return context;
95
+	}
96
+
97
+	@Override
98
+	public void strokePath(DPath path, DTransform2D transform, int color, float lineWidth) {
99
+		AffineTransform old = g.getTransform();
100
+		GeneralPath jPath = context.getPath(path);
101
+		g.setColor(new Color(color, true));
102
+		g.setStroke(new BasicStroke(lineWidth));
103
+		g.transform(getTransform(transform));
104
+		g.draw(jPath);
105
+		g.setTransform(old);
106
+	}
107
+
108
+	@Override
109
+	public void fillPath(DPath path, DTransform2D transform, int color) {
110
+		AffineTransform old = g.getTransform();
111
+		GeneralPath jPath = context.getPath(path);
112
+		g.setColor(new Color(color, true));
113
+		g.transform(getTransform(transform));
114
+		g.fill(jPath);
115
+		g.setTransform(old);
116
+	}
117
+
118
+	@Override
119
+	public void shadowPath(DPath path, DTransform2D transform, int color, float dx, float dy, float radius) {
120
+		// TODO
121
+	}
122
+
123
+	@Override
124
+	public void fillRectangle(int x, int y, int width, int height, int color) {
125
+		g.setColor(new Color(color, true));
126
+		g.fillRect(x, y, width, height);
127
+	}
128
+	
129
+	public static void prepare(DFont font) {
130
+		if (font.cached != null && font.cached instanceof Font)
131
+			return;
132
+		
133
+		String baseFontName = font.family == DFontFamily.CODE ? "Consolas" : Font.DIALOG;
134
+		int style = 0;
135
+		if (font.bold)
136
+			style |= Font.BOLD;
137
+		if (font.italic)
138
+			style |= Font.ITALIC;
139
+		
140
+		font.cached = Font.decode(baseFontName).deriveFont(style, font.size);
141
+	}
142
+	
143
+	private AffineTransform getTransform(DTransform2D transform) {
144
+		return new AffineTransform(transform.xx, transform.xy, transform.yx, transform.yy, transform.dx, transform.dy);
145
+	}
146
+}

+ 49
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingFontMetrics.java View File

@@ -0,0 +1,49 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.swing;
7
+
8
+import java.awt.FontMetrics;
9
+import java.awt.Graphics;
10
+import org.openzen.drawablegui.DFontMetrics;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class SwingFontMetrics implements DFontMetrics {
17
+	private final FontMetrics metrics;
18
+	private final Graphics context;
19
+	
20
+	public SwingFontMetrics(FontMetrics metrics, Graphics context) {
21
+		this.metrics = metrics;
22
+		this.context = context;
23
+	}
24
+
25
+	@Override
26
+	public int getAscent() {
27
+		return metrics.getAscent();
28
+	}
29
+
30
+	@Override
31
+	public int getDescent() {
32
+		return metrics.getDescent();
33
+	}
34
+
35
+	@Override
36
+	public int getLeading() {
37
+		return metrics.getLeading();
38
+	}
39
+
40
+	@Override
41
+	public int getWidth(String str) {
42
+		return (int) metrics.getStringBounds(str, context).getWidth();
43
+	}
44
+
45
+	@Override
46
+	public int getWidth(String str, int offset, int length) {
47
+		return (int) metrics.getStringBounds(str, offset, length, context).getWidth();
48
+	}
49
+}

+ 137
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingGraphicsContext.java View File

@@ -0,0 +1,137 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.swing;
7
+
8
+import java.awt.Font;
9
+import java.awt.Graphics;
10
+import java.awt.geom.GeneralPath;
11
+import java.util.WeakHashMap;
12
+import org.openzen.drawablegui.DComponent;
13
+import org.openzen.drawablegui.DPath;
14
+import org.openzen.drawablegui.DDrawingContext;
15
+import org.openzen.drawablegui.DFont;
16
+import org.openzen.drawablegui.DFontMetrics;
17
+import org.openzen.drawablegui.DPathTracer;
18
+
19
+/**
20
+ *
21
+ * @author Hoofdgebruiker
22
+ */
23
+public class SwingGraphicsContext implements DDrawingContext {
24
+	public final float scale;
25
+	private final WeakHashMap<DPath, GeneralPath> preparedPaths = new WeakHashMap<>();
26
+	private final SwingRoot root;
27
+	private Graphics graphics;
28
+	
29
+	public SwingGraphicsContext(float scale, SwingRoot root) {
30
+		this.scale = scale;
31
+		this.root = root;
32
+	}
33
+	
34
+	public GeneralPath getPath(DPath path) {
35
+		GeneralPath generalPath = preparedPaths.get(path);
36
+		if (generalPath == null) {
37
+			generalPath = new GeneralPath();
38
+			path.trace(new PathTracer(generalPath));
39
+			preparedPaths.put(path, generalPath);
40
+		}
41
+		return generalPath;
42
+	}
43
+	
44
+	@Override
45
+	public float getScale() {
46
+		return scale;
47
+	}
48
+	
49
+	@Override
50
+	public void repaint(int x, int y, int width, int height) {
51
+		root.repaint(x, y, width, height);
52
+	}
53
+
54
+	@Override
55
+	public void setCursor(Cursor cursor) {
56
+		int translated = java.awt.Cursor.DEFAULT_CURSOR;
57
+		switch (cursor) {
58
+			case NORMAL:
59
+				translated = java.awt.Cursor.DEFAULT_CURSOR;
60
+				break;
61
+			case MOVE:
62
+				translated = java.awt.Cursor.MOVE_CURSOR;
63
+				break;
64
+			case HAND:
65
+				translated = java.awt.Cursor.HAND_CURSOR;
66
+				break;
67
+			case TEXT:
68
+				translated = java.awt.Cursor.TEXT_CURSOR;
69
+				break;
70
+			case E_RESIZE:
71
+				translated = java.awt.Cursor.E_RESIZE_CURSOR;
72
+				break;
73
+			case S_RESIZE:
74
+				translated = java.awt.Cursor.S_RESIZE_CURSOR;
75
+				break;
76
+			case NE_RESIZE:
77
+				translated = java.awt.Cursor.NE_RESIZE_CURSOR;
78
+				break;
79
+			case NW_RESIZE:
80
+				translated = java.awt.Cursor.NW_RESIZE_CURSOR;
81
+				break;
82
+		}
83
+		root.setCursor(java.awt.Cursor.getPredefinedCursor(translated));
84
+	}
85
+
86
+	@Override
87
+	public DFontMetrics getFontMetrics(DFont font) {
88
+		if (graphics == null)
89
+			graphics = root.getGraphics();
90
+		
91
+		SwingCanvas.prepare(font);
92
+		return new SwingFontMetrics(graphics.getFontMetrics((Font) font.cached), graphics);
93
+	}
94
+	
95
+	@Override
96
+	public void focus(DComponent component) {
97
+		this.root.focus(component);
98
+	}
99
+
100
+	@Override
101
+	public void scrollInView(int x, int y, int width, int height) {
102
+		// not in a scrollable context
103
+	}
104
+	
105
+	private class PathTracer implements DPathTracer {
106
+		private final GeneralPath path;
107
+		
108
+		public PathTracer(GeneralPath path) {
109
+			this.path = path;
110
+		}
111
+
112
+		@Override
113
+		public void moveTo(float x, float y) {
114
+			path.moveTo(x, y);
115
+		}
116
+
117
+		@Override
118
+		public void lineTo(float x, float y) {
119
+			path.lineTo(x, y);
120
+		}
121
+
122
+		@Override
123
+		public void bezierCubic(float x1, float y1, float x2, float y2, float x3, float y3) {
124
+			path.curveTo(x1, y1, x2, y2, x3, y3);
125
+		}
126
+
127
+		@Override
128
+		public void bezierQuadratic(float x1, float y1, float x2, float y2) {
129
+			path.quadTo(x1, y1, x2, y2);
130
+		}
131
+
132
+		@Override
133
+		public void close() {
134
+			path.closePath();
135
+		}
136
+	}
137
+}

+ 539
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingRoot.java View File

@@ -0,0 +1,539 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.swing;
7
+
8
+import java.awt.Component;
9
+import java.awt.Graphics;
10
+import java.awt.Graphics2D;
11
+import java.awt.Rectangle;
12
+import java.awt.Toolkit;
13
+import java.awt.event.ComponentEvent;
14
+import java.awt.event.ComponentListener;
15
+import java.awt.event.KeyEvent;
16
+import java.awt.event.KeyListener;
17
+import java.awt.event.MouseEvent;
18
+import java.awt.event.MouseListener;
19
+import java.awt.event.MouseMotionListener;
20
+import java.awt.event.MouseWheelEvent;
21
+import java.awt.event.MouseWheelListener;
22
+import org.openzen.drawablegui.DComponent;
23
+import org.openzen.drawablegui.DIRectangle;
24
+import org.openzen.drawablegui.DKeyEvent;
25
+import static org.openzen.drawablegui.DKeyEvent.KeyCode.*;
26
+import org.openzen.drawablegui.DMouseEvent;
27
+import org.openzen.drawablegui.DRectangle;
28
+
29
+/**
30
+ *
31
+ * @author Hoofdgebruiker
32
+ */
33
+public final class SwingRoot extends Component implements ComponentListener, MouseListener, MouseMotionListener, MouseWheelListener, KeyListener {
34
+	public final SwingGraphicsContext context;
35
+	public final DComponent component;
36
+	private DComponent focus = null;
37
+
38
+	public SwingRoot(DComponent root) {
39
+		this.component = root;
40
+		setFocusable(true);
41
+		setFocusTraversalKeysEnabled(false); // prevent tab from being handled by focus traversal
42
+		
43
+		context = new SwingGraphicsContext(Toolkit.getDefaultToolkit().getScreenResolution() / 96.0f, this);
44
+		component.setContext(context);
45
+		component.setBounds(new DRectangle(0, 0, getWidth(), getHeight()));
46
+		addComponentListener(this);
47
+		addMouseListener(this);
48
+		addMouseMotionListener(this);
49
+		addMouseWheelListener(this);
50
+		addKeyListener(this);
51
+	}
52
+	
53
+	public void focus(DComponent component) {
54
+		if (component == focus)
55
+			return;
56
+		
57
+		if (focus != null)
58
+			focus.onFocusLost();
59
+		this.focus = component;
60
+		if (focus != null)
61
+			focus.onFocusGained();
62
+	}
63
+
64
+	@Override
65
+	public void paint(Graphics g) {
66
+		Rectangle clipBounds = g.getClipBounds();
67
+		DIRectangle clipBounds2 = clipBounds == null ? null : new DIRectangle(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);
68
+		SwingCanvas canvas = new SwingCanvas((Graphics2D) g, context, clipBounds2);
69
+		component.paint(canvas);
70
+	}
71
+
72
+	@Override
73
+	public void componentResized(ComponentEvent e) {
74
+		component.setBounds(new DRectangle(0, 0,
75
+				e.getComponent().getWidth(),
76
+				e.getComponent().getHeight()));
77
+
78
+		invalidate();
79
+	}
80
+
81
+	@Override
82
+	public void componentMoved(ComponentEvent e) {
83
+
84
+	}
85
+
86
+	@Override
87
+	public void componentShown(ComponentEvent e) {
88
+
89
+	}
90
+
91
+	@Override
92
+	public void componentHidden(ComponentEvent e) {
93
+
94
+	}
95
+
96
+	@Override
97
+	public void mouseClicked(MouseEvent e) {
98
+		component.onMouseClick(translateMouseEvent(e));
99
+	}
100
+
101
+	@Override
102
+	public void mousePressed(MouseEvent e) {
103
+		component.onMouseDown(translateMouseEvent(e));
104
+	}
105
+
106
+	@Override
107
+	public void mouseReleased(MouseEvent e) {
108
+		component.onMouseRelease(translateMouseEvent(e));
109
+	}
110
+
111
+	@Override
112
+	public void mouseEntered(MouseEvent e) {
113
+		component.onMouseEnter(translateMouseEvent(e));
114
+	}
115
+
116
+	@Override
117
+	public void mouseExited(MouseEvent e) {
118
+		component.onMouseExit(translateMouseEvent(e));
119
+	}
120
+
121
+	@Override
122
+	public void mouseDragged(MouseEvent e) {
123
+		component.onMouseDrag(translateMouseEvent(e));
124
+	}
125
+
126
+	@Override
127
+	public void mouseMoved(MouseEvent e) {
128
+		component.onMouseMove(translateMouseEvent(e));
129
+	}
130
+
131
+	@Override
132
+	public void mouseWheelMoved(MouseWheelEvent e) {
133
+		component.onMouseScroll(translateScrollEvent(e));
134
+	}
135
+
136
+	@Override
137
+	public void keyTyped(KeyEvent e) {
138
+		if (focus == null)
139
+			return;
140
+
141
+		focus.onKeyTyped(translateKeyEvent(e));
142
+	}
143
+
144
+	@Override
145
+	public void keyPressed(KeyEvent e) {
146
+		if (focus == null)
147
+			return;
148
+		
149
+		focus.onKeyPressed(translateKeyEvent(e));
150
+	}
151
+
152
+	@Override
153
+	public void keyReleased(KeyEvent e) {
154
+		if (focus == null)
155
+			return;
156
+		
157
+		focus.onKeyReleased(translateKeyEvent(e));
158
+	}
159
+
160
+	private DMouseEvent translateMouseEvent(MouseEvent e) {
161
+		return new DMouseEvent(e.getX(), e.getY(), getModifiers(e.getModifiersEx()), 0, e.getClickCount());
162
+	}
163
+
164
+	private DMouseEvent translateScrollEvent(MouseWheelEvent e) {
165
+		return new DMouseEvent(e.getX(), e.getY(), getModifiers(e.getModifiersEx()), e.getUnitsToScroll(), e.getClickCount());
166
+	}
167
+	
168
+	private DKeyEvent translateKeyEvent(KeyEvent e) {
169
+		return new DKeyEvent(e.getKeyChar(), getKeyCode(e.getKeyCode()), getModifiers(e.getModifiersEx()));
170
+	}
171
+	
172
+	private DKeyEvent.KeyCode getKeyCode(int code) {
173
+		switch (code) {
174
+			case KeyEvent.VK_ENTER:
175
+				return ENTER;
176
+			case KeyEvent.VK_BACK_SPACE:
177
+				return BACKSPACE;
178
+			case KeyEvent.VK_TAB:
179
+				return TAB;
180
+    		case KeyEvent.VK_CANCEL:
181
+				return DKeyEvent.KeyCode.CANCEL;
182
+    		case KeyEvent.VK_CLEAR:
183
+				return DKeyEvent.KeyCode.CLEAR;
184
+    		case KeyEvent.VK_SHIFT:
185
+				return DKeyEvent.KeyCode.SHIFT;
186
+    		case KeyEvent.VK_CONTROL:
187
+				return DKeyEvent.KeyCode.CONTROL;
188
+    		case KeyEvent.VK_ALT:
189
+				return DKeyEvent.KeyCode.ALT;
190
+    		case KeyEvent.VK_PAUSE:
191
+				return DKeyEvent.KeyCode.PAUSE;
192
+    		case KeyEvent.VK_CAPS_LOCK:
193
+				return DKeyEvent.KeyCode.CAPS_LOCK;
194
+    		case KeyEvent.VK_ESCAPE:
195
+				return DKeyEvent.KeyCode.ESCAPE;
196
+    		case KeyEvent.VK_SPACE:
197
+				return DKeyEvent.KeyCode.SPACE;
198
+    		case KeyEvent.VK_PAGE_UP:
199
+				return DKeyEvent.KeyCode.PAGE_UP;
200
+    		case KeyEvent.VK_PAGE_DOWN:
201
+				return DKeyEvent.KeyCode.PAGE_DOWN;
202
+    		case KeyEvent.VK_END:
203
+				return DKeyEvent.KeyCode.END;
204
+    		case KeyEvent.VK_HOME:
205
+				return DKeyEvent.KeyCode.HOME;
206
+    		case KeyEvent.VK_LEFT:
207
+				return DKeyEvent.KeyCode.LEFT;
208
+    		case KeyEvent.VK_UP:
209
+				return DKeyEvent.KeyCode.UP;
210
+    		case KeyEvent.VK_RIGHT:
211
+				return DKeyEvent.KeyCode.RIGHT;
212
+    		case KeyEvent.VK_DOWN:
213
+				return DKeyEvent.KeyCode.DOWN;
214
+    		case KeyEvent.VK_COMMA:
215
+				return DKeyEvent.KeyCode.COMMA;
216
+    		case KeyEvent.VK_MINUS:
217
+				return DKeyEvent.KeyCode.MINUS;
218
+    		case KeyEvent.VK_PERIOD:
219
+				return DKeyEvent.KeyCode.PERIOD;
220
+    		case KeyEvent.VK_SLASH:
221
+				return DKeyEvent.KeyCode.SLASH;
222
+    		case KeyEvent.VK_0:
223
+				return DKeyEvent.KeyCode.NUM0;
224
+    		case KeyEvent.VK_1:
225
+				return DKeyEvent.KeyCode.NUM1;
226
+    		case KeyEvent.VK_2:
227
+				return DKeyEvent.KeyCode.NUM2;
228
+    		case KeyEvent.VK_3:
229
+				return DKeyEvent.KeyCode.NUM3;
230
+    		case KeyEvent.VK_4:
231
+				return DKeyEvent.KeyCode.NUM4;
232
+    		case KeyEvent.VK_5:
233
+				return DKeyEvent.KeyCode.NUM5;
234
+    		case KeyEvent.VK_6:
235
+				return DKeyEvent.KeyCode.NUM6;
236
+    		case KeyEvent.VK_7:
237
+				return DKeyEvent.KeyCode.NUM7;
238
+    		case KeyEvent.VK_8:
239
+				return DKeyEvent.KeyCode.NUM8;
240
+    		case KeyEvent.VK_9:
241
+				return DKeyEvent.KeyCode.NUM9;
242
+    		case KeyEvent.VK_SEMICOLON:
243
+				return DKeyEvent.KeyCode.SEMICOLON;
244
+    		case KeyEvent.VK_EQUALS:
245
+				return DKeyEvent.KeyCode.EQUALS;
246
+    		case KeyEvent.VK_A:
247
+				return DKeyEvent.KeyCode.A;
248
+    		case KeyEvent.VK_B:
249
+				return DKeyEvent.KeyCode.B;
250
+    		case KeyEvent.VK_C:
251
+				return DKeyEvent.KeyCode.C;
252
+    		case KeyEvent.VK_D:
253
+				return DKeyEvent.KeyCode.D;
254
+    		case KeyEvent.VK_E:
255
+				return DKeyEvent.KeyCode.E;
256
+    		case KeyEvent.VK_F:
257
+				return DKeyEvent.KeyCode.F;
258
+    		case KeyEvent.VK_G:
259
+				return DKeyEvent.KeyCode.G;
260
+    		case KeyEvent.VK_H:
261
+				return DKeyEvent.KeyCode.H;
262
+    		case KeyEvent.VK_I:
263
+				return DKeyEvent.KeyCode.I;
264
+    		case KeyEvent.VK_J:
265
+				return DKeyEvent.KeyCode.J;
266
+    		case KeyEvent.VK_K:
267
+				return DKeyEvent.KeyCode.K;
268
+    		case KeyEvent.VK_L:
269
+				return DKeyEvent.KeyCode.L;
270
+    		case KeyEvent.VK_M:
271
+				return DKeyEvent.KeyCode.M;
272
+    		case KeyEvent.VK_N:
273
+				return DKeyEvent.KeyCode.N;
274
+    		case KeyEvent.VK_O:
275
+				return DKeyEvent.KeyCode.O;
276
+    		case KeyEvent.VK_P:
277
+				return DKeyEvent.KeyCode.P;
278
+    		case KeyEvent.VK_Q:
279
+				return DKeyEvent.KeyCode.Q;
280
+    		case KeyEvent.VK_R:
281
+				return DKeyEvent.KeyCode.R;
282
+    		case KeyEvent.VK_S:
283
+				return DKeyEvent.KeyCode.S;
284
+    		case KeyEvent.VK_T:
285
+				return DKeyEvent.KeyCode.T;
286
+    		case KeyEvent.VK_U:
287
+				return DKeyEvent.KeyCode.U;
288
+    		case KeyEvent.VK_V:
289
+				return DKeyEvent.KeyCode.V;
290
+    		case KeyEvent.VK_W:
291
+				return DKeyEvent.KeyCode.W;
292
+    		case KeyEvent.VK_X:
293
+				return DKeyEvent.KeyCode.X;
294
+    		case KeyEvent.VK_Y:
295
+				return DKeyEvent.KeyCode.Y;
296
+    		case KeyEvent.VK_Z:
297
+				return DKeyEvent.KeyCode.Z;
298
+    		case KeyEvent.VK_OPEN_BRACKET:
299
+				return DKeyEvent.KeyCode.OPEN_BRACKET;
300
+    		case KeyEvent.VK_BACK_SLASH:
301
+				return DKeyEvent.KeyCode.BACKSLASH;
302
+    		case KeyEvent.VK_CLOSE_BRACKET:
303
+				return DKeyEvent.KeyCode.CLOSE_BRACKET;
304
+    		case KeyEvent.VK_NUMPAD0:
305
+				return DKeyEvent.KeyCode.NUMPAD0;
306
+    		case KeyEvent.VK_NUMPAD1:
307
+				return DKeyEvent.KeyCode.NUMPAD1;
308
+    		case KeyEvent.VK_NUMPAD2:
309
+				return DKeyEvent.KeyCode.NUMPAD2;
310
+    		case KeyEvent.VK_NUMPAD3:
311
+				return DKeyEvent.KeyCode.NUMPAD3;
312
+    		case KeyEvent.VK_NUMPAD4:
313
+				return DKeyEvent.KeyCode.NUMPAD4;
314
+    		case KeyEvent.VK_NUMPAD5:
315
+				return DKeyEvent.KeyCode.NUMPAD5;
316
+    		case KeyEvent.VK_NUMPAD6:
317
+				return DKeyEvent.KeyCode.NUMPAD6;
318
+    		case KeyEvent.VK_NUMPAD7:
319
+				return DKeyEvent.KeyCode.NUMPAD7;
320
+    		case KeyEvent.VK_NUMPAD8:
321
+				return DKeyEvent.KeyCode.NUMPAD8;
322
+    		case KeyEvent.VK_NUMPAD9:
323
+				return DKeyEvent.KeyCode.NUMPAD9;
324
+    		case KeyEvent.VK_MULTIPLY:
325
+				return DKeyEvent.KeyCode.MULTIPLY;
326
+    		case KeyEvent.VK_ADD:
327
+				return DKeyEvent.KeyCode.ADD;
328
+			case KeyEvent.VK_SEPARATER:
329
+				return DKeyEvent.KeyCode.SEPARATOR;
330
+    		case KeyEvent.VK_SUBTRACT:
331
+				return DKeyEvent.KeyCode.SUBTRACT;
332
+    		case KeyEvent.VK_DECIMAL:
333
+				return DKeyEvent.KeyCode.DECIMAL;
334
+    		case KeyEvent.VK_DIVIDE:
335
+				return DKeyEvent.KeyCode.DIVIDE;
336
+    		case KeyEvent.VK_DELETE:
337
+				return DKeyEvent.KeyCode.DELETE; /* ASCII DEL */
338
+    		case KeyEvent.VK_NUM_LOCK:
339
+				return DKeyEvent.KeyCode.NUM_LOCK;
340
+    		case KeyEvent.VK_SCROLL_LOCK:
341
+				return DKeyEvent.KeyCode.SCROLL_LOCK;
342
+    		case KeyEvent.VK_F1:
343
+				return DKeyEvent.KeyCode.F1;
344
+    		case KeyEvent.VK_F2:
345
+				return DKeyEvent.KeyCode.F2;
346
+    		case KeyEvent.VK_F3:
347
+				return DKeyEvent.KeyCode.F3;
348
+    		case KeyEvent.VK_F4:
349
+				return DKeyEvent.KeyCode.F4;
350
+    		case KeyEvent.VK_F5:
351
+				return DKeyEvent.KeyCode.F5;
352
+    		case KeyEvent.VK_F6:
353
+				return DKeyEvent.KeyCode.F6;
354
+    		case KeyEvent.VK_F7:
355
+				return DKeyEvent.KeyCode.F7;
356
+    		case KeyEvent.VK_F8:
357
+				return DKeyEvent.KeyCode.F8;
358
+    		case KeyEvent.VK_F9:
359
+				return DKeyEvent.KeyCode.F9;
360
+    		case KeyEvent.VK_F10:
361
+				return DKeyEvent.KeyCode.F10;
362
+    		case KeyEvent.VK_F11:
363
+				return DKeyEvent.KeyCode.F11;
364
+    		case KeyEvent.VK_F12:
365
+				return DKeyEvent.KeyCode.F12;
366
+    		case KeyEvent.VK_F13:
367
+				return DKeyEvent.KeyCode.F13;
368
+    		case KeyEvent.VK_F14:
369
+				return DKeyEvent.KeyCode.F14;
370
+    		case KeyEvent.VK_F15:
371
+				return DKeyEvent.KeyCode.F15;
372
+    		case KeyEvent.VK_F16:
373
+				return DKeyEvent.KeyCode.F16;
374
+    		case KeyEvent.VK_F17:
375
+				return DKeyEvent.KeyCode.F17;
376
+    		case KeyEvent.VK_F18:
377
+				return DKeyEvent.KeyCode.F18;
378
+    		case KeyEvent.VK_F19:
379
+				return DKeyEvent.KeyCode.F19;
380
+    		case KeyEvent.VK_F20:
381
+				return DKeyEvent.KeyCode.F20;
382
+    		case KeyEvent.VK_F21:
383
+				return DKeyEvent.KeyCode.F21;
384
+    		case KeyEvent.VK_F22:
385
+				return DKeyEvent.KeyCode.F22;
386
+    		case KeyEvent.VK_F23:
387
+				return DKeyEvent.KeyCode.F23;
388
+    		case KeyEvent.VK_F24:
389
+				return DKeyEvent.KeyCode.F24;
390
+    		case KeyEvent.VK_PRINTSCREEN:
391
+				return DKeyEvent.KeyCode.PRINTSCREEN;
392
+    		case KeyEvent.VK_INSERT:
393
+				return DKeyEvent.KeyCode.INSERT;
394
+    		case KeyEvent.VK_HELP:
395
+				return DKeyEvent.KeyCode.HELP;
396
+    		case KeyEvent.VK_META:
397
+				return DKeyEvent.KeyCode.META;
398
+    		case KeyEvent.VK_BACK_QUOTE:
399
+				return DKeyEvent.KeyCode.BACKQUOTE;
400
+    		case KeyEvent.VK_QUOTE:
401
+				return DKeyEvent.KeyCode.QUOTE;
402
+    		case KeyEvent.VK_KP_UP:
403
+				return DKeyEvent.KeyCode.KEYPAD_UP;
404
+    		case KeyEvent.VK_KP_DOWN:
405
+				return DKeyEvent.KeyCode.KEYPAD_DOWN;
406
+    		case KeyEvent.VK_KP_LEFT:
407
+				return DKeyEvent.KeyCode.KEYPAD_LEFT;
408
+    		case KeyEvent.VK_KP_RIGHT:
409
+				return DKeyEvent.KeyCode.KEYPAD_RIGHT;
410
+    		case KeyEvent.VK_AMPERSAND:
411
+				return DKeyEvent.KeyCode.AMPERSAND;
412
+    		case KeyEvent.VK_ASTERISK:
413
+				return DKeyEvent.KeyCode.ASTERISK;
414
+    		case KeyEvent.VK_QUOTEDBL:
415
+				return DKeyEvent.KeyCode.QUOTEDBL;
416
+    		case KeyEvent.VK_LESS:
417
+				return DKeyEvent.KeyCode.LESS;
418
+			case KeyEvent.VK_GREATER:
419
+				return DKeyEvent.KeyCode.GREATER;
420
+			case KeyEvent.VK_BRACELEFT:
421
+				return DKeyEvent.KeyCode.BRACELEFT;
422
+			case KeyEvent.VK_BRACERIGHT:
423
+				return DKeyEvent.KeyCode.BRACERIGHT;
424
+    		case KeyEvent.VK_AT:
425
+				return DKeyEvent.KeyCode.AT;
426
+    		case KeyEvent.VK_COLON:
427
+				return DKeyEvent.KeyCode.COLON;
428
+    		case KeyEvent.VK_CIRCUMFLEX:
429
+				return DKeyEvent.KeyCode.CIRCUMFLEX;
430
+    		case KeyEvent.VK_DOLLAR:
431
+				return DKeyEvent.KeyCode.DOLLAR;
432
+    		case KeyEvent.VK_EURO_SIGN:
433
+				return DKeyEvent.KeyCode.EURO_SIGN;
434
+    		case KeyEvent.VK_EXCLAMATION_MARK:
435
+				return DKeyEvent.KeyCode.EXCLAMATION_MARK;
436
+    		case KeyEvent.VK_INVERTED_EXCLAMATION_MARK:
437
+				return DKeyEvent.KeyCode.INVERTED_EXCLAMATION_MARK;
438
+    		case KeyEvent.VK_LEFT_PARENTHESIS:
439
+				return DKeyEvent.KeyCode.LEFT_PARENTHESIS;
440
+    		case KeyEvent.VK_NUMBER_SIGN:
441
+				return DKeyEvent.KeyCode.NUMBER_SIGN;
442
+    		case KeyEvent.VK_PLUS:
443
+				return DKeyEvent.KeyCode.PLUS;
444
+    		case KeyEvent.VK_RIGHT_PARENTHESIS:
445
+				return DKeyEvent.KeyCode.RIGHT_PARENTHESIS;
446
+    		case KeyEvent.VK_UNDERSCORE:
447
+				return DKeyEvent.KeyCode.UNDERSCORE;
448
+    		case KeyEvent.VK_WINDOWS:
449
+				return DKeyEvent.KeyCode.WINDOWS;
450
+    		case KeyEvent.VK_CONTEXT_MENU:
451
+				return DKeyEvent.KeyCode.CONTEXT_MENU;
452
+    		case KeyEvent.VK_FINAL:
453
+				return DKeyEvent.KeyCode.FINAL;
454
+    		case KeyEvent.VK_CONVERT:
455
+				return DKeyEvent.KeyCode.CONVERT;
456
+    		case KeyEvent.VK_NONCONVERT:
457
+				return DKeyEvent.KeyCode.NONCONVERT;
458
+    		case KeyEvent.VK_ACCEPT:
459
+				return DKeyEvent.KeyCode.ACCEPT;
460
+    		case KeyEvent.VK_KANA:
461
+				return DKeyEvent.KeyCode.KANA;
462
+    		case KeyEvent.VK_KANJI:
463
+				return DKeyEvent.KeyCode.KANJI;
464
+    		case KeyEvent.VK_ALPHANUMERIC:
465
+				return DKeyEvent.KeyCode.ALPHANUMERIC;
466
+    		case KeyEvent.VK_KATAKANA:
467
+				return DKeyEvent.KeyCode.KATAKANA;
468
+    		case KeyEvent.VK_HIRAGANA:
469
+				return DKeyEvent.KeyCode.HIRAGANA;
470
+    		case KeyEvent.VK_FULL_WIDTH:
471
+				return DKeyEvent.KeyCode.FULL_WIDTH;
472
+    		case KeyEvent.VK_HALF_WIDTH:
473
+				return DKeyEvent.KeyCode.HALF_WIDTH;
474
+    		case KeyEvent.VK_ROMAN_CHARACTERS:
475
+				return DKeyEvent.KeyCode.ROMAN_CHARACTERS;
476
+    		case KeyEvent.VK_ALL_CANDIDATES:
477
+				return DKeyEvent.KeyCode.ALL_CANDIDATES;
478
+    		case KeyEvent.VK_PREVIOUS_CANDIDATE:
479
+				return DKeyEvent.KeyCode.PREVIOUS_CANDIDATE;
480
+    		case KeyEvent.VK_CODE_INPUT:
481
+				return DKeyEvent.KeyCode.CODE_INPUT;
482
+    		case KeyEvent.VK_JAPANESE_KATAKANA:
483
+				return DKeyEvent.KeyCode.JAPANESE_KATAKANA;
484
+    		case KeyEvent.VK_JAPANESE_HIRAGANA:
485
+				return DKeyEvent.KeyCode.JAPANESE_HIRAGANA;
486
+    		case KeyEvent.VK_JAPANESE_ROMAN:
487
+				return DKeyEvent.KeyCode.JAPANESE_ROMAN;
488
+			case KeyEvent.VK_KANA_LOCK:
489
+				return DKeyEvent.KeyCode.KANA_LOCK;
490
+    		case KeyEvent.VK_INPUT_METHOD_ON_OFF:
491
+				return DKeyEvent.KeyCode.INPUT_METHOD_ON_OFF;
492
+    		case KeyEvent.VK_CUT:
493
+				return DKeyEvent.KeyCode.CUT;
494
+    		case KeyEvent.VK_COPY:
495
+				return DKeyEvent.KeyCode.COPY;
496
+    		case KeyEvent.VK_PASTE:
497
+				return DKeyEvent.KeyCode.PASTE;
498
+    		case KeyEvent.VK_UNDO:
499
+				return DKeyEvent.KeyCode.UNDO;
500
+    		case KeyEvent.VK_AGAIN:
501
+				return DKeyEvent.KeyCode.AGAIN;
502
+    		case KeyEvent.VK_FIND:
503
+				return DKeyEvent.KeyCode.FIND;
504
+    		case KeyEvent.VK_PROPS:
505
+				return DKeyEvent.KeyCode.PROPS;
506
+    		case KeyEvent.VK_STOP:
507
+				return DKeyEvent.KeyCode.STOP;
508
+    		case KeyEvent.VK_COMPOSE:
509
+				return DKeyEvent.KeyCode.COMPOSE;
510
+    		case KeyEvent.VK_ALT_GRAPH:
511
+				return DKeyEvent.KeyCode.ALT_GRAPH;
512
+    		case KeyEvent.VK_BEGIN:
513
+				return DKeyEvent.KeyCode.BEGIN;
514
+			default:
515
+				return DKeyEvent.KeyCode.UNKNOWN;
516
+		}
517
+	}
518
+
519
+	private int getModifiers(int swingModifiers) {
520
+		int result = 0;
521
+		if ((swingModifiers & MouseEvent.BUTTON1_DOWN_MASK) > 0)
522
+			result |= DMouseEvent.BUTTON1;
523
+		if ((swingModifiers & MouseEvent.BUTTON2_DOWN_MASK) > 0)
524
+			result |= DMouseEvent.BUTTON2;
525
+		if ((swingModifiers & MouseEvent.BUTTON3_DOWN_MASK) > 0)
526
+			result |= DMouseEvent.BUTTON3;
527
+		if ((swingModifiers & MouseEvent.SHIFT_DOWN_MASK) > 0)
528
+			result |= DMouseEvent.SHIFT;
529
+		if ((swingModifiers & MouseEvent.CTRL_DOWN_MASK) > 0)
530
+			result |= DMouseEvent.CTRL;
531
+		if ((swingModifiers & MouseEvent.ALT_DOWN_MASK) > 0)
532
+			result |= DMouseEvent.ALT;
533
+		if ((swingModifiers & MouseEvent.ALT_GRAPH_DOWN_MASK) > 0)
534
+			result |= DMouseEvent.ALT_GRAPH;
535
+		if ((swingModifiers & MouseEvent.META_DOWN_MASK) > 0)
536
+			result |= DMouseEvent.META;
537
+		return result;
538
+	}
539
+}

+ 30
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingWindow.java View File

@@ -0,0 +1,30 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.swing;
7
+
8
+import java.awt.BorderLayout;
9
+import javax.swing.JFrame;
10
+import org.openzen.drawablegui.DComponent;
11
+import org.openzen.drawablegui.DDrawingContext;
12
+
13
+/**
14
+ *
15
+ * @author Hoofdgebruiker
16
+ */
17
+public final class SwingWindow extends JFrame {
18
+	private final SwingRoot swingComponent;
19
+	
20
+	public SwingWindow(String title, DComponent root) {
21
+		super(title);
22
+		
23
+		getContentPane().add(swingComponent = new SwingRoot(root), BorderLayout.CENTER);
24
+		swingComponent.requestFocusInWindow();
25
+	}
26
+	
27
+	public DDrawingContext getContext() {
28
+		return swingComponent.context;
29
+	}
30
+}

+ 35
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/CollapsedArrow.java View File

@@ -0,0 +1,35 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DTransform2D;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class CollapsedArrow implements DDrawable {
17
+	public static final CollapsedArrow INSTANCE = new CollapsedArrow();
18
+	
19
+	private CollapsedArrow() {}
20
+
21
+	@Override
22
+	public void draw(DCanvas canvas, DTransform2D transform) {
23
+		ColoredCollapsedArrow.INSTANCE.draw(canvas, transform, 0xFF000000);
24
+	}
25
+
26
+	@Override
27
+	public float getNominalWidth() {
28
+		return 24;
29
+	}
30
+
31
+	@Override
32
+	public float getNominalHeight() {
33
+		return 24;
34
+	}
35
+}

+ 39
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/ColoredCollapsedArrow.java View File

@@ -0,0 +1,39 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.drawablegui.DColorableIcon;
12
+
13
+public class ColoredCollapsedArrow implements DColorableIcon {
14
+	public static final ColoredCollapsedArrow INSTANCE = new ColoredCollapsedArrow();
15
+	
16
+	private ColoredCollapsedArrow() {}
17
+	
18
+	private static final DPath PATH_0 = tracer -> {
19
+		tracer.moveTo(10, 19);
20
+		tracer.lineTo(17, 12);
21
+		tracer.lineTo(10, 5);
22
+		tracer.close();
23
+	};
24
+	
25
+	@Override
26
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
27
+		canvas.fillPath(PATH_0, transform, color);
28
+	}
29
+
30
+	@Override
31
+	public float getNominalWidth() {
32
+		return 24;
33
+	}
34
+
35
+	@Override
36
+	public float getNominalHeight() {
37
+		return 24;
38
+	}
39
+}

+ 29
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/DTreeNode.java View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import org.openzen.drawablegui.DColorableIcon;
9
+import org.openzen.drawablegui.DMouseEvent;
10
+import org.openzen.drawablegui.live.LiveBool;
11
+import org.openzen.drawablegui.live.LiveList;
12
+
13
+/**
14
+ *
15
+ * @author Hoofdgebruiker
16
+ */
17
+public interface DTreeNode<N extends DTreeNode<N>> {
18
+	DColorableIcon getIcon();
19
+	
20
+	String getTitle();
21
+	
22
+	LiveList<N> getChildren();
23
+	
24
+	LiveBool isCollapsed();
25
+	
26
+	boolean isLeaf();
27
+	
28
+	default void onMouseClick(DMouseEvent e) {}
29
+}

+ 221
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/DTreeView.java View File

@@ -0,0 +1,221 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import java.io.Closeable;
9
+import java.util.ArrayList;
10
+import java.util.List;
11
+import org.openzen.drawablegui.DRectangle;
12
+import org.openzen.drawablegui.DCanvas;
13
+import org.openzen.drawablegui.DComponent;
14
+import org.openzen.drawablegui.DDimensionPreferences;
15
+import org.openzen.drawablegui.DDrawable;
16
+import org.openzen.drawablegui.DDrawingContext;
17
+import org.openzen.drawablegui.DMouseEvent;
18
+import org.openzen.drawablegui.DTransform2D;
19
+import org.openzen.drawablegui.listeners.ListenerHandle;
20
+import org.openzen.drawablegui.live.LiveBool;
21
+import org.openzen.drawablegui.live.LiveObject;
22
+import org.openzen.drawablegui.live.SimpleLiveObject;
23
+
24
+/**
25
+ *
26
+ * @author Hoofdgebruiker
27
+ */
28
+public class DTreeView<N extends DTreeNode<N>> implements DComponent {
29
+	private final LiveObject<DDimensionPreferences> dimensionPreferences = new SimpleLiveObject<>(DDimensionPreferences.EMPTY);
30
+	private DRectangle bounds;
31
+	
32
+	private int selectedRow = -1;
33
+	private N selectedNode = null;
34
+	
35
+	private final DTreeViewStyle style;
36
+	
37
+	private DDrawingContext context;
38
+	private final N root;
39
+	private final boolean showRoot;
40
+	private final List<Row> rows = new ArrayList<>();
41
+	
42
+	public DTreeView(DTreeViewStyle style, N root, boolean showRoot) {
43
+		this.style = style;
44
+		
45
+		this.root = root;
46
+		this.showRoot = showRoot;
47
+	}
48
+
49
+	@Override
50
+	public void setContext(DDrawingContext context) {
51
+		this.context = context;
52
+		updateLayout();
53
+	}
54
+
55
+	@Override
56
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
57
+		return dimensionPreferences;
58
+	}
59
+	
60
+	@Override
61
+	public DRectangle getBounds() {
62
+		return bounds;
63
+	}
64
+
65
+	@Override
66
+	public void setBounds(DRectangle bounds) {
67
+		this.bounds = bounds;
68
+	}
69
+
70
+	@Override
71
+	public void paint(DCanvas canvas) {
72
+		canvas.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height, style.backgroundColor);
73
+		
74
+		int drawX = bounds.x + style.padding;
75
+		int drawY = bounds.y + style.padding;
76
+		if (showRoot) {
77
+			paintNode(canvas, root, drawX, drawY);
78
+		} else {
79
+			for (N child : root.getChildren()) {
80
+				drawY = paintNode(canvas, child, drawX, drawY);
81
+				drawY += style.rowSpacing;
82
+			}
83
+		}
84
+	}
85
+	
86
+	@Override
87
+	public void onMouseClick(DMouseEvent e) {
88
+		int row = yToRow(e.y);
89
+		if (row >= 0 && row < rows.size()) {
90
+			Row rowEntry = rows.get(row);
91
+			if (e.x >= rowEntry.x && e.x < (rowEntry.x + style.nodeOpenedIcon.getNominalWidth())) {
92
+				if (!rowEntry.node.isLeaf())
93
+					rowEntry.node.isCollapsed().toggle();
94
+				return;
95
+			}
96
+			
97
+			rowEntry.node.onMouseClick(e);
98
+			
99
+			if (e.isSingleClick()) {
100
+				int oldRow = selectedRow;
101
+				selectedRow = row;
102
+				selectedNode = rows.get(row).node;
103
+
104
+				if (oldRow >= 0)
105
+					context.repaint(
106
+							bounds.x,
107
+							rowToY(oldRow) - style.selectedPaddingTop,
108
+							bounds.width,
109
+							style.font.size + style.selectedPaddingTop + style.selectedPaddingBottom);
110
+				context.repaint(
111
+						bounds.x,
112
+						rowToY(row) - style.selectedPaddingTop,
113
+						bounds.width,
114
+						style.font.size + style.selectedPaddingTop + style.selectedPaddingBottom);
115
+			}
116
+		}
117
+	}
118
+	
119
+	private int rowToY(int row) {
120
+		return row * (style.rowSpacing + style.font.size) + style.padding;
121
+	}
122
+	
123
+	private int yToRow(int y) {
124
+		return (y - bounds.y - style.padding) / (style.rowSpacing + style.font.size);
125
+	}
126
+	
127
+	private int paintNode(DCanvas canvas, N node, int drawX, int drawY) {
128
+		int textColor = style.nodeTextColor;
129
+		if (node == selectedNode) {
130
+			textColor = style.selectedNodeTextColor;
131
+			canvas.fillRectangle(bounds.x, drawY - style.selectedPaddingTop, bounds.width, style.font.size + style.selectedPaddingTop + style.selectedPaddingBottom, style.selectedBackgroundColor);
132
+		}
133
+		
134
+		int drawingX = drawX;
135
+		if (!node.isLeaf()) {
136
+			DDrawable icon = node.isCollapsed().getValue() ? style.nodeClosedIcon : style.nodeOpenedIcon;
137
+			icon.draw(canvas, DTransform2D.translate(drawingX, drawY + style.font.size - icon.getNominalHeight()));
138
+			drawingX += style.iconTextSpacing + icon.getNominalWidth();
139
+		} else {
140
+			drawingX += style.iconTextSpacing + style.nodeClosedIcon.getNominalWidth();
141
+		}
142
+		node.getIcon().draw(canvas, DTransform2D.translate(drawingX, drawY + style.font.size - node.getIcon().getNominalHeight()), textColor);
143
+		drawingX += style.iconTextSpacing + node.getIcon().getNominalWidth();
144
+		canvas.drawText(style.font, textColor, drawingX, drawY + style.font.size + style.textShift, node.getTitle());
145
+		drawY += style.font.size;
146
+		
147
+		if (!node.isCollapsed().getValue()) {
148
+			for (N child : node.getChildren()) {
149
+				drawY += style.rowSpacing;
150
+				drawY = paintNode(canvas, child, drawX + style.indent, drawY);
151
+			}
152
+		}
153
+		
154
+		return drawY;
155
+	}
156
+	
157
+	private void updateLayout() {
158
+		int oldRowCount = rows.size();
159
+		
160
+		for (Row row : rows)
161
+			row.close();
162
+		
163
+		rows.clear();
164
+		
165
+		if (showRoot) {
166
+			updateLayout(root, 0);
167
+		} else {
168
+			for (N child : root.getChildren()) {
169
+				updateLayout(child, 0);
170
+			}
171
+		}
172
+		
173
+		if (rows.size() != oldRowCount) {
174
+			DDimensionPreferences preferences = dimensionPreferences.getValue();
175
+			dimensionPreferences.setValue(
176
+					new DDimensionPreferences(
177
+							preferences.minimumWidth,
178
+							preferences.minimumHeight,
179
+							preferences.preferredWidth,
180
+							rowToY(rows.size()),
181
+							preferences.maximumWidth,
182
+							1000000));
183
+		}
184
+		
185
+		System.out.println("Rows after updateLayout: " + rows.size());
186
+		System.out.println("Height: " + rowToY(rows.size()));
187
+	}
188
+	
189
+	private void updateLayout(N node, int x) {
190
+		rows.add(new Row(x, node));
191
+		
192
+		if (!node.isCollapsed().getValue()) {
193
+			for (N child : node.getChildren()) {
194
+				updateLayout(child, x + style.indent);
195
+			}
196
+		}
197
+	}
198
+	
199
+	private class Row implements Closeable, LiveBool.Listener {
200
+		private final int x;
201
+		private final N node;
202
+		private final ListenerHandle<LiveBool.Listener> collapseListener;
203
+		
204
+		public Row(int x, N node) {
205
+			this.x = x;
206
+			this.node = node;
207
+			this.collapseListener = node.isCollapsed().addListener(this);
208
+		}
209
+
210
+		@Override
211
+		public void onChanged(boolean oldValue, boolean newValue) {
212
+			updateLayout();
213
+			context.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
214
+		}
215
+		
216
+		@Override
217
+		public void close() {
218
+			collapseListener.close();
219
+		}
220
+	}
221
+}

+ 52
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/DTreeViewStyle.java View File

@@ -0,0 +1,52 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import org.openzen.drawablegui.DDrawable;
9
+import org.openzen.drawablegui.DFont;
10
+import org.openzen.drawablegui.DFontFamily;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class DTreeViewStyle {
17
+	public static DTreeViewStyle DEFAULT = new DTreeViewStyle();
18
+	
19
+	public final DDrawable nodeOpenedIcon;
20
+	public final DDrawable nodeClosedIcon;
21
+	public final int backgroundColor;
22
+	public final int borderColor;
23
+	public final int padding;
24
+	public final int rowSpacing;
25
+	public final DFont font;
26
+	public final int indent;
27
+	public final int textShift;
28
+	public final int iconTextSpacing;
29
+	public final int nodeTextColor;
30
+	public final int selectedNodeTextColor;
31
+	public final int selectedBackgroundColor;
32
+	public final int selectedPaddingTop;
33
+	public final int selectedPaddingBottom;
34
+	
35
+	private DTreeViewStyle() {
36
+		nodeOpenedIcon = ExpandedArrow.INSTANCE;
37
+		nodeClosedIcon = CollapsedArrow.INSTANCE;
38
+		backgroundColor = 0xFFFFFFFF;
39
+		borderColor = 0xFF888888;
40
+		padding = 5;
41
+		rowSpacing = 10;
42
+		font = new DFont(DFontFamily.UI, false, false, false, 22);
43
+		indent = 20;
44
+		textShift = -4;
45
+		iconTextSpacing = 3;
46
+		nodeTextColor = 0xFF000000;
47
+		selectedNodeTextColor = 0xFFFFFFFF;
48
+		selectedBackgroundColor = 0xFF007ACC;
49
+		selectedPaddingTop = 3;
50
+		selectedPaddingBottom = 3;
51
+	}
52
+}

+ 35
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/ExpandedArrow.java View File

@@ -0,0 +1,35 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DTransform2D;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class ExpandedArrow implements DDrawable {
17
+	public static final ExpandedArrow INSTANCE = new ExpandedArrow();
18
+	
19
+	private ExpandedArrow() {}
20
+
21
+	@Override
22
+	public void draw(DCanvas canvas, DTransform2D transform) {
23
+		ExpandedColoredArrow.INSTANCE.draw(canvas, transform, 0xFF000000);
24
+	}
25
+
26
+	@Override
27
+	public float getNominalWidth() {
28
+		return 24;
29
+	}
30
+
31
+	@Override
32
+	public float getNominalHeight() {
33
+		return 24;
34
+	}
35
+}

+ 39
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/tree/ExpandedColoredArrow.java View File

@@ -0,0 +1,39 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.tree;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.drawablegui.DColorableIcon;
12
+
13
+public class ExpandedColoredArrow implements DColorableIcon {
14
+	public static final ExpandedColoredArrow INSTANCE = new ExpandedColoredArrow();
15
+	
16
+	private ExpandedColoredArrow() {}
17
+	
18
+	private static final DPath PATH_0 = tracer -> {
19
+		tracer.moveTo(17, 7);
20
+		tracer.lineTo(17, 17);
21
+		tracer.lineTo(7, 17);
22
+		tracer.close();
23
+	};
24
+	
25
+	@Override
26
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
27
+		canvas.fillPath(PATH_0, transform, color);
28
+	}
29
+
30
+	@Override
31
+	public float getNominalWidth() {
32
+		return 24;
33
+	}
34
+
35
+	@Override
36
+	public float getNominalHeight() {
37
+		return 24;
38
+	}
39
+}

+ 24
- 0
DrawableGuiIconConverter/build.gradle View File

@@ -0,0 +1,24 @@
1
+// Note: "common.gradle" in the root project contains additional initialization
2
+//   for this project. This initialization is applied in the "build.gradle"
3
+//   of the root project.
4
+
5
+// NetBeans will automatically add "run" and "debug" tasks relying on the
6
+// "mainClass" property. You may however define the property prior executing
7
+// tasks by passing a "-PmainClass=<QUALIFIED_CLASS_NAME>" argument.
8
+//
9
+// Note however, that you may define your own "run" and "debug" task if you
10
+// prefer. In this case NetBeans will not add these tasks but you may rely on
11
+// your own implementation.
12
+if (!hasProperty('mainClass')) {
13
+    ext.mainClass = 'org.openzen.drawablegui.iconconverter.Main'
14
+}
15
+
16
+dependencies {
17
+    // TODO: Add dependencies here
18
+    //   but note that JUnit should have already been added in parent.gradle.
19
+    //   By default, only the Maven Central Repository is specified in
20
+    //   parent.gradle.
21
+    //
22
+    // You can read more about how to add dependency here:
23
+    //   http://www.gradle.org/docs/current/userguide/dependency_management.html#sec:how_to_declare_your_dependencies
24
+}

+ 275
- 0
DrawableGuiIconConverter/src/main/java/org/openzen/drawablegui/iconconverter/Main.java View File

@@ -0,0 +1,275 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.iconconverter;
7
+
8
+import java.io.File;
9
+import java.util.ArrayList;
10
+import java.util.List;
11
+import javax.xml.parsers.DocumentBuilder;
12
+import javax.xml.parsers.DocumentBuilderFactory;
13
+import org.w3c.dom.Document;
14
+import org.w3c.dom.Element;
15
+import org.w3c.dom.Node;
16
+import org.w3c.dom.NodeList;
17
+
18
+/**
19
+ *
20
+ * @author Hoofdgebruiker
21
+ */
22
+public class Main {
23
+	public static void main(String[] args) throws Exception {
24
+		String filename = "baseline-folder-24px.svg"; //args[0];
25
+		//String filename = "baseline-dashboard-24px.svg";
26
+		String className = "ColorableFolderIcon";
27
+		File file = new File(filename);
28
+		if (!file.exists()) {
29
+			System.out.println("No such file: " + filename);
30
+			return;
31
+		}
32
+		
33
+		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
34
+		DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
35
+		Document doc = dBuilder.parse(file);
36
+		doc.getDocumentElement().normalize();
37
+		
38
+		List<String> pathNames = new ArrayList<>();
39
+		
40
+		Element rootElement = doc.getDocumentElement();
41
+		String width = rootElement.getAttribute("width");
42
+		String height = rootElement.getAttribute("height");
43
+		StringBuilder output = new StringBuilder();
44
+		output.append("import org.openzen.drawablegui.DCanvas;\n");
45
+		output.append("import org.openzen.drawablegui.DPath;\n");
46
+		output.append("import org.openzen.drawablegui.DTransform2D;\n");
47
+		output.append("import org.openzen.drawablegui.DColorableIcon;\n");
48
+		output.append("\n");
49
+		output.append("public class ").append(className).append(" implements DColorableIcon {\n");
50
+		output.append("\tpublic static final ").append(className).append(" INSTANCE = new ").append(className).append("();\n");
51
+		output.append("\t\n");
52
+		output.append("\tprivate ").append(className).append("() {}\n");
53
+		output.append("\t\n");
54
+		
55
+		NodeList childNodes = rootElement.getChildNodes();
56
+		for (int i = 0; i < childNodes.getLength(); i++) {
57
+			Node node = childNodes.item(i);
58
+			if (node.getNodeName().equals("path")) {
59
+				Element pathElement = (Element)node;
60
+				String pathName = "PATH_" + pathNames.size();
61
+				output.append("\tprivate static final DPath ").append(pathName).append(" = tracer -> {\n");
62
+				convertPath(pathElement.getAttribute("d"), output, "\t\t");
63
+				output.append("\t};\n");
64
+				pathNames.add(pathName);
65
+			} else if (node.getNodeName().equals("#text")) {
66
+				// skip
67
+			} else {
68
+				System.out.println("Warning: " + node.getNodeName() + " not supported");
69
+			}
70
+		}
71
+		
72
+		output.append("\t\n");
73
+		output.append("\t@Override\n");
74
+		output.append("\tpublic void draw(DCanvas canvas, DTransform2D transform, int color) {\n");
75
+		for (String pathName : pathNames) {
76
+			output.append("\t\tcanvas.fillPath(").append(pathName).append(", transform, color);\n");
77
+		}
78
+		output.append("\t}\n");
79
+		output.append("\n");
80
+		output.append("\t@Override\n");
81
+		output.append("\tpublic float getNominalWidth() {\n");
82
+		output.append("\t\treturn ").append(width).append(";\n");
83
+		output.append("\t}\n");
84
+		output.append("\n");
85
+		output.append("\t@Override\n");
86
+		output.append("\tpublic float getNominalHeight() {\n");
87
+		output.append("\t\treturn ").append(height).append(";\n");
88
+		output.append("\t}\n");
89
+		output.append("}\n");
90
+		System.out.println(output.toString());
91
+	}
92
+	
93
+	private static void convertPath(String path, StringBuilder output, String indent) {
94
+		CharStream stream = new CharStream(path);
95
+		float x = 0;
96
+		float y = 0;
97
+		char instruction = stream.next();
98
+		while (stream.hasMore()) {
99
+			if (!stream.nextIsNumber())
100
+				instruction = stream.next();
101
+			
102
+			switch (instruction) {
103
+				case 'M': {
104
+					String sx = stream.nextFloat();
105
+					String sy = stream.nextFloat();
106
+					output.append(indent).append("tracer.moveTo(")
107
+							.append(sx).append("f, ")
108
+							.append(sy).append("f);\n");
109
+					x = Float.parseFloat(sx);
110
+					y = Float.parseFloat(sy);
111
+					break;
112
+				}
113
+				case 'm': {
114
+					x += stream.parseFloat();
115
+					y += stream.parseFloat();
116
+					output.append(indent)
117
+							.append("tracer.moveTo(")
118
+							.append(x).append("f, ")
119
+							.append(y).append("f);\n");
120
+					break;
121
+				}
122
+				case 'L': {
123
+					String sx = stream.nextFloat();
124
+					String sy = stream.nextFloat();
125
+					output.append(indent).append("tracer.lineTo(")
126
+							.append(sx).append("f, ")
127
+							.append(sy).append("f);\n");
128
+					x = Float.parseFloat(sx);
129
+					y = Float.parseFloat(sy);
130
+					break;
131
+				}
132
+				case 'l': {
133
+					x += stream.parseFloat();
134
+					y += stream.parseFloat();
135
+					output.append(indent)
136
+							.append("tracer.lineTo(")
137
+							.append(x).append("f, ")
138
+							.append(y).append("f);\n");
139
+					break;
140
+				}
141
+				case 'H': {
142
+					x = stream.parseFloat();
143
+					output.append(indent)
144
+							.append("tracer.lineTo(")
145
+							.append(x).append("f, ")
146
+							.append(y).append("f);\n");
147
+					break;
148
+				}
149
+				case 'h': {
150
+					x += stream.parseFloat();
151
+					output.append(indent)
152
+							.append("tracer.lineTo(")
153
+							.append(x).append("f, ")
154
+							.append(y).append("f);\n");
155
+					break;
156
+				}
157
+				case 'V': {
158
+					y = stream.parseFloat();
159
+					output.append(indent)
160
+							.append("tracer.lineTo(")
161
+							.append(x).append("f, ")
162
+							.append(y).append("f);\n");
163
+					break;
164
+				}
165
+				case 'v': {
166
+					y += stream.parseFloat();
167
+					output.append(indent)
168
+							.append("tracer.lineTo(")
169
+							.append(x).append("f, ")
170
+							.append(y).append("f);\n");
171
+					break;
172
+				}
173
+				case 'C': {
174
+					float x1 = stream.parseFloat();
175
+					float y1 = stream.parseFloat();
176
+					float x2 = stream.parseFloat();
177
+					float y2 = stream.parseFloat();
178
+					float x3 = stream.parseFloat();
179
+					float y3 = stream.parseFloat();
180
+					output.append(indent)
181
+							.append("tracer.bezierCubic(")
182
+							.append(x1).append("f, ")
183
+							.append(y1).append("f, ")
184
+							.append(x2).append("f, ")
185
+							.append(y2).append("f, ")
186
+							.append(x3).append("f, ")
187
+							.append(y3).append("f);\n");
188
+					x = x3;
189
+					y = y3;
190
+					break;
191
+				}
192
+				case 'c': {
193
+					float x1 = x + stream.parseFloat();
194
+					float y1 = y + stream.parseFloat();
195
+					float x2 = x + stream.parseFloat();
196
+					float y2 = y + stream.parseFloat();
197
+					float x3 = x + stream.parseFloat();
198
+					float y3 = y + stream.parseFloat();
199
+					x = x3;
200
+					y = y3;
201
+					output.append(indent)
202
+							.append("tracer.bezierCubic(")
203
+							.append(x1).append("f, ")
204
+							.append(y1).append("f, ")
205
+							.append(x2).append("f, ")
206
+							.append(y2).append("f, ")
207
+							.append(x3).append("f, ")
208
+							.append(y3).append("f);\n");
209
+					break;
210
+				}
211
+				case 'Z':
212
+				case 'z':
213
+					output.append(indent).append("tracer.close();\n");
214
+					break;
215
+				default:
216
+					System.out.println("Error: path instruction " + instruction + " not supported");
217
+					return;
218
+			}
219
+		}
220
+	}
221
+	
222
+	private static class CharStream {
223
+		private int index;
224
+		private final String value;
225
+		
226
+		public CharStream(String value) {
227
+			this.index = 0;
228
+			this.value = value;
229
+		}
230
+		
231
+		public char next() {
232
+			skipWhitespace();
233
+			return value.charAt(index++);
234
+		}
235
+		
236
+		public boolean hasMore() {
237
+			return index < value.length();
238
+		}
239
+		
240
+		public boolean nextIsNumber() {
241
+			skipWhitespace();
242
+			char c = value.charAt(index);
243
+			return c == '-' || (c >= '0' && c <= '9');
244
+		}
245
+		
246
+		public String nextFloat() {
247
+			skipWhitespace();
248
+			int from = index;
249
+			if (value.charAt(index) == '-')
250
+				index++;
251
+			
252
+			char next = value.charAt(index);
253
+			while (next >= '0' && next <= '9')
254
+				next = value.charAt(++index);
255
+			if (value.charAt(index) == '.') {
256
+				index++;
257
+				next = value.charAt(index);
258
+				while (next >= '0' && next <= '9')
259
+					next = value.charAt(++index);
260
+			}
261
+			
262
+			return value.substring(from, index);
263
+		}
264
+		
265
+		public float parseFloat() {
266
+			return Float.parseFloat(nextFloat());
267
+		}
268
+		
269
+		public void skipWhitespace() {
270
+			char next = value.charAt(index);
271
+			while (next == ' ' || next == '\t' || next == '\r' || next == '\n' || next == ',')
272
+				next = value.charAt(++index);
273
+		}
274
+	}
275
+}

+ 19
- 0
IDE/build.gradle View File

@@ -0,0 +1,19 @@
1
+// Note: "common.gradle" in the root project contains additional initialization
2
+//   for this project. This initialization is applied in the "build.gradle"
3
+//   of the root project.
4
+
5
+// NetBeans will automatically add "run" and "debug" tasks relying on the
6
+// "mainClass" property. You may however define the property prior executing
7
+// tasks by passing a "-PmainClass=<QUALIFIED_CLASS_NAME>" argument.
8
+//
9
+// Note however, that you may define your own "run" and "debug" task if you
10
+// prefer. In this case NetBeans will not add these tasks but you may rely on
11
+// your own implementation.
12
+if (!hasProperty('mainClass')) {
13
+    ext.mainClass = 'org.openzen.zenscript.ide.Main'
14
+}
15
+
16
+dependencies {
17
+	compile project(':DrawableGui')
18
+	compile project(':Constructor')
19
+}

+ 35
- 0
IDE/src/main/java/org/openzen/zenscript/ide/Main.java View File

@@ -0,0 +1,35 @@
1
+package org.openzen.zenscript.ide;
2
+
3
+import java.io.File;
4
+import java.io.IOException;
5
+import javax.swing.JFrame;
6
+import org.openzen.zenscript.ide.host.DevelopmentHost;
7
+import org.openzen.zenscript.ide.host.local.LocalProjectDevelopmentHost;
8
+import org.openzen.drawablegui.swing.SwingWindow;
9
+import org.openzen.zenscript.constructor.ModuleLoader;
10
+import org.openzen.zenscript.constructor.Project;
11
+import org.openzen.zenscript.ide.ui.IDEWindow;
12
+import org.openzen.zenscript.ide.ui.view.WindowView;
13
+
14
+public class Main {
15
+    /**
16
+     * @param args the command line arguments
17
+     */
18
+    public static void main(String[] args) throws IOException {
19
+		File directory = new File("../../ZenCode");
20
+		ModuleLoader loader = new ModuleLoader();
21
+		Project project = new Project(loader, directory);
22
+		
23
+		DevelopmentHost host = new LocalProjectDevelopmentHost(project);
24
+		
25
+		int pixelPerInch = java.awt.Toolkit.getDefaultToolkit().getScreenResolution();
26
+		
27
+		IDEWindow window = new IDEWindow();
28
+		WindowView root = new WindowView(window, host);
29
+		
30
+		SwingWindow swingWindow = new SwingWindow("ZenCode IDE - " + host.getName(), root);
31
+		swingWindow.setSize(800 * pixelPerInch / 96, 600 * pixelPerInch / 96);
32
+		swingWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
33
+		swingWindow.setVisible(true);
34
+    }
35
+}

+ 20
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/DevelopmentHost.java View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host;
7
+
8
+import org.openzen.drawablegui.live.LiveList;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface DevelopmentHost {
15
+	public String getName();
16
+	
17
+	public LiveList<IDEModule> getModules();
18
+	
19
+	public LiveList<IDELibrary> getLibraries();
20
+}

+ 18
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/IDELibrary.java View File

@@ -0,0 +1,18 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host;
7
+
8
+import org.openzen.drawablegui.live.LiveList;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface IDELibrary {
15
+	public String getName();
16
+	
17
+	public LiveList<IDEModule> getModules();
18
+}

+ 16
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/IDEModule.java View File

@@ -0,0 +1,16 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface IDEModule {
13
+	public String getName();
14
+	
15
+	public IDEPackage getRootPackage();
16
+}

+ 20
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/IDEPackage.java View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host;
7
+
8
+import org.openzen.drawablegui.live.LiveList;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface IDEPackage {
15
+	public String getName();
16
+	
17
+	public LiveList<IDEPackage> getSubPackages();
18
+	
19
+	public LiveList<IDESourceFile> getSourceFiles();
20
+}

+ 21
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/IDESourceFile.java View File

@@ -0,0 +1,21 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host;
7
+
8
+import java.io.IOException;
9
+import java.io.Reader;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public interface IDESourceFile {
16
+	public String getName();
17
+	
18
+	public Reader read() throws IOException;
19
+	
20
+	public void update(String content);
21
+}

+ 39
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalLibrary.java View File

@@ -0,0 +1,39 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host.local;
7
+
8
+import org.openzen.drawablegui.live.LiveArrayList;
9
+import org.openzen.drawablegui.live.LiveList;
10
+import org.openzen.zenscript.constructor.Library;
11
+import org.openzen.zenscript.constructor.module.ModuleReference;
12
+import org.openzen.zenscript.ide.host.IDELibrary;
13
+import org.openzen.zenscript.ide.host.IDEModule;
14
+
15
+/**
16
+ *
17
+ * @author Hoofdgebruiker
18
+ */
19
+public class LocalLibrary implements IDELibrary {
20
+	private final Library library;
21
+	private final LiveList<IDEModule> modules;
22
+	
23
+	public LocalLibrary(Library library) {
24
+		this.library = library;
25
+		modules = new LiveArrayList<>();
26
+		for (ModuleReference module : library.modules)
27
+			modules.add(new LocalModule(module));
28
+	}
29
+
30
+	@Override
31
+	public String getName() {
32
+		return library.name;
33
+	}
34
+
35
+	@Override
36
+	public LiveList<IDEModule> getModules() {
37
+		return modules;
38
+	}
39
+}

+ 32
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalModule.java View File

@@ -0,0 +1,32 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host.local;
7
+
8
+import org.openzen.zenscript.constructor.module.ModuleReference;
9
+import org.openzen.zenscript.ide.host.IDEModule;
10
+import org.openzen.zenscript.ide.host.IDEPackage;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class LocalModule implements IDEModule {
17
+	private final ModuleReference module;
18
+	
19
+	public LocalModule(ModuleReference module) {
20
+		this.module = module;
21
+	}
22
+
23
+	@Override
24
+	public String getName() {
25
+		return module.getName();
26
+	}
27
+
28
+	@Override
29
+	public IDEPackage getRootPackage() {
30
+		return new LocalPackage(module.getRootPackage());
31
+	}
32
+}

+ 57
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalPackage.java View File

@@ -0,0 +1,57 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host.local;
7
+
8
+import java.util.Arrays;
9
+import org.openzen.drawablegui.live.LiveArrayList;
10
+import org.openzen.drawablegui.live.LiveList;
11
+import org.openzen.zenscript.constructor.module.SourcePackage;
12
+import org.openzen.zenscript.ide.host.IDEPackage;
13
+import org.openzen.zenscript.ide.host.IDESourceFile;
14
+
15
+/**
16
+ *
17
+ * @author Hoofdgebruiker
18
+ */
19
+public class LocalPackage implements IDEPackage {
20
+	private final SourcePackage pkg;
21
+	private final LiveList<IDEPackage> subPackages = new LiveArrayList<>();
22
+	private final LiveList<IDESourceFile> sourceFiles = new LiveArrayList<>();
23
+	
24
+	public LocalPackage(SourcePackage pkg) {
25
+		this.pkg = pkg;
26
+		
27
+		String[] subPackageKeys = pkg.subPackages.keySet()
28
+				.toArray(new String[pkg.subPackages.size()]);
29
+		Arrays.sort(subPackageKeys);
30
+		
31
+		String[] sourceFileKeys = pkg.sourceFiles.keySet()
32
+				.toArray(new String[pkg.sourceFiles.size()]);
33
+		Arrays.sort(sourceFileKeys);
34
+		
35
+		for (String subKey : subPackageKeys) {
36
+			subPackages.add(new LocalPackage(pkg.subPackages.get(subKey)));
37
+		}
38
+		for (String sourceFileKey : sourceFileKeys) {
39
+			sourceFiles.add(new LocalSourceFile(pkg.sourceFiles.get(sourceFileKey)));
40
+		}
41
+	}
42
+	
43
+	@Override
44
+	public String getName() {
45
+		return pkg.name;
46
+	}
47
+
48
+	@Override
49
+	public LiveList<IDEPackage> getSubPackages() {
50
+		return subPackages;
51
+	}
52
+
53
+	@Override
54
+	public LiveList<IDESourceFile> getSourceFiles() {
55
+		return sourceFiles;
56
+	}
57
+}

+ 53
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalProjectDevelopmentHost.java View File

@@ -0,0 +1,53 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host.local;
7
+
8
+import java.util.List;
9
+import org.openzen.drawablegui.live.LiveArrayList;
10
+import org.openzen.drawablegui.live.LiveList;
11
+import org.openzen.zenscript.constructor.Library;
12
+import org.openzen.zenscript.constructor.Project;
13
+import org.openzen.zenscript.constructor.module.ModuleReference;
14
+import org.openzen.zenscript.ide.host.DevelopmentHost;
15
+import org.openzen.zenscript.ide.host.IDEModule;
16
+import org.openzen.zenscript.ide.host.IDELibrary;
17
+
18
+/**
19
+ *
20
+ * @author Hoofdgebruiker
21
+ */
22
+public class LocalProjectDevelopmentHost implements DevelopmentHost {
23
+	private final Project project;
24
+	private final LiveList<IDEModule> modules;
25
+	private final LiveList<IDELibrary> libraries;
26
+	
27
+	public LocalProjectDevelopmentHost(Project project) {
28
+		this.project = project;
29
+		
30
+		modules = new LiveArrayList<>();
31
+		for (ModuleReference module : project.modules)
32
+			modules.add(new LocalModule(module));
33
+		
34
+		libraries = new LiveArrayList<>();
35
+		for (Library library : project.libraries)
36
+			libraries.add(new LocalLibrary(library));
37
+	}
38
+	
39
+	@Override
40
+	public String getName() {
41
+		return project.name;
42
+	}
43
+
44
+	@Override
45
+	public LiveList<IDEModule> getModules() {
46
+		return modules;
47
+	}
48
+
49
+	@Override
50
+	public LiveList<IDELibrary> getLibraries() {
51
+		return libraries;
52
+	}
53
+}

+ 54
- 0
IDE/src/main/java/org/openzen/zenscript/ide/host/local/LocalSourceFile.java View File

@@ -0,0 +1,54 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.host.local;
7
+
8
+import java.io.BufferedInputStream;
9
+import java.io.BufferedOutputStream;
10
+import java.io.FileInputStream;
11
+import java.io.FileOutputStream;
12
+import java.io.IOException;
13
+import java.io.InputStreamReader;
14
+import java.io.OutputStreamWriter;
15
+import java.io.Reader;
16
+import java.io.Writer;
17
+import java.nio.charset.StandardCharsets;
18
+import org.openzen.zenscript.constructor.module.SourceFile;
19
+import org.openzen.zenscript.ide.host.IDESourceFile;
20
+
21
+/**
22
+ *
23
+ * @author Hoofdgebruiker
24
+ */
25
+public class LocalSourceFile implements IDESourceFile {
26
+	private final SourceFile file;
27
+	
28
+	public LocalSourceFile(SourceFile file) {
29
+		this.file = file;
30
+	}
31
+
32
+	@Override
33
+	public String getName() {
34
+		return file.name;
35
+	}
36
+
37
+	@Override
38
+	public Reader read() throws IOException {
39
+		return new InputStreamReader(
40
+				new BufferedInputStream(new FileInputStream(file.file)),
41
+				StandardCharsets.UTF_8);
42
+	}
43
+
44
+	@Override
45
+	public void update(String content) {
46
+		try {
47
+			Writer writer = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(file.file)), StandardCharsets.UTF_8);
48
+			writer.write(content);
49
+			writer.close();
50
+		} catch (IOException ex) {
51
+			ex.printStackTrace();
52
+		}
53
+	}
54
+}

+ 56
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEAspectBar.java View File

@@ -0,0 +1,56 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import java.util.ArrayList;
9
+import java.util.List;
10
+import org.openzen.drawablegui.listeners.ListenerHandle;
11
+import org.openzen.drawablegui.listeners.ListenerList;
12
+
13
+/**
14
+ *
15
+ * @author Hoofdgebruiker
16
+ */
17
+public class IDEAspectBar {
18
+	private final List<IDEAspectToolbar> aspectToolbars = new ArrayList<>();
19
+	private final ListenerList<Listener> listeners = new ListenerList<>();
20
+	
21
+	public ListenerHandle<Listener> addListener(Listener listener) {
22
+		return listeners.add(listener);
23
+	}
24
+	
25
+	public void addToolbar(IDEAspectToolbar toolbar) {
26
+		int index = insertToolbar(toolbar);
27
+		listeners.accept(listener -> listener.onAspectBarAdded(index, toolbar));
28
+	}
29
+	
30
+	public boolean removeToolbar(IDEAspectToolbar toolbar) {
31
+		int index = aspectToolbars.indexOf(toolbar);
32
+		if (index < 0)
33
+			return false;
34
+		
35
+		aspectToolbars.remove(index);
36
+		listeners.accept(listener -> listener.onAspectBarRemoved(index, toolbar));
37
+		return true;
38
+	}
39
+	
40
+	private int insertToolbar(IDEAspectToolbar toolbar) {
41
+		for (int i = 0; i < aspectToolbars.size(); i++) {
42
+			if (toolbar.order < aspectToolbars.get(i).order) {
43
+				aspectToolbars.add(i, toolbar);
44
+				return i;
45
+			}
46
+		}
47
+		int index = aspectToolbars.size();
48
+		aspectToolbars.add(toolbar);
49
+		return index;
50
+	}
51
+	
52
+	public interface Listener {
53
+		void onAspectBarAdded(int index, IDEAspectToolbar toolbar);
54
+		void onAspectBarRemoved(int index, IDEAspectToolbar toolbar);
55
+	}
56
+}

+ 24
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEAspectBarControl.java View File

@@ -0,0 +1,24 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface IDEAspectBarControl extends IDEDrawable {
15
+	ListenerHandle<InvalidationListener> addInvalidationListener(InvalidationListener listener);
16
+	
17
+	public interface InvalidationListener {
18
+		/* Indicates that this component needs to be redrawn */
19
+		void invalidateView(int x, int y, int width, int height);
20
+		
21
+		/* Indicates that layout should be recalculated for this component (eg because its size changed). */
22
+		void invalidateLayout();
23
+	}
24
+}

+ 71
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEAspectToolbar.java View File

@@ -0,0 +1,71 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import org.openzen.drawablegui.DColorableIcon;
9
+import java.util.ArrayList;
10
+import java.util.Collections;
11
+import java.util.List;
12
+import org.openzen.drawablegui.listeners.ListenerHandle;
13
+import org.openzen.drawablegui.listeners.ListenerList;
14
+
15
+/**
16
+ *
17
+ * @author Hoofdgebruiker
18
+ */
19
+public class IDEAspectToolbar {
20
+	private final ListenerList<Listener> listeners = new ListenerList<>();
21
+	
22
+	public final int order;
23
+	public final DColorableIcon icon;
24
+	public final String title;
25
+	public final String description;
26
+	private boolean active;
27
+	private final List<IDEAspectBarControl> controls = new ArrayList<>();
28
+	
29
+	public IDEAspectToolbar(int order, DColorableIcon icon, String title, String description) {
30
+		this.order = order;
31
+		this.icon = icon;
32
+		this.title = title;
33
+		this.description = description;
34
+		this.active = true;
35
+	}
36
+	
37
+	public ListenerHandle<Listener> addListener(Listener listener) {
38
+		return listeners.add(listener);
39
+	}
40
+	
41
+	public boolean getActive() {
42
+		return active;
43
+	}
44
+	
45
+	public void setActive(boolean active) {
46
+		this.active = active;
47
+		listeners.accept(listener -> listener.onActiveChanged(active));
48
+	}
49
+	
50
+	public List<IDEAspectBarControl> getControls() {
51
+		return Collections.unmodifiableList(controls);
52
+	}
53
+	
54
+	public void addControl(IDEAspectBarControl control) {
55
+		controls.add(control);
56
+		listeners.accept(listener -> listener.onControlAdded(control));
57
+	}
58
+	
59
+	public void removeControl(IDEAspectBarControl control) {
60
+		controls.remove(control);
61
+		listeners.accept(listener -> listener.onControlRemoved(control));
62
+	}
63
+	
64
+	public interface Listener {
65
+		void onActiveChanged(boolean active);
66
+		
67
+		void onControlAdded(IDEAspectBarControl control);
68
+		
69
+		void onControlRemoved(IDEAspectBarControl control);
70
+	}
71
+}

+ 18
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEDockWindow.java View File

@@ -0,0 +1,18 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import org.openzen.drawablegui.live.LiveObject;
9
+import org.openzen.drawablegui.live.SimpleLiveObject;
10
+import org.openzen.zenscript.ide.host.IDESourceFile;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public class IDEDockWindow {
17
+	public final LiveObject<IDESourceFile> currentSourceFile = new SimpleLiveObject<>(null);
18
+}

+ 21
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEDrawable.java View File

@@ -0,0 +1,21 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DTransform2D;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public interface IDEDrawable {
16
+	void draw(DCanvas canvas, DTransform2D transform, IDEStyle style);
17
+	
18
+	float getNominalWidth();
19
+	
20
+	float getNominalHeight();
21
+}

+ 35
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEStatusBar.java View File

@@ -0,0 +1,35 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import org.openzen.drawablegui.listeners.ListenerHandle;
9
+import org.openzen.drawablegui.listeners.ListenerList;
10
+
11
+/**
12
+ *
13
+ * @author Hoofdgebruiker
14
+ */
15
+public class IDEStatusBar {
16
+	private final ListenerList<Listener> listeners = new ListenerList<>();
17
+	private String message;
18
+	
19
+	public ListenerHandle<Listener> addListener(Listener listener) {
20
+		return listeners.add(listener);
21
+	}
22
+	
23
+	public String getMessage() {
24
+		return message;
25
+	}
26
+	
27
+	public void setMessage(String message) {
28
+		this.message = message;
29
+		listeners.accept(listener -> listener.onMessageUpdated(message));
30
+	}
31
+	
32
+	public interface Listener {
33
+		void onMessageUpdated(String message);
34
+	}
35
+}

+ 18
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEStyle.java View File

@@ -0,0 +1,18 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class IDEStyle {
13
+	public final int primaryColor;
14
+	
15
+	public IDEStyle(int primaryColor) {
16
+		this.primaryColor = primaryColor;
17
+	}
18
+}

+ 34
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/IDEWindow.java View File

@@ -0,0 +1,34 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui;
7
+
8
+import org.openzen.zenscript.ide.host.IDESourceFile;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public class IDEWindow {
15
+	public final IDEAspectBar aspectBar;
16
+	public final IDEDockWindow dockWindow;
17
+	public final IDEStatusBar statusBar;
18
+	
19
+	public IDEWindow() {
20
+		aspectBar = new IDEAspectBar();
21
+		dockWindow = new IDEDockWindow();
22
+		statusBar = new IDEStatusBar();
23
+	}
24
+	
25
+	public IDEWindow(IDEAspectBar aspectBar, IDEDockWindow dockWindow, IDEStatusBar statusBar) {
26
+		this.aspectBar = aspectBar;
27
+		this.dockWindow = dockWindow;
28
+		this.statusBar = statusBar;
29
+	}
30
+	
31
+	public void open(IDESourceFile sourceFile) {
32
+		dockWindow.currentSourceFile.setValue(sourceFile);
33
+	}
34
+}

+ 42
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ChevronRight.java View File

@@ -0,0 +1,42 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.drawablegui.DColorableIcon;
12
+
13
+public class ChevronRight implements DColorableIcon {
14
+	public static final ChevronRight INSTANCE = new ChevronRight();
15
+	
16
+	private ChevronRight() {}
17
+	
18
+	private static final DPath PATH_0 = tracer -> {
19
+		tracer.moveTo(10f, 6f);
20
+		tracer.lineTo(8.59f, 7.41f);
21
+		tracer.lineTo(13.17f, 12f);
22
+		tracer.lineTo(8.59f, 16.59f);
23
+		tracer.lineTo(10f, 18f);
24
+		tracer.lineTo(16.0f, 12.0f);
25
+		tracer.close();
26
+	};
27
+	
28
+	@Override
29
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
30
+		canvas.fillPath(PATH_0, transform, color);
31
+	}
32
+
33
+	@Override
34
+	public float getNominalWidth() {
35
+		return 24;
36
+	}
37
+
38
+	@Override
39
+	public float getNominalHeight() {
40
+		return 24;
41
+	}
42
+}

+ 31
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/CodeIcon.java View File

@@ -0,0 +1,31 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DTransform2D;
11
+
12
+public class CodeIcon implements DDrawable {
13
+	public static final CodeIcon INSTANCE = new CodeIcon();
14
+	
15
+	private CodeIcon() {}
16
+	
17
+	@Override
18
+	public void draw(DCanvas canvas, DTransform2D transform) {
19
+		ColorableCodeIcon.INSTANCE.draw(canvas, transform, 0xFF000000);
20
+	}
21
+
22
+	@Override
23
+	public float getNominalWidth() {
24
+		return 24;
25
+	}
26
+
27
+	@Override
28
+	public float getNominalHeight() {
29
+		return 24;
30
+	}
31
+}

+ 51
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableCodeIcon.java View File

@@ -0,0 +1,51 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.drawablegui.DColorableIcon;
12
+
13
+public class ColorableCodeIcon implements DColorableIcon {
14
+	public static final ColorableCodeIcon INSTANCE = new ColorableCodeIcon();
15
+	
16
+	private ColorableCodeIcon() {}
17
+	
18
+	private static final DPath PATH = tracer -> {
19
+		tracer.moveTo(9.4f, 16.6f);
20
+		tracer.lineTo(4.8f, 12f);
21
+		tracer.lineTo(9.4f, 7.4f);
22
+		tracer.lineTo(8f, 6f);
23
+		tracer.lineTo(2.0f, 12.0f);
24
+		tracer.lineTo(8.0f, 18.0f);
25
+		tracer.lineTo(9.4f, 16.6f);
26
+		tracer.close();
27
+		tracer.moveTo(14.599999f, 16.6f);
28
+		tracer.lineTo(19.199999f, 12.0f);
29
+		tracer.lineTo(14.599998f, 7.4f);
30
+		tracer.lineTo(16f, 6f);
31
+		tracer.lineTo(22.0f, 12.0f);
32
+		tracer.lineTo(16.0f, 18.0f);
33
+		tracer.lineTo(14.6f, 16.6f);
34
+		tracer.close();
35
+	};
36
+	
37
+	@Override
38
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
39
+		canvas.fillPath(PATH, transform, color);
40
+	}
41
+
42
+	@Override
43
+	public float getNominalWidth() {
44
+		return 24;
45
+	}
46
+
47
+	@Override
48
+	public float getNominalHeight() {
49
+		return 24;
50
+	}
51
+}

+ 47
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableFolderIcon.java View File

@@ -0,0 +1,47 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.drawablegui.DColorableIcon;
12
+
13
+public class ColorableFolderIcon implements DColorableIcon {
14
+	public static final ColorableFolderIcon INSTANCE = new ColorableFolderIcon();
15
+	
16
+	private ColorableFolderIcon() {}
17
+	
18
+	private static final DPath PATH = tracer -> {
19
+		tracer.moveTo(10f, 4f);
20
+		tracer.lineTo(4.0f, 4.0f);
21
+		tracer.bezierCubic(2.9f, 4.0f, 2.01f, 4.9f, 2.01f, 6.0f);
22
+		tracer.lineTo(2f, 18f);
23
+		tracer.bezierCubic(2.0f, 19.1f, 2.9f, 20.0f, 4.0f, 20.0f);
24
+		tracer.lineTo(20.0f, 20.0f);
25
+		tracer.bezierCubic(21.1f, 20.0f, 22.0f, 19.1f, 22.0f, 18.0f);
26
+		tracer.lineTo(22.0f, 8.0f);
27
+		tracer.bezierCubic(22.0f, 6.9f, 21.1f, 6.0f, 20.0f, 6.0f);
28
+		tracer.lineTo(12.0f, 6.0f);
29
+		tracer.lineTo(10.0f, 4.0f);
30
+		tracer.close();
31
+	};
32
+	
33
+	@Override
34
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
35
+		canvas.fillPath(PATH, transform, color);
36
+	}
37
+
38
+	@Override
39
+	public float getNominalWidth() {
40
+		return 24;
41
+	}
42
+
43
+	@Override
44
+	public float getNominalHeight() {
45
+		return 24;
46
+	}
47
+}

+ 59
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableLibraryIcon.java View File

@@ -0,0 +1,59 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+import org.openzen.drawablegui.DCanvas;
13
+import org.openzen.drawablegui.DPath;
14
+import org.openzen.drawablegui.DTransform2D;
15
+import org.openzen.drawablegui.DColorableIcon;
16
+
17
+public class ColorableLibraryIcon implements DColorableIcon {
18
+	public static final ColorableLibraryIcon INSTANCE = new ColorableLibraryIcon();
19
+	
20
+	private ColorableLibraryIcon() {}
21
+	
22
+	private static final DPath PATH_0 = tracer -> {
23
+		tracer.moveTo(20.54f, 5.23f);
24
+		tracer.lineTo(19.15f, 3.55f);
25
+		tracer.bezierCubic(18.88f, 3.21f, 18.47f, 3.0f, 18.0f, 3.0f);
26
+		tracer.lineTo(6f, 3f);
27
+		tracer.bezierCubic(5.53f, 3.0f, 5.12f, 3.21f, 4.84f, 3.55f);
28
+		tracer.lineTo(3.46f, 5.23f);
29
+		tracer.bezierCubic(3.17f, 5.57f, 3.0f, 6.02f, 3.0f, 6.5f);
30
+		tracer.lineTo(3f, 19f);
31
+		tracer.bezierCubic(3.0f, 20.1f, 3.9f, 21.0f, 5.0f, 21.0f);
32
+		tracer.lineTo(19f, 21f);
33
+		tracer.bezierCubic(20.1f, 21.0f, 21.0f, 20.1f, 21.0f, 19.0f);
34
+		tracer.lineTo(21f, 6.5f);
35
+		tracer.bezierCubic(21.0f, 6.02f, 20.83f, 5.57f, 20.54f, 5.23f);
36
+		tracer.close();
37
+		tracer.moveTo(5.12f, 5f);
38
+		tracer.lineTo(5.93f, 4f);
39
+		tracer.lineTo(17.93f, 4f);
40
+		tracer.lineTo(18.87f, 5f);
41
+		tracer.lineTo(5.12f, 5f);
42
+		tracer.close();
43
+	};
44
+	
45
+	@Override
46
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
47
+		canvas.fillPath(PATH_0, transform, color);
48
+	}
49
+
50
+	@Override
51
+	public float getNominalWidth() {
52
+		return 24;
53
+	}
54
+
55
+	@Override
56
+	public float getNominalHeight() {
57
+		return 24;
58
+	}
59
+}

+ 53
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableModuleIcon.java View File

@@ -0,0 +1,53 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.drawablegui.DColorableIcon;
12
+
13
+public class ColorableModuleIcon implements DColorableIcon {
14
+	public static final ColorableModuleIcon INSTANCE = new ColorableModuleIcon();
15
+	
16
+	private ColorableModuleIcon() {}
17
+	
18
+	private static final DPath PATH = tracer -> {
19
+		tracer.moveTo(3f, 19f);
20
+		tracer.lineTo(9.0f, 19.0f);
21
+		tracer.lineTo(9.0f, 12.0f);
22
+		tracer.lineTo(3.0f, 12.0f);
23
+		tracer.lineTo(3.0f, 19.0f);
24
+		tracer.close();
25
+		tracer.moveTo(10.0f, 19.0f);
26
+		tracer.lineTo(22.0f, 19.0f);
27
+		tracer.lineTo(22.0f, 12.0f);
28
+		tracer.lineTo(10.0f, 12.0f);
29
+		tracer.lineTo(10.0f, 19.0f);
30
+		tracer.close();
31
+		tracer.moveTo(3f, 5f);
32
+		tracer.lineTo(3.0f, 11.0f);
33
+		tracer.lineTo(22.0f, 11.0f);
34
+		tracer.lineTo(22.0f, 5.0f);
35
+		tracer.lineTo(3.0f, 5.0f);
36
+		tracer.close();
37
+	};
38
+	
39
+	@Override
40
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
41
+		canvas.fillPath(PATH, transform, color);
42
+	}
43
+
44
+	@Override
45
+	public float getNominalWidth() {
46
+		return 24;
47
+	}
48
+
49
+	@Override
50
+	public float getNominalHeight() {
51
+		return 24;
52
+	}
53
+}

+ 60
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ColorableProjectIcon.java View File

@@ -0,0 +1,60 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DPath;
10
+import org.openzen.drawablegui.DTransform2D;
11
+import org.openzen.zenscript.ide.ui.IDEStyle;
12
+import org.openzen.drawablegui.DColorableIcon;
13
+
14
+public class ColorableProjectIcon implements DColorableIcon {
15
+	public static final ColorableProjectIcon INSTANCE = new ColorableProjectIcon();
16
+	
17
+	private ColorableProjectIcon() {}
18
+	
19
+	private static final DPath PATH_0 = tracer -> {
20
+		tracer.moveTo(3f, 13f);
21
+		tracer.lineTo(11.0f, 13.0f);
22
+		tracer.lineTo(11.0f, 3.0f);
23
+		tracer.lineTo(3.0f, 3.0f);
24
+		tracer.lineTo(3.0f, 13.0f);
25
+		tracer.close();
26
+		tracer.moveTo(3.0f, 21.0f);
27
+		tracer.lineTo(11.0f, 21.0f);
28
+		tracer.lineTo(11.0f, 15.0f);
29
+		tracer.lineTo(3.0f, 15.0f);
30
+		tracer.lineTo(3.0f, 21.0f);
31
+		tracer.close();
32
+		tracer.moveTo(13.0f, 21.0f);
33
+		tracer.lineTo(21.0f, 21.0f);
34
+		tracer.lineTo(21.0f, 11.0f);
35
+		tracer.lineTo(13.0f, 11.0f);
36
+		tracer.lineTo(13.0f, 21.0f);
37
+		tracer.close();
38
+		tracer.moveTo(13.0f, 3.0f);
39
+		tracer.lineTo(13.0f, 9.0f);
40
+		tracer.lineTo(21.0f, 9.0f);
41
+		tracer.lineTo(21.0f, 3.0f);
42
+		tracer.lineTo(13.0f, 3.0f);
43
+		tracer.close();
44
+	};
45
+	
46
+	@Override
47
+	public void draw(DCanvas canvas, DTransform2D transform, int color) {
48
+		canvas.fillPath(PATH_0, transform, color);
49
+	}
50
+
51
+	@Override
52
+	public float getNominalWidth() {
53
+		return 24;
54
+	}
55
+
56
+	@Override
57
+	public float getNominalHeight() {
58
+		return 24;
59
+	}
60
+}

+ 31
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/FolderIcon.java View File

@@ -0,0 +1,31 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DTransform2D;
11
+
12
+public class FolderIcon implements DDrawable {
13
+	public static final FolderIcon INSTANCE = new FolderIcon();
14
+	
15
+	private FolderIcon() {}
16
+	
17
+	@Override
18
+	public void draw(DCanvas canvas, DTransform2D transform) {
19
+		ColorableFolderIcon.INSTANCE.draw(canvas, transform, 0xFF000000);
20
+	}
21
+
22
+	@Override
23
+	public float getNominalWidth() {
24
+		return 24;
25
+	}
26
+
27
+	@Override
28
+	public float getNominalHeight() {
29
+		return 24;
30
+	}
31
+}

+ 32
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/LibraryIcon.java View File

@@ -0,0 +1,32 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DPath;
11
+import org.openzen.drawablegui.DTransform2D;
12
+
13
+public class LibraryIcon implements DDrawable {
14
+	public static final LibraryIcon INSTANCE = new LibraryIcon();
15
+	
16
+	private LibraryIcon() {}
17
+	
18
+	@Override
19
+	public void draw(DCanvas canvas, DTransform2D transform) {
20
+		ColorableLibraryIcon.INSTANCE.draw(canvas, transform, 0xFF888888);
21
+	}
22
+
23
+	@Override
24
+	public float getNominalWidth() {
25
+		return 24;
26
+	}
27
+
28
+	@Override
29
+	public float getNominalHeight() {
30
+		return 24;
31
+	}
32
+}

+ 32
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ModuleIcon.java View File

@@ -0,0 +1,32 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DPath;
11
+import org.openzen.drawablegui.DTransform2D;
12
+
13
+public class ModuleIcon implements DDrawable {
14
+	public static final ModuleIcon INSTANCE = new ModuleIcon();
15
+	
16
+	private ModuleIcon() {}
17
+	
18
+	@Override
19
+	public void draw(DCanvas canvas, DTransform2D transform) {
20
+		ColorableModuleIcon.INSTANCE.draw(canvas, transform, 0xFF000000);
21
+	}
22
+
23
+	@Override
24
+	public float getNominalWidth() {
25
+		return 24;
26
+	}
27
+
28
+	@Override
29
+	public float getNominalHeight() {
30
+		return 24;
31
+	}
32
+}

+ 32
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/icons/ProjectIcon.java View File

@@ -0,0 +1,32 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.icons;
7
+
8
+import org.openzen.drawablegui.DCanvas;
9
+import org.openzen.drawablegui.DDrawable;
10
+import org.openzen.drawablegui.DPath;
11
+import org.openzen.drawablegui.DTransform2D;
12
+
13
+public class ProjectIcon implements DDrawable {
14
+	public static final ProjectIcon INSTANCE = new ProjectIcon();
15
+	
16
+	private ProjectIcon() {}
17
+	
18
+	@Override
19
+	public void draw(DCanvas canvas, DTransform2D transform) {
20
+		ColorableProjectIcon.INSTANCE.draw(canvas, transform, 0xFF000000);
21
+	}
22
+
23
+	@Override
24
+	public float getNominalWidth() {
25
+		return 24;
26
+	}
27
+
28
+	@Override
29
+	public float getNominalHeight() {
30
+		return 24;
31
+	}
32
+}

+ 52
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/StatusBarView.java View File

@@ -0,0 +1,52 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view;
7
+
8
+import org.openzen.drawablegui.DRectangle;
9
+import org.openzen.drawablegui.DCanvas;
10
+import org.openzen.drawablegui.DComponent;
11
+import org.openzen.drawablegui.DDimensionPreferences;
12
+import org.openzen.drawablegui.DDrawingContext;
13
+import org.openzen.drawablegui.live.LiveObject;
14
+import org.openzen.drawablegui.live.SimpleLiveObject;
15
+
16
+/**
17
+ *
18
+ * @author Hoofdgebruiker
19
+ */
20
+public class StatusBarView implements DComponent {
21
+	private static final int COLOR = 0xFFF0F0F0;
22
+	//private static final int COLOR = 0xFFFFFFFF;
23
+	
24
+	private final SimpleLiveObject<DDimensionPreferences> dimensionPreferences = new SimpleLiveObject<>(new DDimensionPreferences(0, 40));
25
+	private DRectangle bounds;
26
+	private DDrawingContext context;
27
+
28
+	@Override
29
+	public void setContext(DDrawingContext context) {
30
+		this.context = context;
31
+	}
32
+	
33
+	@Override
34
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
35
+		return dimensionPreferences;
36
+	}
37
+	
38
+	@Override
39
+	public DRectangle getBounds() {
40
+		return bounds;
41
+	}
42
+
43
+	@Override
44
+	public void setBounds(DRectangle bounds) {
45
+		this.bounds = bounds;
46
+	}
47
+
48
+	@Override
49
+	public void paint(DCanvas canvas) {
50
+		canvas.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height, COLOR);
51
+	}
52
+}

+ 41
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/WindowView.java View File

@@ -0,0 +1,41 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view;
7
+
8
+import org.openzen.drawablegui.DDimensionPreferences;
9
+import org.openzen.drawablegui.DEmptyView;
10
+import org.openzen.drawablegui.scroll.DScrollPane;
11
+import org.openzen.drawablegui.scroll.DScrollPaneStyle;
12
+import org.openzen.drawablegui.DSideLayout;
13
+import org.openzen.drawablegui.tree.DTreeView;
14
+import org.openzen.drawablegui.tree.DTreeViewStyle;
15
+import org.openzen.zenscript.ide.host.DevelopmentHost;
16
+import org.openzen.zenscript.ide.ui.IDEWindow;
17
+import org.openzen.zenscript.ide.ui.view.editor.SourceEditor;
18
+import org.openzen.zenscript.ide.ui.view.project.RootTreeNode;
19
+
20
+/**
21
+ *
22
+ * @author Hoofdgebruiker
23
+ */
24
+public final class WindowView extends DSideLayout {
25
+	public WindowView(IDEWindow window, DevelopmentHost host) {
26
+		super(DEmptyView.INSTANCE);
27
+		
28
+		DTreeView projectView = new DTreeView(DTreeViewStyle.DEFAULT, new RootTreeNode(window, host), false);
29
+		projectView.getDimensionPreferences().setValue(new DDimensionPreferences(500, 500));
30
+		add(Side.LEFT, new DScrollPane(DScrollPaneStyle.DEFAULT, projectView));
31
+		add(Side.BOTTOM, new StatusBarView());
32
+		
33
+		window.dockWindow.currentSourceFile.addListener((oldSource, newSource) -> {
34
+			if (newSource == null) {
35
+				setMain(DEmptyView.INSTANCE);
36
+			} else {
37
+				setMain(new DScrollPane(DScrollPaneStyle.DEFAULT, new SourceEditor(newSource)));
38
+			}
39
+		});
40
+	}
41
+}

+ 53
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/aspectbar/AspectBarView.java View File

@@ -0,0 +1,53 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.aspectbar;
7
+
8
+import org.openzen.zenscript.ide.ui.view.*;
9
+import org.openzen.drawablegui.DRectangle;
10
+import org.openzen.drawablegui.DCanvas;
11
+import org.openzen.drawablegui.DComponent;
12
+import org.openzen.drawablegui.DDimensionPreferences;
13
+import org.openzen.drawablegui.DDrawingContext;
14
+import org.openzen.drawablegui.live.LiveObject;
15
+import org.openzen.drawablegui.live.SimpleLiveObject;
16
+
17
+/**
18
+ *
19
+ * @author Hoofdgebruiker
20
+ */
21
+public class AspectBarView implements DComponent {
22
+	private static final int COLOR = 0xFFF0F0F0;
23
+	//private static final int COLOR = 0xFFFFFFFF;
24
+	
25
+	private final SimpleLiveObject<DDimensionPreferences> dimensionPreferences = new SimpleLiveObject<>(new DDimensionPreferences(0, 40));
26
+	private DRectangle bounds;
27
+	private DDrawingContext context;
28
+
29
+	@Override
30
+	public void setContext(DDrawingContext context) {
31
+		this.context = context;
32
+	}
33
+	
34
+	@Override
35
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
36
+		return dimensionPreferences;
37
+	}
38
+	
39
+	@Override
40
+	public DRectangle getBounds() {
41
+		return bounds;
42
+	}
43
+
44
+	@Override
45
+	public void setBounds(DRectangle bounds) {
46
+		this.bounds = bounds;
47
+	}
48
+
49
+	@Override
50
+	public void paint(DCanvas canvas) {
51
+		canvas.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height, COLOR);
52
+	}
53
+}

+ 858
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditor.java View File

@@ -0,0 +1,858 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.editor;
7
+
8
+import java.io.IOException;
9
+import java.util.ArrayList;
10
+import java.util.Iterator;
11
+import java.util.List;
12
+import java.util.Timer;
13
+import java.util.TimerTask;
14
+import org.openzen.drawablegui.DCanvas;
15
+import org.openzen.drawablegui.DComponent;
16
+import org.openzen.drawablegui.DDimensionPreferences;
17
+import org.openzen.drawablegui.DDrawingContext;
18
+import org.openzen.drawablegui.DFont;
19
+import org.openzen.drawablegui.DFontFamily;
20
+import org.openzen.drawablegui.DFontMetrics;
21
+import org.openzen.drawablegui.DIRectangle;
22
+import org.openzen.drawablegui.DKeyEvent;
23
+import org.openzen.drawablegui.DKeyEvent.KeyCode;
24
+import org.openzen.drawablegui.DMouseEvent;
25
+import org.openzen.drawablegui.DRectangle;
26
+import org.openzen.drawablegui.DTransform2D;
27
+import org.openzen.drawablegui.live.LiveObject;
28
+import org.openzen.drawablegui.live.SimpleLiveObject;
29
+import org.openzen.zenscript.ide.host.IDESourceFile;
30
+import org.openzen.zenscript.lexer.ReaderCharReader;
31
+import org.openzen.zenscript.lexer.TokenParser;
32
+import org.openzen.zenscript.lexer.ZSToken;
33
+import org.openzen.zenscript.lexer.ZSTokenParser;
34
+import org.openzen.zenscript.lexer.ZSTokenType;
35
+
36
+/**
37
+ *
38
+ * @author Hoofdgebruiker
39
+ */
40
+public class SourceEditor implements DComponent {
41
+	private final SourceEditorStyle style = SourceEditorStyle.DEFAULT;
42
+	private final DFont font = new DFont(DFontFamily.CODE, false, false, false, 24);
43
+	private final LiveObject<DDimensionPreferences> dimensionPreferences = new SimpleLiveObject<>(new DDimensionPreferences(0, 0));
44
+	private final String tab = "    ";
45
+	private final IDESourceFile sourceFile;
46
+	
47
+	private DRectangle bounds;
48
+	private DDrawingContext context;
49
+	private DFontMetrics fontMetrics;
50
+	private int textLineHeight;
51
+	private int fullLineHeight;
52
+	private int selectionLineHeight;
53
+	private final List<Line> lines = new ArrayList<>();
54
+	
55
+	private int lineBarWidth;
56
+	private CursorPosition cursorStart = null;
57
+	private CursorPosition cursorEnd = null;
58
+	
59
+	private Timer blink = new Timer();
60
+	private boolean cursorBlink = true;
61
+	
62
+	private int mouseDownX = -1;
63
+	private int mouseDownY = -1;
64
+	private boolean dragging = false;
65
+	
66
+	public SourceEditor(IDESourceFile sourceFile) {
67
+		this.sourceFile = sourceFile;
68
+		
69
+		blink.scheduleAtFixedRate(new TimerTask() {
70
+			@Override
71
+			public void run() {
72
+				blink();
73
+			}
74
+		}, 300, 300);
75
+		
76
+		try {
77
+			parse();
78
+		} catch (IOException ex) {
79
+			ex.printStackTrace();
80
+		}
81
+	}
82
+
83
+	@Override
84
+	public void setContext(DDrawingContext context) {
85
+		this.context = context;
86
+		fontMetrics = context.getFontMetrics(font);
87
+		textLineHeight = fontMetrics.getAscent() + fontMetrics.getDescent();
88
+		fullLineHeight = textLineHeight + fontMetrics.getLeading() + style.extraLineSpacing;
89
+		selectionLineHeight = textLineHeight + style.selectionPaddingTop + style.selectionPaddingBottom;
90
+		
91
+		dimensionPreferences.setValue(new DDimensionPreferences(0, fullLineHeight * lines.size()));
92
+	}
93
+
94
+	@Override
95
+	public LiveObject<DDimensionPreferences> getDimensionPreferences() {
96
+		return dimensionPreferences;
97
+	}
98
+
99
+	@Override
100
+	public DRectangle getBounds() {
101
+		return bounds;
102
+	}
103
+
104
+	@Override
105
+	public void setBounds(DRectangle bounds) {
106
+		this.bounds = bounds;
107
+	}
108
+
109
+	@Override
110
+	public void paint(DCanvas canvas) {
111
+		DIRectangle canvasBounds = canvas.getBounds();
112
+		canvas.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height, 0xFFFFFFFF);
113
+		
114
+		lineBarWidth = Math.max(50, fontMetrics.getWidth(Integer.toString(lines.size()))) + 5;
115
+		canvas.fillRectangle(bounds.x, bounds.y, lineBarWidth, bounds.height, 0xFFE9E8E2);
116
+		canvas.strokePath(tracer -> {
117
+			tracer.moveTo(bounds.x + lineBarWidth, bounds.y);
118
+			tracer.lineTo(bounds.x + lineBarWidth, bounds.y + bounds.height);
119
+		}, DTransform2D.IDENTITY, 0xFFA0A0A0, 1);
120
+		
121
+		int x = bounds.x + lineBarWidth + 10;
122
+		if (cursorEnd != null)
123
+			canvas.fillRectangle(x, lineToY(cursorEnd.line), bounds.width - x, selectionLineHeight, style.currentLineHighlight);
124
+		
125
+		if (cursorStart != null && !cursorStart.equals(cursorEnd)) {
126
+			if (cursorStart.line == cursorEnd.line) {
127
+				int y = cursorStart.getY();
128
+				int x1 = cursorStart.getX();
129
+				int x2 = cursorEnd.getX();
130
+				int fromX = Math.min(x1, x2);
131
+				int toX = Math.max(x1, x2);
132
+				canvas.fillRectangle(fromX, y, toX - fromX, selectionLineHeight, style.selectionColor);
133
+			} else {
134
+				CursorPosition from = cursorStart.line < cursorEnd.line ? cursorStart : cursorEnd;
135
+				CursorPosition to = cursorStart.line < cursorEnd.line ? cursorEnd : cursorStart;
136
+				
137
+				int fromX = from.getX();
138
+				canvas.fillRectangle(fromX, from.getY(), bounds.width - fromX, selectionLineHeight, style.selectionColor);
139
+				
140
+				for (int i = from.line + 1; i < to.line; i++) {
141
+					canvas.fillRectangle(x, lineToY(i), bounds.width - x, selectionLineHeight, style.selectionColor);
142
+				}
143
+				
144
+				int toX = to.getX();
145
+				canvas.fillRectangle(x, to.getY(), toX - x, selectionLineHeight, style.selectionColor);
146
+			}
147
+		}
148
+		
149
+		int y = bounds.y + style.selectionPaddingTop;
150
+		int lineIndex = 1;
151
+		for (Line line : lines) {
152
+			if (y + textLineHeight  >= canvasBounds.y && y < canvasBounds.y + canvasBounds.height) {
153
+				String lineNumber = Integer.toString(lineIndex);
154
+				int lineNumberX = x - 15 - (int)canvas.measureTextLength(font, lineNumber);
155
+				canvas.drawText(font, 0xFFA0A0A0, lineNumberX, y + fontMetrics.getAscent(), lineNumber);
156
+
157
+				int lineX = x;
158
+				for (ZSToken token : line.tokens) {
159
+					String content = getDisplayContent(token);
160
+					canvas.drawText(font, TokenClass.get(token.type).color, lineX, y + fontMetrics.getAscent(), content);
161
+					lineX += canvas.measureTextLength(font, content);
162
+				}
163
+			}
164
+			
165
+			y += fullLineHeight;
166
+			lineIndex++;
167
+		}
168
+		
169
+		if (cursorEnd != null && cursorBlink) {
170
+			int cursorX = cursorEnd.getX();
171
+			int cursorY = cursorEnd.getY();
172
+			canvas.fillRectangle(cursorX, cursorY, 2, selectionLineHeight, 0xFF000000);
173
+		}
174
+	}
175
+	
176
+	@Override
177
+	public void onMouseEnter(DMouseEvent e) {
178
+		context.setCursor(DDrawingContext.Cursor.TEXT);
179
+	}
180
+	
181
+	@Override
182
+	public void onMouseExit(DMouseEvent e) {
183
+		context.setCursor(DDrawingContext.Cursor.NORMAL);
184
+	}
185
+	
186
+	@Override
187
+	public void onMouseClick(DMouseEvent e) {
188
+		context.focus(this);
189
+		
190
+		CursorPosition position = getPositionAt(e.x, e.y);
191
+		if (e.isDoubleClick()) {
192
+			// select entire word
193
+			TokenPosition token = getTokenAt(position);
194
+			setCursor(
195
+					new CursorPosition(position.line, position.offset - token.offset),
196
+					new CursorPosition(position.line, position.offset + token.token.content.length() - token.offset));
197
+		} else if (e.isTripleClick()) {
198
+			setCursor(
199
+					new CursorPosition(position.line, 0),
200
+					new CursorPosition(position.line + 1, 0));
201
+		} else {
202
+			setCursor(position, position);
203
+		}
204
+	}
205
+	
206
+	private void setCursor(CursorPosition start, CursorPosition end) {
207
+		if (cursorStart != null)
208
+			repaint(cursorStart, cursorEnd);
209
+		
210
+		int previousLine = cursorEnd == null ? -1 : cursorEnd.line;
211
+		
212
+		cursorStart = start;
213
+		cursorEnd = end;
214
+		
215
+		if (previousLine != cursorEnd.line) {
216
+			if (previousLine >= 0)
217
+				repaintLine(previousLine);
218
+			repaintLine(cursorEnd.line);
219
+		}
220
+		repaint(cursorStart, cursorEnd);
221
+		scrollTo(cursorEnd);
222
+	}
223
+	
224
+	@Override
225
+	public void onMouseDown(DMouseEvent e) {
226
+		mouseDownX = e.x;
227
+		mouseDownY = e.y;
228
+	}
229
+	
230
+	@Override
231
+	public void onMouseDrag(DMouseEvent e) {
232
+		CursorPosition start = cursorStart;
233
+		if (!dragging)
234
+			start = getPositionAt(mouseDownX, mouseDownY);
235
+		
236
+		setCursor(start, getPositionAt(e.x, e.y));
237
+	}
238
+	
239
+	@Override
240
+	public void onKeyPressed(DKeyEvent e) {
241
+		boolean shift = e.has(DKeyEvent.SHIFT);
242
+		switch (e.keyCode) {
243
+			case UP:
244
+				if (cursorEnd == null || cursorEnd.line == 0)
245
+					return;
246
+				
247
+				{
248
+					int line = cursorEnd.line - 1;
249
+					CursorPosition position = new CursorPosition(
250
+							line,
251
+							Math.min(lines.get(line).length, cursorEnd.offset));
252
+					setCursor(shift ? cursorStart : position, position);
253
+				}
254
+				break;
255
+			case DOWN:
256
+				if (cursorEnd == null || cursorEnd.line >= lines.size() - 1)
257
+					return;
258
+				
259
+				{
260
+					int line = cursorEnd.line + 1;
261
+					CursorPosition position = new CursorPosition(
262
+							line,
263
+							Math.min(lines.get(line).length, cursorEnd.offset));
264
+					setCursor(shift ? cursorStart : position, position);
265
+				}
266
+				break;
267
+			case LEFT:
268
+				if (cursorEnd == null || (cursorEnd.line == 0 && cursorEnd.offset == 0))
269
+					return;
270
+				
271
+				{
272
+					CursorPosition position;
273
+					if (cursorEnd.offset == 0) {
274
+						int line = cursorEnd.line - 1;
275
+						position = new CursorPosition(line, lines.get(line).length);
276
+					} else {
277
+						position = new CursorPosition(cursorEnd.line, cursorEnd.offset - 1);
278
+					}
279
+					setCursor(shift ? cursorStart : position, position);
280
+				}
281
+				break;
282
+			case RIGHT:
283
+				if (cursorEnd == null || (cursorEnd.offset == lines.get(cursorEnd.line).length && cursorEnd.line >= lines.size() - 1))
284
+					return;
285
+				
286
+				{
287
+					CursorPosition position;
288
+					if (cursorEnd.offset == lines.get(cursorEnd.line).length) {
289
+						position = new CursorPosition(cursorEnd.line + 1, 0);
290
+					} else {
291
+						position = new CursorPosition(cursorEnd.line, cursorEnd.offset + 1);
292
+					}
293
+					setCursor(shift ? cursorStart : position, position);
294
+				}
295
+				break;
296
+			case DELETE:
297
+				delete();
298
+				break;
299
+			case BACKSPACE:
300
+				backspace();
301
+				break;
302
+			case ENTER:
303
+				newline();
304
+				break;
305
+			case TAB:
306
+				type(tab);
307
+				break;
308
+			default:
309
+				if (e.character == DKeyEvent.CHAR_UNDEFINED)
310
+					return;
311
+				if (e.has(DKeyEvent.CTRL) || e.has(DKeyEvent.ALT)) {
312
+					handleShortcut(e);
313
+				} else {
314
+					type(Character.toString(e.character));
315
+				}
316
+				break;
317
+		}
318
+	}
319
+	
320
+	private void handleShortcut(DKeyEvent e) {
321
+		if (e.has(DKeyEvent.CTRL)) {
322
+			if (e.keyCode == KeyCode.S) {
323
+				save();
324
+			}
325
+		}
326
+	}
327
+	
328
+	private void save() {
329
+		String content = contentToString();
330
+		sourceFile.update(content);
331
+	}
332
+	
333
+	private String contentToString() {
334
+		StringBuilder result = new StringBuilder();
335
+		for (int i = 0; i < lines.size(); i++) {
336
+			if (i > 0)
337
+				result.append("\n");
338
+			
339
+			Line line = lines.get(i);
340
+			for (ZSToken token : line.tokens) {
341
+				result.append(token.content);
342
+			}
343
+		}
344
+		return result.toString();
345
+	}
346
+	
347
+	private void delete() {
348
+		Line line = lines.get(cursorEnd.line);
349
+		if (cursorEnd.offset == line.length) {
350
+			// merge 2 lines
351
+			if (cursorEnd.line == lines.size() - 1)
352
+				return;
353
+			
354
+			line.merge(cursorEnd.line, lines.get(cursorEnd.line + 1));
355
+			lines.remove(cursorEnd.line + 1);
356
+			onLinesUpdated();
357
+			return;
358
+		}
359
+		
360
+		line.delete(cursorEnd.line, cursorEnd.offset);
361
+		repaintLine(cursorEnd.line);
362
+	}
363
+	
364
+	private void backspace() {
365
+		if (cursorEnd.offset == 0) {
366
+			if (cursorEnd.line == 0)
367
+				return;
368
+			
369
+			int length = lines.get(cursorEnd.line - 1).length;
370
+			lines.get(cursorEnd.line - 1).merge(cursorEnd.line - 1, lines.get(cursorEnd.line));
371
+			lines.remove(cursorEnd.line);
372
+			onLinesUpdated();
373
+			
374
+			CursorPosition position = new CursorPosition(cursorEnd.line - 1, length);
375
+			setCursor(position, position);
376
+			return;
377
+		}
378
+		
379
+		lines.get(cursorEnd.line).delete(cursorEnd.line, cursorEnd.offset - 1);
380
+		CursorPosition position = new CursorPosition(cursorEnd.line, cursorEnd.offset - 1);
381
+		setCursor(position, position);
382
+		repaintLine(cursorEnd.line);
383
+	}
384
+	
385
+	private void type(String value) {
386
+		lines.get(cursorEnd.line).insert(cursorEnd.line, cursorEnd.offset, value);
387
+		CursorPosition position = new CursorPosition(cursorEnd.line, cursorEnd.offset + value.length());
388
+		setCursor(position, position);
389
+		repaintLine(cursorEnd.line);
390
+	}
391
+	
392
+	private void newline() {
393
+		lines.get(cursorEnd.line).split(cursorEnd.line, cursorEnd.offset);
394
+		onLinesUpdated();
395
+		
396
+		CursorPosition position = new CursorPosition(cursorEnd.line + 1, 0);
397
+		setCursor(position, position);
398
+	}
399
+	
400
+	private TokenPosition getTokenAt(CursorPosition position) {
401
+		Line line = lines.get(position.line);
402
+		int offset = 0;
403
+		for (ZSToken token : line.tokens) {
404
+			if (offset + token.content.length() > position.offset)
405
+				return new TokenPosition(token, position.offset - offset);
406
+			offset += token.content.length();
407
+		}
408
+		
409
+		return new TokenPosition(line.tokens.get(line.tokens.size() - 1), position.offset - offset);
410
+	}
411
+	
412
+	private class TokenPosition {
413
+		public final ZSToken token;
414
+		public final int offset;
415
+		
416
+		public TokenPosition(ZSToken token, int offset) {
417
+			this.token = token;
418
+			this.offset = offset;
419
+		}
420
+	}
421
+	
422
+	private void repaint(CursorPosition from, CursorPosition to) {
423
+		if (from.line == to.line) {
424
+			int y = lineToY(from.line);
425
+			int fromX = offsetToX(from.line, Math.min(from.offset, to.offset));
426
+			int toX = offsetToX(from.line, Math.max(from.offset, to.offset)) + 2;
427
+			context.repaint(fromX, y, toX - fromX, selectionLineHeight);
428
+		} else {
429
+			int fromY = lineToY(Math.min(from.line, to.line));
430
+			int toY = lineToY(Math.max(from.line, to.line) + 1);
431
+			context.repaint(bounds.x, fromY, bounds.width, toY - fromY);
432
+		}
433
+	}
434
+	
435
+	private void repaintLine(int line) {
436
+		context.repaint(bounds.x, lineToY(line), bounds.width, selectionLineHeight);
437
+	}
438
+	
439
+	private void scrollTo(CursorPosition position) {
440
+		int y = lineToY(position.line);
441
+		int x = offsetToX(position.line, position.offset);
442
+		context.scrollInView(x, y, 2, selectionLineHeight);
443
+	}
444
+	
445
+	private void blink() {
446
+		if (cursorEnd != null) {
447
+			cursorBlink = !cursorBlink;
448
+			repaint(cursorEnd, cursorEnd);
449
+		}
450
+	}
451
+	
452
+	private CursorPosition getPositionAt(int x, int y) {
453
+		int line = yToLine(y);
454
+		int offset = xToOffset(line, x);
455
+		
456
+		if (line < 0)
457
+			line = 0;
458
+		if (line >= lines.size())
459
+			line = lines.size() - 1;
460
+		
461
+		return new CursorPosition(line, offset);
462
+	}
463
+	
464
+	private int xToOffset(int lineIndex, int x) {
465
+		if (lineIndex < 0 || lineIndex >= lines.size())
466
+			return 0;
467
+		
468
+		Line line = lines.get(lineIndex);
469
+		int lineX = bounds.x + lineBarWidth + 10;
470
+		int offset = 0;
471
+		for (ZSToken token : line.tokens) {
472
+			String content = getDisplayContent(token);
473
+			int tokenWidth = fontMetrics.getWidth(content);
474
+			if (lineX + tokenWidth > x) {
475
+				return offset + getStringIndexForToken(token, tokenWidth, x - lineX);
476
+			}
477
+			lineX += tokenWidth;
478
+			offset += token.content.length();
479
+		}
480
+		
481
+		return offset;
482
+	}
483
+	
484
+	private int getStringIndexForToken(ZSToken token, int strWidth, int pixels) {
485
+		if (token.type == ZSTokenType.T_WHITESPACE_TAB)
486
+			return pixels > strWidth / 2 ? 1 : 0;
487
+		
488
+		return getStringIndexForPixels(token.content, pixels);
489
+	}
490
+	
491
+	private int getStringIndexForPixels(String str, int pixels) {
492
+		int previousX = 0;
493
+		for (int i = 1; i < str.length(); i++) {
494
+			int currentX = fontMetrics.getWidth(str, 0, i);
495
+			if (currentX >= pixels)
496
+				return (pixels - previousX < currentX - pixels) ? i - 1 : i;
497
+		}
498
+		
499
+		return str.length();
500
+	}
501
+	
502
+	private int yToLine(int y) {
503
+		int startY = bounds.y;
504
+		return (y - startY) / fullLineHeight;
505
+	}
506
+	
507
+	private int lineToY(int line) {
508
+		int startY = bounds.y;
509
+		return startY + line * fullLineHeight;
510
+	}
511
+	
512
+	private int offsetToX(int line, int offset) {
513
+		if (line >= lines.size())
514
+			return 0;
515
+		
516
+		int tokensOffset = 0;
517
+		int x = bounds.x + lineBarWidth + 10;
518
+		Line lineData = lines.get(line);
519
+		for (ZSToken token : lineData.tokens) {
520
+			String content = getDisplayContent(token);
521
+			if (tokensOffset + token.content.length() >= offset) {
522
+				if (token.type == ZSTokenType.T_WHITESPACE_TAB)
523
+					return offset == tokensOffset ? x : x + fontMetrics.getWidth(tab);
524
+				
525
+				return x + fontMetrics.getWidth(token.content, 0, offset - tokensOffset);
526
+			}
527
+			
528
+			x += fontMetrics.getWidth(content);
529
+			tokensOffset += token.content.length();
530
+		}
531
+		return x;
532
+	}
533
+	
534
+	private String getDisplayContent(ZSToken token) {
535
+		return token.type == ZSTokenType.T_WHITESPACE_TAB ? tab : token.content;
536
+	}
537
+	
538
+	private void parse() throws IOException {
539
+		lines.clear();
540
+		lines.add(new Line());
541
+		
542
+		TokenParser<ZSToken, ZSTokenType> parser = ZSTokenParser.createRaw(sourceFile.getName(), new ReaderCharReader(sourceFile.read()), tab.length());
543
+		insertTokens(0, 0, parser);
544
+	}
545
+	
546
+	private void onLinesUpdated() {
547
+		dimensionPreferences.setValue(new DDimensionPreferences(0, fullLineHeight * lines.size()));
548
+		
549
+		if (bounds != null)
550
+			context.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
551
+	}
552
+	
553
+	public class Line {
554
+		public final List<ZSToken> tokens = new ArrayList<>();
555
+		private int length;
556
+		
557
+		private void calculateLength() {
558
+			length = 0;
559
+			for (ZSToken token : tokens)
560
+				length += token.content.length();
561
+		}
562
+		
563
+		public void merge(int lineIndex, Line other) {
564
+			if (tokens.isEmpty()) {
565
+				tokens.addAll(other.tokens);
566
+				length = other.length;
567
+				return;
568
+			}
569
+			
570
+			int fromToken = tokens.size() - 1;
571
+			int toToken = other.tokens.size() > 0 ? fromToken + 2 : fromToken + 1;
572
+			tokens.addAll(other.tokens);
573
+			length += other.length;
574
+			reparse(lineIndex, fromToken, lineIndex, toToken);
575
+		}
576
+		
577
+		public Line split(int lineIndex, int offset) {
578
+			if (offset < 0)
579
+				throw new IllegalArgumentException("offset must be >= 0");
580
+			
581
+			int tokenOffset = 0;
582
+			for (int i = 0; i < tokens.size(); i++) {
583
+				ZSToken token = tokens.get(i);
584
+				if (tokenOffset + token.content.length() > offset) {
585
+					ZSToken broken = token;
586
+					Line newLine = new Line();
587
+					if (offset - tokenOffset != token.content.length() && offset - tokenOffset != 0) {
588
+						ZSToken.Pair split = token.split(offset - tokenOffset);
589
+						broken = split.first;
590
+						newLine.tokens.add(split.second);
591
+					}
592
+					
593
+					tokens.set(i, broken);
594
+					for (int j = i + 1; j < tokens.size(); j++)
595
+						newLine.tokens.add(tokens.get(j));
596
+					for (int j = tokens.size() - 1; j > i; j--)
597
+						tokens.remove(j);
598
+					
599
+					lines.add(lineIndex + 1, newLine);
600
+					reparse(lineIndex, i, lineIndex + 1, 1);
601
+					return newLine;
602
+				}
603
+				tokenOffset += token.content.length();
604
+			}
605
+			
606
+			Line result = new Line();
607
+			lines.add(result);
608
+			return result;
609
+		}
610
+		
611
+		public void delete(int lineIndex, int offset) {
612
+			int tokenOffset = 0;
613
+			for (int i = 0; i < tokens.size(); i++) {
614
+				ZSToken token = tokens.get(i);
615
+				if (tokenOffset + token.content.length() > offset) {
616
+					if (token.content.length() == 1) {
617
+						tokens.remove(i);
618
+					} else {
619
+						token = token.delete(offset - tokenOffset, 1);
620
+						tokens.set(i, token);
621
+					}
622
+					length--;
623
+					
624
+					reparse(lineIndex, i, lineIndex, i + 1);
625
+					return;
626
+				}
627
+				tokenOffset += token.content.length();
628
+			}
629
+		}
630
+		
631
+		public void insert(int lineIndex, int offset, String value) {
632
+			int tokenOffset = 0;
633
+			if (tokens.isEmpty()) {
634
+				tokens.add(new ZSToken(ZSTokenType.INVALID, value));
635
+				reparse(lineIndex, 0, lineIndex, 1);
636
+				return;
637
+			}
638
+			
639
+			for (int i = 0; i < tokens.size(); i++) {
640
+				ZSToken token = tokens.get(i);
641
+				if (tokenOffset + token.content.length() > offset) {
642
+					token = token.insert(offset - tokenOffset, value);
643
+					tokens.set(i, token);
644
+					length += value.length();
645
+					reparse(lineIndex, i, lineIndex, i + 1);
646
+					return;
647
+				}
648
+				tokenOffset += token.content.length();
649
+			}
650
+			
651
+			ZSToken token = tokens.get(tokens.size() - 1);
652
+			token = new ZSToken(token.type, token.content + value);
653
+			tokens.set(tokens.size() - 1, token);
654
+			length += value.length();
655
+			reparse(lineIndex, tokens.size() - 1, lineIndex, tokens.size());
656
+		}
657
+	}
658
+	
659
+	private void reparse(int fromLine, int fromToken, int toLine, int toToken) {
660
+		TokenReparser reparser = new TokenReparser(sourceFile.getName(), lines, fromLine, fromToken, toLine, toToken, tab.length());
661
+		List<ZSToken> tokens = reparser.reparse();
662
+		replaceTokens(fromLine, fromToken, reparser.getLine(), reparser.getToken(), tokens);
663
+	}
664
+	
665
+	private void replaceTokens(int fromLine, int fromToken, int toLine, int toToken, List<ZSToken> tokens) {
666
+		removeTokens(fromLine, fromToken, toLine, toToken);
667
+		insertTokens(fromLine, fromToken, tokens.iterator());
668
+	}
669
+	
670
+	private void removeTokens(int fromLine, int fromToken, int toLine, int toToken) {
671
+		if (toLine > fromLine) {
672
+			Line fromLineObject = lines.get(fromLine);
673
+			for (int i = fromLineObject.tokens.size() - 1; i >= fromToken; i--)
674
+				fromLineObject.tokens.remove(i);
675
+			
676
+			Line toLineObject = lines.get(fromLine);
677
+			for (int i = toToken - 1; i >= 0; i--)
678
+				toLineObject.tokens.remove(i);
679
+			
680
+			fromLineObject.merge(fromLine, lines.remove(toLine));
681
+			for (int i = toLine - 1; i > fromLine; i--)
682
+				lines.remove(i);
683
+			
684
+			onLinesUpdated();
685
+		} else {
686
+			Line line = lines.get(fromLine);
687
+			for (int i = toToken - 1; i >= fromToken; i--)
688
+				line.tokens.remove(i);
689
+		}
690
+	}
691
+	
692
+	private void insertTokens(int line, int tokenIndex, Iterator<ZSToken> tokens) {
693
+		boolean linesUpdated = false;
694
+		Line currentLine = lines.get(line);
695
+		while (tokens.hasNext()) {
696
+			ZSToken token = tokens.next();
697
+			if (token.type == ZSTokenType.T_WHITESPACE_NEWLINE) {
698
+				currentLine.calculateLength();
699
+				
700
+				Line newLine = new Line();
701
+				if (tokenIndex < currentLine.tokens.size()) {
702
+					for (int i = tokenIndex; i < currentLine.tokens.size(); i++) {
703
+						newLine.tokens.add(currentLine.tokens.remove(tokenIndex));
704
+					}
705
+				}
706
+				currentLine = newLine;
707
+				tokenIndex = 0;
708
+				lines.add(++line, currentLine);
709
+				linesUpdated = true;
710
+			} else if (token.type != ZSTokenType.T_WHITESPACE_CARRIAGE_RETURN) {
711
+				currentLine.tokens.add(tokenIndex++, token);
712
+			}
713
+		}
714
+		
715
+		currentLine.calculateLength();
716
+		
717
+		if (linesUpdated) {
718
+			onLinesUpdated();
719
+		} else {
720
+			// Could be further optimized to only repaint the modified characters
721
+			repaintLine(line);
722
+		}
723
+	}
724
+	
725
+	private enum TokenClass {
726
+		WHITESPACE(0xFF969696),
727
+		KEYWORD(0xFF0000E6),
728
+		OPERATOR(0xFF000000),
729
+		TYPE(0xFF0000E6),
730
+		IDENTIFIER(0xFF000000),
731
+		NUMBER(0xFF444444),
732
+		STRING(0xFFCE7B00),
733
+		INVALID(0xFFFA0A00);
734
+		
735
+		public final int color;
736
+		
737
+		TokenClass(int color) {
738
+			this.color = color;
739
+		}
740
+		
741
+		public static TokenClass get(ZSTokenType type) {
742
+			switch (type) {
743
+				case T_WHITESPACE_CARRIAGE_RETURN:
744
+				case T_WHITESPACE_NEWLINE:
745
+				case T_WHITESPACE_SPACE:
746
+				case T_WHITESPACE_TAB:
747
+					return WHITESPACE;
748
+				case T_IDENTIFIER:
749
+				case T_DOLLAR:
750
+					return IDENTIFIER;
751
+				case T_FLOAT:
752
+				case T_INT:
753
+					return NUMBER;
754
+				case T_STRING_SQ:
755
+				case T_STRING_DQ:
756
+					return STRING;
757
+				case K_IMPORT:
758
+				case K_ALIAS:
759
+				case K_CLASS:
760
+				case K_FUNCTION:
761
+				case K_INTERFACE:
762
+				case K_ENUM:
763
+				case K_STRUCT:
764
+				case K_EXPAND:
765
+				case K_VARIANT:
766
+				case K_ABSTRACT:
767
+				case K_FINAL:
768
+				case K_OVERRIDE:
769
+				case K_CONST:
770
+				case K_PRIVATE:
771
+				case K_PUBLIC:
772
+				case K_EXPORT:
773
+				case K_STATIC:
774
+				case K_PROTECTED:
775
+				case K_IMPLICIT:
776
+				case K_VIRTUAL:
777
+				case K_EXTERN:
778
+				case K_VAL:
779
+				case K_VAR:
780
+				case K_GET:
781
+				case K_IMPLEMENTS:
782
+				case K_SET:
783
+				case K_IN:
784
+				case K_IS:
785
+				case K_AS:
786
+				case K_MATCH:
787
+				case K_THROWS:
788
+				case K_SUPER:
789
+				case K_THIS:
790
+				case K_NULL:
791
+				case K_TRUE:
792
+				case K_FALSE:
793
+				case K_NEW:
794
+					
795
+				case K_IF:
796
+				case K_ELSE:
797
+				case K_DO:
798
+				case K_WHILE:
799
+				case K_FOR:
800
+				case K_THROW:
801
+				case K_LOCK:
802
+				case K_TRY:
803
+				case K_CATCH:
804
+				case K_FINALLY:
805
+				case K_RETURN:
806
+				case K_BREAK:
807
+				case K_CONTINUE:
808
+				case K_SWITCH:
809
+				case K_CASE:
810
+				case K_DEFAULT:
811
+					return KEYWORD;
812
+					
813
+				case K_VOID:
814
+				case K_ANY:
815
+				case K_BOOL:
816
+				case K_BYTE:
817
+				case K_SBYTE:
818
+				case K_SHORT:
819
+				case K_USHORT:
820
+				case K_INT:
821
+				case K_UINT:
822
+				case K_LONG:
823
+				case K_ULONG:
824
+				case K_FLOAT:
825
+				case K_DOUBLE:
826
+				case K_CHAR:
827
+				case K_STRING:
828
+					return TYPE;
829
+				case INVALID:
830
+					return INVALID;
831
+				default:
832
+					return OPERATOR;
833
+			}
834
+		}
835
+	}
836
+	
837
+	public class CursorPosition {
838
+		public final int line;
839
+		public final int offset;
840
+		
841
+		public CursorPosition(int line, int offset) {
842
+			this.line = line;
843
+			this.offset = offset;
844
+		}
845
+		
846
+		public int getX() {
847
+			return offsetToX(line, offset);
848
+		}
849
+		
850
+		public int getY() {
851
+			return lineToY(line);
852
+		}
853
+		
854
+		public boolean equals(CursorPosition other) {
855
+			return line == other.line && offset == other.offset;
856
+		}
857
+	}
858
+}

+ 33
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditorStyle.java View File

@@ -0,0 +1,33 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.editor;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class SourceEditorStyle {
13
+	public static final SourceEditorStyle DEFAULT = new SourceEditorStyle(3, 0xFFE9EFF8, 0xFFB0C5E3, 2, 2);
14
+	
15
+	public final int extraLineSpacing;
16
+	public final int currentLineHighlight;
17
+	public final int selectionColor;
18
+	public final int selectionPaddingTop;
19
+	public final int selectionPaddingBottom;
20
+	
21
+	public SourceEditorStyle(
22
+			int extraLineSpacing,
23
+			int currentLineHighlight,
24
+			int selectionColor,
25
+			int selectionPaddingTop,
26
+			int selectionPaddingBottom) {
27
+		this.extraLineSpacing = extraLineSpacing;
28
+		this.currentLineHighlight = currentLineHighlight;
29
+		this.selectionColor = selectionColor;
30
+		this.selectionPaddingTop = selectionPaddingTop;
31
+		this.selectionPaddingBottom = selectionPaddingBottom;
32
+	}
33
+}

+ 111
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenReparser.java View File

@@ -0,0 +1,111 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.editor;
7
+
8
+import java.io.IOException;
9
+import java.util.ArrayList;
10
+import java.util.List;
11
+import org.openzen.zenscript.lexer.CharReader;
12
+import org.openzen.zenscript.lexer.TokenParser;
13
+import org.openzen.zenscript.lexer.ZSToken;
14
+import org.openzen.zenscript.lexer.ZSTokenParser;
15
+import org.openzen.zenscript.lexer.ZSTokenType;
16
+
17
+/**
18
+ *
19
+ * @author Hoofdgebruiker
20
+ */
21
+public class TokenReparser {
22
+	private final List<SourceEditor.Line> lines;
23
+	private final String filename;
24
+	private final int toLine;
25
+	private final int toToken;
26
+	private final int spacesPerTab;
27
+	
28
+	private int lineIndex;
29
+	private int token;
30
+	
31
+	public TokenReparser(String filename, List<SourceEditor.Line> lines, int fromLine, int fromToken, int toLine, int toToken, int spacesPerTab) {
32
+		if (fromToken < 0 || toToken < 0)
33
+			throw new IllegalArgumentException("fromToken or toToken cannot be < 0");
34
+		
35
+		this.lines = lines;
36
+		this.filename = filename;
37
+		this.toLine = toLine;
38
+		this.toToken = toToken;
39
+		
40
+		this.lineIndex = fromLine;
41
+		this.token = fromToken;
42
+		this.spacesPerTab = spacesPerTab;
43
+	}
44
+	
45
+	public List<ZSToken> reparse() {
46
+		ReparseCharReader reader = new ReparseCharReader();
47
+		TokenParser<ZSToken, ZSTokenType> reparser = ZSTokenParser.createRaw(filename, reader, spacesPerTab);
48
+		List<ZSToken> result = new ArrayList<>();
49
+		while ((lineIndex < toLine || token < toToken || !reader.isAtTokenBoundary()) && reparser.hasNext())
50
+			result.add(reparser.next());
51
+		return result;
52
+	}
53
+	
54
+	private String get() {
55
+		SourceEditor.Line line = lines.get(lineIndex);
56
+		if (token == line.tokens.size())
57
+			return "\n"; // special "newline" token for transitions to next line
58
+		
59
+		return line.tokens.get(token).content;
60
+	}
61
+	
62
+	private boolean advance() {
63
+		if (lineIndex == lines.size() - 1 && token == lines.get(lineIndex).tokens.size())
64
+			return false;
65
+		
66
+		token++;
67
+		if (token > lines.get(lineIndex).tokens.size()) {
68
+			lineIndex++;
69
+			token = 0;
70
+		}
71
+		return true;
72
+	}
73
+	
74
+	public int getLine() {
75
+		return lineIndex;
76
+	}
77
+	
78
+	public int getToken() {
79
+		return token;
80
+	}
81
+	
82
+	private class ReparseCharReader implements CharReader {
83
+		private String token;
84
+		private int tokenOffset;
85
+		
86
+		public ReparseCharReader() {
87
+			token = get();
88
+		}
89
+		
90
+		public boolean isAtTokenBoundary() {
91
+			return tokenOffset == 0;
92
+		}
93
+
94
+		@Override
95
+		public int peek() throws IOException {
96
+			return token == null ? -1 : token.charAt(tokenOffset);
97
+		}
98
+
99
+		@Override
100
+		public int next() throws IOException {
101
+			int result = peek();
102
+			tokenOffset++;
103
+			if (tokenOffset == token.length()) {
104
+				advance();
105
+				token = get();
106
+				tokenOffset = 0;
107
+			}
108
+			return result;
109
+		}
110
+	}
111
+}

+ 53
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/LibraryTreeNode.java View File

@@ -0,0 +1,53 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.project;
7
+
8
+import org.openzen.drawablegui.DColorableIcon;
9
+import org.openzen.drawablegui.live.LiveList;
10
+import org.openzen.drawablegui.live.LiveMappedList;
11
+import org.openzen.zenscript.ide.host.IDELibrary;
12
+import org.openzen.zenscript.ide.ui.IDEWindow;
13
+import org.openzen.zenscript.ide.ui.icons.ColorableLibraryIcon;
14
+
15
+/**
16
+ *
17
+ * @author Hoofdgebruiker
18
+ */
19
+public class LibraryTreeNode extends ProjectOverviewNode {
20
+	private final IDELibrary library;
21
+	private final LiveList<ProjectOverviewNode> modules;
22
+	
23
+	public LibraryTreeNode(IDEWindow window, IDELibrary library) {
24
+		this.library = library;
25
+		
26
+		modules = new LiveMappedList<>(library.getModules(), module -> new ModuleTreeNode(window, module));
27
+	}
28
+	
29
+	@Override
30
+	public DColorableIcon getIcon() {
31
+		return ColorableLibraryIcon.INSTANCE;
32
+	}
33
+
34
+	@Override
35
+	public Kind getKind() {
36
+		return Kind.PROJECT;
37
+	}
38
+
39
+	@Override
40
+	public String getTitle() {
41
+		return library.getName();
42
+	}
43
+
44
+	@Override
45
+	public LiveList<ProjectOverviewNode> getChildren() {
46
+		return modules;
47
+	}
48
+
49
+	@Override
50
+	public boolean isLeaf() {
51
+		return false;
52
+	}
53
+}

+ 44
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/ModuleTreeNode.java View File

@@ -0,0 +1,44 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.project;
7
+
8
+import org.openzen.drawablegui.DColorableIcon;
9
+import org.openzen.zenscript.ide.host.IDEModule;
10
+import org.openzen.zenscript.ide.ui.IDEWindow;
11
+import org.openzen.zenscript.ide.ui.icons.ColorableModuleIcon;
12
+
13
+/**
14
+ *
15
+ * @author Hoofdgebruiker
16
+ */
17
+public class ModuleTreeNode extends PackageTreeNode {
18
+	private final IDEModule module;
19
+	
20
+	public ModuleTreeNode(IDEWindow window, IDEModule module) {
21
+		super(window, module.getRootPackage());
22
+		this.module = module;
23
+	}
24
+
25
+	@Override
26
+	public Kind getKind() {
27
+		return Kind.MODULE;
28
+	}
29
+
30
+	@Override
31
+	public DColorableIcon getIcon() {
32
+		return ColorableModuleIcon.INSTANCE;
33
+	}
34
+
35
+	@Override
36
+	public String getTitle() {
37
+		return module.getName();
38
+	}
39
+
40
+	@Override
41
+	public boolean isLeaf() {
42
+		return false;
43
+	}
44
+}

+ 57
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/PackageTreeNode.java View File

@@ -0,0 +1,57 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.project;
7
+
8
+import org.openzen.drawablegui.DColorableIcon;
9
+import org.openzen.drawablegui.live.LiveConcatList;
10
+import org.openzen.drawablegui.live.LiveList;
11
+import org.openzen.drawablegui.live.LiveMappedList;
12
+import org.openzen.zenscript.ide.host.IDEPackage;
13
+import org.openzen.zenscript.ide.ui.IDEWindow;
14
+import org.openzen.zenscript.ide.ui.icons.ColorableFolderIcon;
15
+
16
+/**
17
+ *
18
+ * @author Hoofdgebruiker
19
+ */
20
+public class PackageTreeNode extends ProjectOverviewNode {
21
+	private final IDEPackage pkg;
22
+	private final LiveList<ProjectOverviewNode> contents;
23
+	
24
+	public PackageTreeNode(IDEWindow window, IDEPackage pkg) {
25
+		this.pkg = pkg;
26
+		
27
+		contents = new LiveConcatList<>(
28
+				new LiveMappedList<>(pkg.getSubPackages(), sub -> new PackageTreeNode(window, sub)),
29
+				new LiveMappedList<>(pkg.getSourceFiles(), source -> new SourceFileTreeNode(window, source))
30
+		);
31
+	}
32
+
33
+	@Override
34
+	public LiveList<ProjectOverviewNode> getChildren() {
35
+		return contents;
36
+	}
37
+
38
+	@Override
39
+	public Kind getKind() {
40
+		return Kind.PACKAGE;
41
+	}
42
+
43
+	@Override
44
+	public DColorableIcon getIcon() {
45
+		return ColorableFolderIcon.INSTANCE;
46
+	}
47
+
48
+	@Override
49
+	public String getTitle() {
50
+		return pkg.getName();
51
+	}
52
+
53
+	@Override
54
+	public boolean isLeaf() {
55
+		return false;
56
+	}
57
+}

+ 34
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/ProjectOverviewNode.java View File

@@ -0,0 +1,34 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.project;
7
+
8
+import org.openzen.drawablegui.DMouseEvent;
9
+import org.openzen.drawablegui.live.LiveBool;
10
+import org.openzen.drawablegui.tree.DTreeNode;
11
+
12
+/**
13
+ *
14
+ * @author Hoofdgebruiker
15
+ */
16
+public abstract class ProjectOverviewNode implements DTreeNode<ProjectOverviewNode> {
17
+	private final LiveBool collapsed = new LiveBool();
18
+	
19
+	public abstract Kind getKind();
20
+	
21
+	@Override
22
+	public LiveBool isCollapsed() {
23
+		return collapsed;
24
+	}
25
+	
26
+	public enum Kind {
27
+		ROOT,
28
+		PROJECT,
29
+		LIBRARY,
30
+		MODULE,
31
+		PACKAGE,
32
+		SCRIPT
33
+	}
34
+}

+ 0
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/project/ProjectTreeNode.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save