Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
029f03d
#1296: implemented basic IdeDialog Class
laim2003 Mar 27, 2026
89cfb67
#1296: Added basic context state manager class
laim2003 Mar 30, 2026
a53296d
#1785: Added basic question/confirmation modal implementation
laim2003 Mar 30, 2026
37cad74
#1296: implemented basic IdeDialog Class
laim2003 Mar 27, 2026
55a50ce
#1296: Added basic context state manager class
laim2003 Mar 30, 2026
3f1569f
#1785: Added basic question/confirmation modal implementation
laim2003 Mar 30, 2026
6ce99b3
Merge remote-tracking branch 'origin/#1296-implement-idecontext-for-g…
laim2003 Mar 30, 2026
7ef65f3
#1785: - Added logging to IdeGuiStateManager.
laim2003 Mar 30, 2026
1764b2f
#1724: implemented basic gui state management
laim2003 Apr 7, 2026
ddd3e5f
#1802: implemented basic gui state management
laim2003 Apr 7, 2026
9c5a720
Merge remote-tracking branch 'origin/#1802-state-management-implement…
laim2003 Apr 7, 2026
b28e216
#1802: removed a few changes so this branch can be the base for other…
laim2003 Apr 7, 2026
53dd605
#1784: Added basic progress bar structure
laim2003 Apr 7, 2026
5fb0201
#1784: Added Task Management system
laim2003 Apr 8, 2026
b38bda7
#1784: TaskOverviewWindow can now only be opened once
laim2003 Apr 8, 2026
97a3f81
#1784: TaskOverviewWindows now uses data binding for displaying progr…
laim2003 Apr 9, 2026
234e98b
#1802: disabled javadoc warning
laim2003 Apr 9, 2026
1c4e766
#1802: - fixed AppBaseTest being stuck
laim2003 Apr 9, 2026
6fc3884
Merge branch 'main' into #1802-state-management-implementation
laim2003 Apr 14, 2026
2026757
#1802: Added ProjectManager class to handle business logic of reading…
laim2003 Apr 14, 2026
c76e185
#1802: Updated IdeGuiStateManager to also update its projectManager, …
laim2003 Apr 14, 2026
6f92d93
#1802: javafx updated to java 25
laim2003 Apr 15, 2026
f3eef9f
#1784: - fixed main-view minimum layout bounds (for progress bar)
laim2003 Apr 15, 2026
9f741a5
Merge branch 'main' into #1802-state-management-implementation
laim2003 Apr 21, 2026
48fe018
Revert "#1802: javafx updated to java 25"
laim2003 Apr 21, 2026
e0ad015
#1803: added DI for IDE_ROOT in GuiStateManager
laim2003 Apr 21, 2026
2c9fc55
#1802: added DI for IDE_ROOT in GuiStateManager
laim2003 Apr 21, 2026
2b62c60
Merge remote-tracking branch 'origin/#1802-state-management-implement…
laim2003 Apr 21, 2026
40ff54e
#1802: Workaround for IDE_ROOT=null issue. The ideRoot in GuiStateMan…
laim2003 Apr 21, 2026
0b67210
#1802: removed redundant DI variant of switchContext() (DI via getIns…
laim2003 Apr 21, 2026
2e15d60
#1802: Added tests for GuiStateManager
laim2003 Apr 21, 2026
10cf940
Merge branch 'main' into #1802-state-management-implementation
laim2003 Apr 23, 2026
5f0b40a
#1802: Extracted mock IDE_ROOT logic into seperate class that can be …
laim2003 Apr 23, 2026
3b567ef
Merge remote-tracking branch 'origin/#1802-state-management-implement…
laim2003 Apr 23, 2026
e406b65
#1802: added ProjectManagerTest
laim2003 Apr 23, 2026
e0e2d9d
#1802: Detached startContext creation from switchContext in IdeGuiSta…
laim2003 Apr 24, 2026
d70f460
#1802: Fixed bug in IdeGuiStateManagerTest leading to test failure
laim2003 Apr 24, 2026
d6aace2
Merge branch 'main' into #1802-state-management-implementation
laim2003 Apr 24, 2026
9c78eac
#1802: Added ContextChangeListener; improved thread safety of IdeGuiS…
laim2003 Apr 24, 2026
4c84bb9
Merge branch 'main' into #1802-state-management-implementation
laim2003 Apr 24, 2026
69055d9
#1802: tests now use AbstractIdeContextTest
laim2003 Apr 28, 2026
9978ce9
#1802: Updated UI logic to:
laim2003 Apr 28, 2026
ac3a045
#1802: cleaned up ProjectManagerTest, fixed AppBaseTest to follow new…
laim2003 Apr 28, 2026
6c44432
#1802: cleaned up IdeGuiStateManagerTest
laim2003 Apr 28, 2026
c30c2ea
Merge branch 'main' into #1802-state-management-implementation
laim2003 Apr 28, 2026
1e8b556
#1802: cleaned up ProjectManagerTest to use FileUtils instead of own …
laim2003 Apr 28, 2026
d51111d
Merge remote-tracking branch 'origin/#1802-state-management-implement…
laim2003 Apr 28, 2026
880ad96
Merge branch '#1802-state-management-implementation' into #1784-Imple…
laim2003 Apr 30, 2026
2d293f0
#1784: improved seperation of concerns in TaskOverviewWindow, added d…
laim2003 May 8, 2026
860ac85
#1784: small fixes
laim2003 May 11, 2026
dbd7374
#1784: switched listener system to javafx ObservableList
laim2003 May 11, 2026
39fa408
#1784: improved TaksManager for UI Thread safety, added documentation…
laim2003 May 12, 2026
a02d248
#1784: cleanup
laim2003 May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@
</archive>
</configuration>
</execution>

<!-- jar for test classes -->
<execution>
<id>test-jar</id>
<goals>
Expand Down
2 changes: 1 addition & 1 deletion cli/src/main/java/com/devonfw/tools/ide/tool/gui/Gui.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ protected void doRun() {

Mvn mvn = context.getCommandletManager().getCommandlet(Mvn.class);

Path pomPath = context.getIdeInstallationPath().resolve("gui/pom.xml");
Path pomPath = context.getIdeInstallationPath().resolve("gui").resolve("pom.xml");
if (!Files.exists(pomPath)) {
LOG.error("Fatal error: The pom.xml file required for launching the IDEasy GUI could not be found in expected location: {}", pomPath);
return;
Expand Down
22 changes: 11 additions & 11 deletions cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
import java.util.Properties;

import org.junit.jupiter.api.Test;
import com.devonfw.tools.ide.security.ToolVersionChoice;
import com.devonfw.tools.ide.tool.ToolEditionAndVersion;
import com.devonfw.tools.ide.security.ToolVulnerabilities;
import com.devonfw.tools.ide.version.VersionIdentifier;
import com.devonfw.tools.ide.version.VersionRange;
import com.devonfw.tools.ide.url.model.file.json.Cve;
import com.devonfw.tools.ide.tool.ToolEdition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -24,13 +17,20 @@
import com.devonfw.tools.ide.log.IdeLogLevel;
import com.devonfw.tools.ide.os.SystemInfo;
import com.devonfw.tools.ide.os.SystemInfoMock;
import com.devonfw.tools.ide.security.ToolVersionChoice;
import com.devonfw.tools.ide.security.ToolVulnerabilities;
import com.devonfw.tools.ide.tool.ToolEdition;
import com.devonfw.tools.ide.tool.ToolEditionAndVersion;
import com.devonfw.tools.ide.url.model.file.json.Cve;
import com.devonfw.tools.ide.variable.IdeVariables;
import com.devonfw.tools.ide.version.IdeVersion;
import com.devonfw.tools.ide.version.VersionIdentifier;
import com.devonfw.tools.ide.version.VersionRange;

/**
* Test of {@link IdeContext}.
*/
class IdeContextTest extends AbstractIdeContextTest {
public class IdeContextTest extends AbstractIdeContextTest {

private static final Logger LOG = LoggerFactory.getLogger(IdeContextTest.class);

Expand Down Expand Up @@ -284,7 +284,7 @@ void testFindBashOnSystemPathOnWindowsWithInvalidBashPathSet() {
@Test
void testQuestionWithMultipleOptions() {
IdeTestContext context = newContext(PROJECT_BASIC, null, false);
String[] options = {"option1", "option2"};
String[] options = { "option1", "option2" };
context.setAnswers("option1");
String result = context.question(options, "Which option?");
assertThat(result).isEqualTo("option1");
Expand All @@ -293,7 +293,7 @@ void testQuestionWithMultipleOptions() {
@Test
void testQuestionWithSingleOptionAndEmptyAnswer() {
IdeTestContext context = newContext(PROJECT_BASIC, null, false);
String[] options = {"onlyOption"};
String[] options = { "onlyOption" };
context.setAnswers(""); // Empty answer (Enter)
String result = context.question(options, "Which option?");
assertThat(result).isEqualTo("onlyOption");
Expand All @@ -306,7 +306,7 @@ void testQuestionWithSingleToolVersionChoiceAndEmptyAnswer() {
VersionIdentifier version = VersionIdentifier.of("17");
Cve cve = new Cve("CVE-2023-XXXX", 9.8, List.of(VersionRange.of("[17,18)")));
ToolVersionChoice choice = new ToolVersionChoice(new ToolEditionAndVersion(edition, version), "current", ToolVulnerabilities.of(List.of(cve)));
ToolVersionChoice[] options = {choice};
ToolVersionChoice[] options = { choice };

context.setAnswers(""); // Empty answer (Enter)
ToolVersionChoice result = context.question(options, "Which version?");
Expand Down
20 changes: 20 additions & 0 deletions documentation/images/gui/Task Management structure.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@startuml
'https://plantuml.com/class-diagram

class TaskManager
class IdeGuiContext
class MainController implements ProgressListener
interface ProgressListener {
+void onUpdateTaskProgress(Task task)
+void onTaskAdded()
+void onTaskRemoved()
}
class GuiProgressBarHandling

IdeGuiContext ..> GuiProgressBarHandling : instantiates
IdeGuiContext ..> TaskManager : adds instantiated task to
TaskManager ..> MainController : notifies about task updates
TaskManager <.. GuiProgressBarHandling : when close() called: removes itself from TaskManager
MainController ..> TaskManager : registers itself for updates

@enduml
87 changes: 87 additions & 0 deletions documentation/images/gui/gui.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
@startuml
'https://plantuml.com/sequence-diagram

title GUI Structure


package "cli" {
abstract class AbstractIdeContext
}
package "gui" {
class AppLauncher {
+void main(String [])
}

class App {
+void main(String [])
+void start()
}

class IdeGuiStateManager <<Thread safe singleton>> {
-String projectDirectory
-IdeGuiContext currentContext
--
-IdeGuiStateManager()
+{static} IdeGuiStateManager getInstance()
..GUI context handling..
+IdeGuiContext switchContext(projectName: String, workspaceName: String)
+IdeGuiContext getCurrentContext()
+void addStateChangeListener(StateChangeListener listener)
+void removeStateChangeListener(StateChangeListener listener)
}

-static class "IdeGuiStateManager.Holder" as Holder {
-{static} INSTANCE
}

class MainController {
-void initialize()
..
-void setProjectsCombobox()
-void setWorkspaceValue()
..
-void updateContext(String projectName, String workspaceName)
}

abstract class "IdeGuiStateManager.StateChangeListener" as StateChangeListener {
+void onStateChange(IdeGuiContext newContext)
}

class IdeGuiContext


note right of IdeGuiStateManager
In the most basic sense, the GUI state depends on the current project context.
This is important to ensure that any commandlets will be executed in the correct project.
end note
note bottom of StateChangeListener
Idea: This listener is implemented by any UI component to get notified of any context changes.
Alternative: an abstract StateDependentGuiComponent class that implements the listener and provides an abstract method for handling context changes.
Or: restart the app everytime the context changes (probably not the user-friendliest idea)
end note
note right of App: FXML layout is loaded here
note bottom of Holder: simple instance wrapper class for Bill Pugh singleton
note right of IdeGuiContext: This will implement modals/progress bars
note top of AppLauncher
<b>Current considerations regarding the UI state management:</b>
- Darf die GUI mehr als einmal offen sein? (Fall: Fenster mit gleichem Projekt parallel offen)
- Wie Context-Handling implementieren?
- Wie werden Log messages gehandelt?
end note
}


'' Relations ''

AppLauncher --> App : launches

IdeGuiContext ..> AbstractIdeContext : extends

IdeGuiStateManager ..> IdeGuiContext : creates/manages instances of
IdeGuiStateManager --> StateChangeListener : inner class
IdeGuiStateManager -left> Holder : inner class

App ..> MainController : initializes when loading layout
MainController ..> IdeGuiStateManager : accesses/interacts with

@enduml
9 changes: 8 additions & 1 deletion gui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>ide-cli</artifactId>
<classifier>tests</classifier>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
Expand Down Expand Up @@ -68,6 +67,14 @@
<artifactId>openjfx-monocle</artifactId>
<scope>test</scope>
</dependency>

<!-- required for using ide-cli testing classes -->
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>3.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
48 changes: 43 additions & 5 deletions gui/src/main/java/com/devonfw/ide/gui/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

import java.io.IOException;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ButtonType;
import javafx.scene.image.Image;
import javafx.stage.Screen;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.devonfw.ide.gui.modal.IdeDialog;
import com.devonfw.ide.gui.progress.TaskManager;
import com.devonfw.tools.ide.variable.IdeVariables;
import com.devonfw.tools.ide.version.IdeVersion;

Expand All @@ -20,25 +26,57 @@ public class App extends Application {

Parent root;

private static final Logger LOG = LoggerFactory.getLogger(App.class);

@Override
public void start(Stage primaryStage) throws IOException {

Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
System.out.println("Uncaught exception in thread " + thread.getName() + ":" + throwable.getMessage());
//Left this in, because of issues with printing errors when the Fx Application Thread crashes. Needs further research.
throwable.printStackTrace();
Platform.runLater(() -> new IdeDialog(IdeDialog.AlertType.ERROR, throwable.getMessage()).showAndWait());
}
);

FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("main-view.fxml"));
fxmlLoader.setController(
new MainController(System.getenv(IdeVariables.IDE_ROOT.getName()))
);
root = fxmlLoader.load();

Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
Scene scene = new Scene(root, bounds.getWidth() / 2, bounds.getHeight() / 2);

Scene scene = new Scene(root, ((BorderPane) fxmlLoader.getRoot()).getPrefWidth(), ((BorderPane) fxmlLoader.getRoot()).getPrefHeight());
Image icon = new Image("com/devonfw/ide/gui/assets/devonfw.png");
primaryStage.getIcons().add(icon);
primaryStage.setTitle("IDEasy - version " + IdeVersion.getVersionString());
primaryStage.setScene(scene);
primaryStage.setMinWidth(scene.getWidth());
primaryStage.setMinHeight(scene.getHeight());
primaryStage.show();

primaryStage.setOnCloseRequest(event -> {

LOG.info("Closing application");
if (!TaskManager.getInstance().getTasks().isEmpty()) {
IdeDialog closeConfirm = new IdeDialog(IdeDialog.AlertType.CONFIRMATION, "There are still running tasks. Are you sure you want to exit?",
ButtonType.CLOSE, ButtonType.CANCEL);
closeConfirm.showAndWait().ifPresent(response -> {
if (response == ButtonType.CLOSE) {
exitApplication();
} else {
event.consume();
}
});
} else {
exitApplication();
}
});
}

private void exitApplication() {

Platform.exit();
System.exit(0);
}


Expand Down
3 changes: 2 additions & 1 deletion gui/src/main/java/com/devonfw/ide/gui/AppLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

/**
* Launcher class for the App. Workaround for "Error: JavaFX runtime components are missing, and are required to run this application." Inspired by
* <ahref=https://stackoverflow.com/questions/56894627/how-to-fix-error-javafx-runtime-components-are-missing-and-are-required-to-ru>StackOverflow</a>
* <a href=https://stackoverflow.com/questions/56894627/how-to-fix-error-javafx-runtime-components-are-missing-and-are-required-to-ru>StackOverflow</a>
*/
public class AppLauncher {

@SuppressWarnings("MissingJavadoc")
public static void main(final String[] args) {

App.main(args);
Expand Down
24 changes: 24 additions & 0 deletions gui/src/main/java/com/devonfw/ide/gui/FxHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.devonfw.ide.gui;

import javafx.application.Platform;

/**
* Helper class containing tools for interacting with JavaFX
*/
public class FxHelper {

/**
* Allows to run operations on the Fx Application Thread, but only, if the call is originating from the Fx Application Thread. Idea: Some tasks which are
* doing (potentially heavy) background work, might not originate from the Fx Application (UI) Thread.
*
* @param runnable code to execute
*/
public static void runFxSafe(Runnable runnable) {
if (Platform.isFxApplicationThread()) {
runnable.run();
} else {
Platform.runLater(runnable);
}
}

}
Loading