diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73cbf7e4..8e96d0d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# Next
+
+- Library: Fix Value size (ValueFactory) and property access
+- Generator: Add cairo PNG support
+- Examples: ImageIO sample
+
# 0.6.0
- Generator: Add markdown summary logs
diff --git a/examples/src/main/java/examples/App.java b/examples/src/main/java/examples/App.java
index 519fb8af..077080d3 100644
--- a/examples/src/main/java/examples/App.java
+++ b/examples/src/main/java/examples/App.java
@@ -28,6 +28,7 @@
import examples.gtk4_tutorial.CustomDrawing;
import examples.gtk4_tutorial.ExampleApplication;
import examples.gtk4_tutorial.UiBuilderExample;
+import examples.image_io.ImageIo;
public class App {
@@ -71,6 +72,7 @@ public static void main (String[] args) {
addSample(demoList, window, new ExampleApplication(app));
addSample(demoList, window, new GlibSettings());
addSample(demoList, window, new Accordion());
+ addSample(demoList, window, new ImageIo());
window.present();
});
diff --git a/examples/src/main/java/examples/ImageBridge.java b/examples/src/main/java/examples/ImageBridge.java
index c13568f8..8963fc6b 100644
--- a/examples/src/main/java/examples/ImageBridge.java
+++ b/examples/src/main/java/examples/ImageBridge.java
@@ -90,14 +90,12 @@ private Pixbuf loadPixbuf(int width, int height) throws IOException {
}
}
- private boolean drawLogo(Context context) {
+ private void drawLogo(Context context) {
if (pixbuf != null) {
context.save();
Gdk.cairoSetSourcePixbuf(context, pixbuf, 0, 0);
context.paint();
- return true;
}
- return false;
}
@Override
diff --git a/examples/src/main/java/examples/gtk4_demo/Pixbufs.java b/examples/src/main/java/examples/gtk4_demo/Pixbufs.java
index 2a751352..1070babe 100644
--- a/examples/src/main/java/examples/gtk4_demo/Pixbufs.java
+++ b/examples/src/main/java/examples/gtk4_demo/Pixbufs.java
@@ -57,7 +57,7 @@ public class Pixbufs implements DemoInterface {
private final Pixbuf[] images = new Pixbuf[IMAGE_NAMES.length];
/* Widgets */
- private DrawingArea da;
+ private DrawingArea drawingArea;
private static final long CYCLE_TIME = 3000000; /* 3 seconds */
private long startTime;
@@ -163,7 +163,7 @@ private boolean onTick(FrameClock frameClock) {
overallAlpha);
}
}
- da.queueDraw();
+ drawingArea.queueDraw();
return GlibConstants.SOURCE_CONTINUE;
}
@@ -175,11 +175,11 @@ public Window runDemo() {
try {
loadPixbufs();
frame = new Pixbuf(Colorspace.RGB, false, 8, backWidth, backHeight);
- da = new DrawingArea();
+ drawingArea = new DrawingArea();
- da.setDrawFunc((cb, drawingArea, cr, width, height, userData) -> onDraw(cr), null, (cb, data) -> {});
- demoWindow.setChild(da);
+ drawingArea.setDrawFunc((cb, drawingArea, cr, width, height, userData) -> onDraw(cr), null, (cb, data) -> {});
+ demoWindow.setChild(drawingArea);
demoWindow.setSizeRequest(backWidth, backHeight);
demoWindow.addTickCallback((cb, widget, frame_clock, user_data) -> onTick(frame_clock), null, (cb, data) -> {});
@@ -193,7 +193,7 @@ public Window runDemo() {
new Str("Failed to load an image: " + e.getMessage()));
dialog.onResponse(response_id -> demoWindow.destroy());
- dialog.show();
+ dialog.present();
}
return demoWindow;
}
diff --git a/examples/src/main/java/examples/image_io/ImageIo.java b/examples/src/main/java/examples/image_io/ImageIo.java
new file mode 100644
index 00000000..3b2c2743
--- /dev/null
+++ b/examples/src/main/java/examples/image_io/ImageIo.java
@@ -0,0 +1,227 @@
+package examples.image_io;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import ch.bailu.gtk.cairo.Cairo;
+import ch.bailu.gtk.cairo.Format;
+import ch.bailu.gtk.cairo.Surface;
+import ch.bailu.gtk.gdk.Gdk;
+import ch.bailu.gtk.gdkpixbuf.Pixbuf;
+import ch.bailu.gtk.gio.File;
+import ch.bailu.gtk.gtk.Box;
+import ch.bailu.gtk.gtk.Button;
+import ch.bailu.gtk.gtk.DrawingArea;
+import ch.bailu.gtk.gtk.DropDown;
+import ch.bailu.gtk.gtk.FileDialog;
+import ch.bailu.gtk.gtk.Label;
+import ch.bailu.gtk.gtk.Orientation;
+import ch.bailu.gtk.gtk.Widget;
+import ch.bailu.gtk.gtk.Window;
+import ch.bailu.gtk.lib.bridge.Image;
+import ch.bailu.gtk.lib.util.JavaResource;
+import ch.bailu.gtk.type.Str;
+import ch.bailu.gtk.type.Strs;
+import ch.bailu.gtk.type.exception.AllocationError;
+import examples.DemoInterface;
+
+public class ImageIo implements DemoInterface {
+ private static int SIZE = 500;
+ private static Str TITLE = new Str("Image IO");
+ private static Str DESCRIPTION = new Str("Load and save images using different methods");
+
+ private DrawingArea drawingArea;
+ private Surface imageSurface;
+ private Label statusLabel;
+ private DropDown methodDropdown;
+
+ @Override
+ public Window runDemo() {
+ statusLabel = new Label(TITLE);
+ statusLabel.setXalign(0);
+
+ var vbox = new Box(Orientation.VERTICAL, 5);
+ var hbox = new Box(Orientation.HORIZONTAL, 5);
+ var demoWindow = new Window();
+
+ hbox.append(createOpenButton(demoWindow));
+ hbox.append(createSaveButton(demoWindow));
+ hbox.append(createLoadDefaultButton());
+ hbox.append(new Label("Method:"));
+ hbox.append(createMethodDropDown());
+
+ vbox.append(hbox);
+ vbox.append(createDrawingArea());
+ vbox.append(statusLabel);
+ vbox.setMarginTop(5);
+ vbox.setMarginBottom(5);
+ vbox.setMarginStart(5);
+ vbox.setMarginEnd(5);
+
+ loadDefaultImage();
+
+ demoWindow.setSizeRequest(SIZE, SIZE);
+ demoWindow.setChild(vbox);
+ return demoWindow;
+ }
+
+ private Widget createDrawingArea() {
+ drawingArea = new DrawingArea();
+ drawingArea.setHexpand(true);
+ drawingArea.setVexpand(true);
+
+ drawingArea.setDrawFunc((__self, drawing_area, cr, width, height, user_data) -> {
+ if (imageSurface != null) {
+ cr.save();
+ cr.setSourceSurface(imageSurface, 0, 0);
+ cr.paint();
+ }
+ }, null, null);
+ return drawingArea;
+ }
+
+ private Widget createMethodDropDown() {
+ var strs = new Strs(new String[]{"Cairo PNG", "Image Bridge"});
+ methodDropdown = DropDown.newFromStringsDropDown(strs);
+ return methodDropdown;
+ }
+
+ private Widget createLoadDefaultButton() {
+ var result = new Button();
+ result.setLabel("GTK.svg");
+ result.onClicked(()-> loadDefaultImage());
+ return result;
+ }
+
+ private Widget createSaveButton(Window parent) {
+ var result = new Button();
+ result.setLabel("Save…");
+ result.onClicked(()->{
+ var fileDialog = new FileDialog();
+ fileDialog.save(parent, null, (__self, source_object, res, data) -> {
+ try {
+ saveImage(fileDialog.saveFinish(res));
+ } catch (AllocationError e) {
+ statusLabel.setLabel(e.getMessage());
+ }
+ }, null);
+ });
+ return result;
+ }
+
+ private Widget createOpenButton(Window parent) {
+ var result = new Button();
+ result.setLabel("Open…");
+ result.onClicked(()->{
+ var fileDialog = new FileDialog();
+ fileDialog.open(parent, null, (__self, source_object, res, data) -> {
+ try {
+ loadImage(fileDialog.openFinish(res));
+ } catch (AllocationError e) {
+ statusLabel.setLabel(e.getMessage());
+ }
+ }, null);
+ });
+ return result;
+ }
+
+ private void loadDefaultImage() {
+ var path = "/GTK.svg";
+
+ try (InputStream inputStream = new JavaResource(path).asStream()) {
+ var start = System.currentTimeMillis();
+
+ var pixbuf = Image.load(inputStream, SIZE, SIZE);
+ var surface = Cairo.imageSurfaceCreate(Format.ARGB32, SIZE, SIZE);
+ var context = surface.createContext();
+ Gdk.cairoSetSourcePixbuf(context,pixbuf,0,0);
+ context.paint();
+ pixbuf.unref();
+ var delta = System.currentTimeMillis() - start;
+
+ statusLabel.setLabel(delta + "ms: R " + path);
+ imageSurface = surface;
+ drawingArea.queueDraw();
+ } catch (IOException e) {
+ statusLabel.setLabel(e.getMessage());
+ }
+ }
+
+ private void saveImage(File file) {
+ if (file != null && file.isNotNull()) {
+ var path = file.getPath();
+ var start = System.currentTimeMillis();
+
+ if (methodDropdown.getSelected() == 0) {
+ imageSurface.writeToPng(path);
+ } else {
+ imageBridgeWrite(path.toString());
+ }
+
+ var delta = System.currentTimeMillis() - start;
+ statusLabel.setLabel(delta + "ms: W " + path);
+ path.destroy();
+ }
+ }
+
+ private void imageBridgeWrite(String path) {
+ try (OutputStream outputStream = new FileOutputStream(path)){
+ Pixbuf pixbuf = Gdk.pixbufGetFromSurface(imageSurface, 0, 0, imageSurface.getWidth(), imageSurface.getHeight());
+ Image.save(outputStream, pixbuf, "png");
+ pixbuf.unref();
+ } catch (IOException e) {
+ statusLabel.setText(e.getMessage());
+ }
+ }
+
+ private void loadImage(File file) {
+ if (file != null && file.isNotNull()) {
+ var path = file.getPath();
+
+ var start = System.currentTimeMillis();
+ Surface surface;
+
+ if (methodDropdown.getSelected() == 0) {
+ surface = Cairo.imageSurfaceCreateFromPng(path);
+ } else {
+ surface = imageBridgeRead(path.toString());
+ }
+
+ if (surface != null) {
+ var delta = System.currentTimeMillis() - start;
+ statusLabel.setLabel(delta + "ms: R " + path);
+ imageSurface = surface;
+ drawingArea.queueDraw();
+ }
+ path.destroy();
+ }
+ }
+
+ private Surface imageBridgeRead(String path) {
+ try (InputStream inputStream = new FileInputStream(path)) {
+ var pixbuf = Image.load(inputStream);
+ var surface = Cairo.imageSurfaceCreate(Format.ARGB32, pixbuf.getWidth(), pixbuf.getHeight());
+ var context = surface.createContext();
+ Gdk.cairoSetSourcePixbuf(context,pixbuf,0,0);
+ context.paint();
+ pixbuf.unref();
+ return surface;
+ } catch (IOException e) {
+ statusLabel.setText(e.getMessage());
+ }
+ return null;
+ }
+
+ @Override
+ public Str getTitle() {
+ return TITLE;
+ }
+
+ @Override
+ public Str getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/examples/src/main/java/examples/libadwaita/layout/SplitViewDemo.java b/examples/src/main/java/examples/libadwaita/layout/SplitViewDemo.java
new file mode 100644
index 00000000..5995f670
--- /dev/null
+++ b/examples/src/main/java/examples/libadwaita/layout/SplitViewDemo.java
@@ -0,0 +1,124 @@
+package examples.libadwaita.layout;
+
+import ch.bailu.gtk.adw.Application;
+import ch.bailu.gtk.adw.ApplicationWindow;
+import ch.bailu.gtk.adw.Breakpoint;
+import ch.bailu.gtk.adw.BreakpointBin;
+import ch.bailu.gtk.adw.BreakpointCondition;
+import ch.bailu.gtk.adw.BreakpointConditionLengthType;
+import ch.bailu.gtk.adw.HeaderBar;
+import ch.bailu.gtk.adw.NavigationPage;
+import ch.bailu.gtk.adw.NavigationSplitView;
+import ch.bailu.gtk.gtk.Box;
+import ch.bailu.gtk.gtk.Button;
+import ch.bailu.gtk.gtk.Label;
+import ch.bailu.gtk.gtk.Orientation;
+import ch.bailu.gtk.gtk.PositionType;
+import ch.bailu.gtk.gtk.Unit;
+import ch.bailu.gtk.type.Str;
+import ch.bailu.gtk.type.Strs;
+
+public class SplitViewDemo {
+
+
+
+ public static void main(String[] args) {
+ var app = new Application(new Str("org.example.SplitViewDemo"), 0);
+
+ app.onActivate(() -> {
+ var headerBar = new HeaderBar();
+ var applicationWindow = new ApplicationWindow(app);
+ applicationWindow.setContent(createNavigationSplitView(applicationWindow, headerBar));
+ applicationWindow.present();
+ headerBar.setShowEndTitleButtons(true);
+ });
+
+ app.run(0, Strs.NULL);
+ }
+
+ private static NavigationSplitView createNavigationSplitView(ApplicationWindow applicationWindow, HeaderBar headerBar) {
+ var navigationSplitView = new NavigationSplitView();
+ var toLevel2Button = Button.newWithLabelButton("Show level 2");
+ var toLevel1Button = Button.newWithLabelButton("Show level 1");
+
+
+ var breakpoint = new Breakpoint(BreakpointCondition.newLengthBreakpointCondition(BreakpointConditionLengthType.MAX_WIDTH, 400d, Unit.POINTS));
+ breakpoint.onApply(() -> {
+ navigationSplitView.setCollapsed(true);
+ toLevel2Button.setVisible(true);
+ toLevel1Button.setVisible(true);
+ });
+ breakpoint.onUnapply(() -> {
+ navigationSplitView.setCollapsed(false);
+ toLevel2Button.setVisible(false);
+ toLevel1Button.setVisible(false);
+ });
+ applicationWindow.addBreakpoint(breakpoint);
+
+ toLevel1Button.onClicked(()->navigationSplitView.setShowContent(false));
+ toLevel2Button.onClicked(()->navigationSplitView.setShowContent(true));
+
+ navigationSplitView.setSidebar(createLeftPage(toLevel2Button, "Level 1", headerBar));
+ navigationSplitView.setContent(createRightPage(toLevel1Button, "Level 2 & 3"));
+ return navigationSplitView;
+ }
+
+ private static NavigationPage createRightPage(Button button, String title) {
+
+ var breakpointBin = new BreakpointBin();
+ var box = new Box(Orientation.VERTICAL, 0);
+ var navigationSplitView = new NavigationSplitView();
+ var toLevel3Button = Button.newWithLabelButton("Show level 3");
+ var toLevel2Button = Button.newWithLabelButton("Show level 2");
+
+
+ var breakpoint = new Breakpoint(BreakpointCondition.newLengthBreakpointCondition(BreakpointConditionLengthType.MAX_WIDTH, 400d, Unit.POINTS));
+ breakpoint.onApply(() -> {
+ navigationSplitView.setCollapsed(true);
+ toLevel3Button.setVisible(true);
+ toLevel2Button.setVisible(true);
+ });
+ breakpoint.onUnapply(() -> {
+ navigationSplitView.setCollapsed(false);
+ toLevel3Button.setVisible(false);
+ toLevel2Button.setVisible(false);
+ });
+
+ var headerBar = new HeaderBar();
+ System.out.println(headerBar.getDecorationLayout());
+ box.append(headerBar);
+
+ toLevel3Button.onClicked(()->navigationSplitView.setShowContent(true));
+ toLevel2Button.onClicked(()->navigationSplitView.setShowContent(false));
+
+ breakpointBin.addBreakpoint(breakpoint);
+ breakpointBin.setIntProperty("width-request", 100);
+ breakpointBin.setIntProperty("height-request", 100);
+ navigationSplitView.setContent(createInnerPage(toLevel2Button, "Level 3"));
+ navigationSplitView.setSidebar(createInnerPage(toLevel3Button, "Level 2"));
+
+ navigationSplitView.setSidebarPosition(PositionType.RIGHT);
+
+ box.append(navigationSplitView);
+ box.append(button);
+ breakpointBin.setChild(box);
+
+ return new NavigationPage(breakpointBin, title);
+ }
+
+ private static NavigationPage createInnerPage(Button button, String title) {
+ var box = new Box(Orientation.VERTICAL, 0);
+ box.append(new Label(title));
+ box.append(button);
+ return new NavigationPage(box, title);
+ }
+
+ private static NavigationPage createLeftPage(Button button, String title, HeaderBar headerBar) {
+ var box = new Box(Orientation.VERTICAL, 0);
+
+ box.append(headerBar);
+ box.append(new Label(title));
+ box.append(button);
+ return new NavigationPage(box, title);
+ }
+}
diff --git a/generator/src/main/resources/cairo-custom.gir b/generator/src/main/resources/cairo-custom.gir
index 1c97f7ca..93e0412f 100644
--- a/generator/src/main/resources/cairo-custom.gir
+++ b/generator/src/main/resources/cairo-custom.gir
@@ -743,6 +743,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1728,6 +1743,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java-gtk/src/main/java/ch/bailu/gtk/gtk/ValueFactory.java b/java-gtk/src/main/java/ch/bailu/gtk/gtk/ValueFactory.java
index 06f1fd7b..9a79ce55 100644
--- a/java-gtk/src/main/java/ch/bailu/gtk/gtk/ValueFactory.java
+++ b/java-gtk/src/main/java/ch/bailu/gtk/gtk/ValueFactory.java
@@ -7,8 +7,11 @@
import ch.bailu.gtk.type.Str;
public class ValueFactory {
+ // TODO: This should come from SizeTable.kt
+ private static final int VALUE_SIZE = 24;
+
public static Value initValue(long typeID) {
- return new Value(new Record(5).cast()).init(typeID);
+ return new Value(new Record(VALUE_SIZE).cast()).init(typeID);
}
public static Value toValue(Str str) {
diff --git a/java-gtk/src/test/java/ch/bailu/gtk/TestPropertyAccess.java b/java-gtk/src/test/java/ch/bailu/gtk/TestPropertyAccess.java
index eec380d0..07cfbeb1 100644
--- a/java-gtk/src/test/java/ch/bailu/gtk/TestPropertyAccess.java
+++ b/java-gtk/src/test/java/ch/bailu/gtk/TestPropertyAccess.java
@@ -7,6 +7,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
+import ch.bailu.gtk.adw.BreakpointBin;
import ch.bailu.gtk.gdk.Display;
import ch.bailu.gtk.gio.ListStore;
import ch.bailu.gtk.glib.Glib;
@@ -94,4 +95,17 @@ public void testPropertyAccessBox() {
assertEquals(box.getCanFocus(), box.getBooleanProperty("homogeneous"));
assertTrue(box.getHomogeneous());
}
+
+ @Test
+ @EnabledIf("gtkInit")
+ public void testPropertyAccessBreakpointBin() {
+ var breakpointBin = new BreakpointBin();
+
+ for (int i=0; i< 10000; i++) {
+ breakpointBin.setIntProperty("width-request", i);
+ breakpointBin.setIntProperty("height-request", i);
+
+ assertEquals(i, breakpointBin.getIntProperty("width-request"));
+ }
+ }
}