Skip to content

Commit

Permalink
Add "Getting started" guide to website
Browse files Browse the repository at this point in the history
  • Loading branch information
jwharm committed Jun 14, 2024
1 parent 821acce commit f54614c
Show file tree
Hide file tree
Showing 31 changed files with 1,466 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish-maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
python-version: '3.x'

- name: Setup MkDocs
run: pip3 install mkdocs mkdocs-material
run: pip3 install mkdocs mkdocs-material mkdocs-macros-plugin

- name: Setup Java
uses: actions/setup-java@v4
Expand Down
10 changes: 10 additions & 0 deletions website/docs/getting-started/getting_started_00.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
This guide is based off the "[Getting Started](https://docs.gtk.org/gtk4/getting_started.html)" guide on [docs.gtk.org](https://docs.gtk.org/). The examples have been ported to Java, and the build instructions will help you setup a Java project and build it with Gradle.

GTK is a [widget toolkit](http://en.wikipedia.org/wiki/Widget_toolkit). Each user interface created by GTK consists of widgets. This is implemented in C using {{ javadoc('GObject') }}, an object-oriented framework for C. Widgets are organized in a hierarchy. The window widget is the main container. The user interface is then built by adding buttons, drop-down menus, input fields, and other widgets to the window. If you are creating complex user interfaces it is recommended to use GtkBuilder and its GTK-specific markup description language, instead of assembling the interface manually.

GTK is event-driven. The toolkit listens for events such as a click on a button, and passes the event to your application.

This chapter contains some tutorial information to get you started with GTK programming. It assumes that you have GTK, its dependencies, a Java compiler and the Gradle build tool installed and ready to use. If you need to build GTK itself first, refer to the [Compiling the GTK libraries](https://docs.gtk.org/gtk4/building.html/building.html) section in this
reference. If you don't know how to install Java or Gradle (we use Gradle in this tutorial), just install a Java IDE and follow its instructions, or use a command-line toolkit manager such as [SDKMAN!](https://sdkman.io/).

[Next](getting_started_01.md){ .md-button }
112 changes: 112 additions & 0 deletions website/docs/getting-started/getting_started_01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
## Basics

To begin our introduction to GTK, we'll start with a very simple application. This program will create an empty 200 × 200 pixel window.

![A window](img/window-default.png)

First, create a small Gradle project with the recommended layout:

```
[top-level project folder]
├── src/
│ ╰── main/
│ ├── java/
│ │ ╰── Example0.java
│ ╰── resources/
╰── build.gradle
```

Add the following content into the top-level `gradle.build` file:

```groovy
plugins {
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'io.github.jwharm.javagi:gtk:0.10.0'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(22)
}
}
tasks.named('run') {
jvmArgs += "--enable-native-access=ALL-UNNAMED"
}
application {
mainClass = "Example0"
}
```

!!! note
In this guide, we use Gradle to build the program and download dependencies. However, we don't use any Gradle-specific features or plugins. You can equally use Maven or any other build tool you prefer, or simply use your IDE project settings.

Enter the following content into `Example0.java`.

```java
import org.gnome.gtk.*;
import org.gnome.gio.ApplicationFlags;

public class Example0 {

public static void main(String[] args) {
Application app = new Application("org.gtk.example", ApplicationFlags.DEFAULT_FLAGS);
app.onActivate(() -> {
Window window = new ApplicationWindow(app);
window.setTitle("Window");
window.setDefaultSize(200, 200);
window.present();
});
app.run(args);
}
}
```

Save the java source file in the `src/main/java` folder.

You can compile and run the program with Gradle using:

```
gradle run
```

!!! tip
If the above command does not work, make sure all prerequisites are installed:

* Gradle version 8.7 or higher (check with `gradle --version`)

* Java version 22 or higher (check with `java --version`)

* Gtk version 4 or higher (check with `pkg-config --modversion gtk4`)

* GLib version 2.74 or higher (check with `pkg-config --modversion glib-2.0`)

All GTK applications will, of course, import classes from `org.gnome.gtk`. Top-level functions and constants are in the class `org.gnome.gtk.Gtk`.

In a GTK application, the purpose of the `main()` method is to create a {{ javadoc('Gtk.Application') }} object and run it. In this example a {{ javadoc('Gtk.Application') }} instance named `app` is initialized using `new Application()`.

When creating a {{ javadoc('Gtk.Application') }}, you need to pick an application identifier (a name) and pass it to `new Application()` as parameter. For this example `org.gtk.example` is used. For choosing an identifier for your application, see [this guide](https://developer.gnome.org/documentation/tutorials/application-id.html). Lastly, `new Application()` takes `ApplicationFlags` from package `org.gnome.gio` as input for your application, if your application would have special needs. To pass a combination of multiple flags, use `Set.of(flags1, flag2, ...)`.

Next the [activate signal](https://developer.gnome.org/documentation/tutorials/application.html) is connected to a callback method (or, in this case, lambda). The `activate` signal will be emitted when your application is launched with `Application.run()` on the line below.

The `run()` call takes as arguments the command line arguments (the `args` String array). Your application can override the command line handling, e.g. to open files passed on the commandline.

Within `Application.run()` the activate signal is sent and we then proceed to handle that signal. This is where we construct our GTK window, so that a window is shown when the application is launched. The call to `new ApplicationWindow()` will create a new {{ javadoc('Gtk.ApplicationWindow') }} and store it inside the `window` variable. The window will have a frame, a title bar, and window controls depending on the platform.

A window title is set using {{ javadoc('Window.setTitle') }}. This method takes a String as input. Finally the window size is set using {{ javadoc('Window.setDefaultSize') }} and the window is then shown by GTK via {{ javadoc('Window.present') }}.

When you close the window, by (for example) pressing the X button, the `Application.run()` call returns and the application exits. In a C app, a call to `g_object_unref` would be required here to free the `Application` object. In Java, a [Cleaner](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/ref/Cleaner.html) will do this after the garbage collector has freed the `Application` object.

While the program is running, GTK is receiving _events_. These are typically input events caused by the user interacting with your program, but also things like messages from the window manager or other applications. GTK processes these and as a result, _signals_ may be emitted on your widgets. Connecting handlers for these signals is how you normally make your program do something in response to user input.

The following example is slightly more complex, and tries to showcase some of the capabilities of GTK.

[Previous](getting_started_00.md){ .md-button } [Next](getting_started_02.md){ .md-button }
70 changes: 70 additions & 0 deletions website/docs/getting-started/getting_started_02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
In the long tradition of programming languages and libraries, this example is called *Hello, World*.

![Hello, world](img/hello-world.png)

### Hello World in Java

Create a new file with the following content named `Example1.java` in the folder `src/main/java`.

```java
import org.gnome.gtk.*;
import org.gnome.gio.ApplicationFlags;

public class Example1 {

private static void printHello() {
System.out.println("Hello World");
}

private static void activate(Application app) {
Window window = new ApplicationWindow(app);
window.setTitle("Window");
window.setDefaultSize(200, 200);

Box box = new Box(Orientation.VERTICAL, 0);
box.setHalign(Align.CENTER);
box.setValign(Align.CENTER);

window.setChild(box);

Button button = Button.withLabel("Hello World");

button.onClicked(Example1::printHello);
button.onClicked(window::destroy);

box.append(button);

window.present();
}

public static void main(String[] args) {
Application app = new Application("org.gtk.example", ApplicationFlags.DEFAULT_FLAGS);
app.onActivate(() -> activate(app));
app.run(args);
}
}
```

Change the final part of the `build.gradle` file to run `Example1.java`, and run it with `gradle run`:

```groovy
application {
mainClass = "Example1"
}
```

As seen above, `Example1.java` builds further upon `Example0.java` by adding a button to our window, with the label "Hello World". The handling of the `activate` event has been moved into a separate method for clarity. Two new GTK widgets are used to accomplish this, `button` and `box`. The box variable is created to store a {{ javadoc('Gtk.Box') }}, which is GTK's way of controlling the size and layout of buttons.

The `GtkBox` widget is created with {{ javadoc('Gtk.Grid.Grid()') }}, which takes a {{ javadoc('Gtk.Orientation') }} enumeration value as parameter. The buttons which this box will contain can either be laid out horizontally or vertically. This does not matter in this particular case, as we are dealing with only one button. After initializing box with the newly created `GtkBox`, the code adds the box widget to the window widget using {{ javadoc('Gtk.Window.setChild') }}.

Next the `button` variable is initialized in similar manner. {{ javadoc('Button.withLabel') }} is called which returns a {{ javadoc('Gtk.Button') }} to be stored in `button`. Afterwards `button` is added to our `box`.

Using `onClicked()`, the button is connected to a method in our app called `printHello()`, so that when the button is clicked, GTK will call this method. `printHello()` calls `System.out.println()` with the string "Hello World" which will print Hello World in a terminal if the GTK application was started from one.

After connecting `printHello()`, another signal is connected to the "clicked" state of the button using `onClicked()` as well. In this case the method being called back is {{ javadoc('Gtk.Window.destroy') }} for the `window` we created. This has the effect that when the button is clicked, the whole GTK window is destroyed.

More information about creating buttons can be found [here](https://wiki.gnome.org/HowDoI/Buttons).

The rest of the code in `Example1.java` is identical to `Example0.java`. The next section will elaborate further on how to add several {{ javadoc('Widget') }}s to your GTK application.

[Previous](getting_started_01.md){ .md-button } [Next](getting_started_03.md){ .md-button }
77 changes: 77 additions & 0 deletions website/docs/getting-started/getting_started_03.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
When creating an application, you'll want to put more than one widget inside a window. When you do so, it becomes important to control how each widget is positioned and sized. This is where packing comes in.

GTK comes with a large variety of _layout containers_ whose purpose it is to control the layout of the child widgets that are added to them, like:

- {{ javadoc('Gtk.Box') }}
- {{ javadoc('Gtk.Grid') }}
- {{ javadoc('Gtk.Revealer') }}
- {{ javadoc('Gtk.Stack') }}
- {{ javadoc('Gtk.Overlay') }}
- {{ javadoc('Gtk.Paned') }}
- {{ javadoc('Gtk.Expander') }}
- {{ javadoc('Gtk.Fixed') }}

The following example shows how the {{ javadoc('Gtk.Grid') }} container lets you arrange several buttons:

![Grid packing](img/grid-packing.png)

### Packing buttons

Create a new file with the following content named `Example2.java`.

```java
import org.gnome.gtk.*;
import org.gnome.gio.ApplicationFlags;

public class Example2 {

private static void printHello() {
System.out.println("Hello World");
}

private static void activate(Application app) {
// create a new window, and set its title
Window window = new ApplicationWindow(app);
window.setTitle("Window");

// Here we construct the container that is going pack our buttons
Grid grid = new Grid();

// Pack the container in the window
window.setChild(grid);

Button button = Button.withLabel("Button 1");
button.onClicked(Example2::printHello);

// Place the first button in the grid cell (0, 0), and make it fill
// just 1 cell horizontally and vertically (ie no spanning)
grid.attach(button, 0, 0, 1, 1);

button = Button.withLabel("Button 2");
button.onClicked(Example2::printHello);

// Place the second button in the grid cell (1, 0), and make it fill
// just 1 cell horizontally and vertically (ie no spanning)
grid.attach(button, 1, 0, 1, 1);

button = Button.withLabel("Quit");
button.onClicked(window::destroy);

// Place the Quit button in the grid cell (0, 1), and make it
// span 2 columns.
grid.attach(button, 0, 1, 2, 1);

window.present();
}

public static void main(String[] args) {
Application app = new Application("org.gtk.example", ApplicationFlags.DEFAULT_FLAGS);
app.onActivate(() -> activate(app));
app.run(args);
}
}
```

Update the `mainClass` in `build.gradle` to `Example2` and test the program with `gradle run`.

[Previous](getting_started_02.md){ .md-button } [Next](getting_started_04.md){ .md-button }
Loading

0 comments on commit f54614c

Please sign in to comment.