Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 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
98a7072
Merge branch 'main' into #1802-state-management-implementation
laim2003 May 5, 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
c55a3a3
Merge branch 'main' into #1802-state-management-implementation
laim2003 May 12, 2026
39fa408
#1784: improved TaksManager for UI Thread safety, added documentation…
laim2003 May 12, 2026
a02d248
#1784: cleanup
laim2003 May 12, 2026
4747bae
#1802: cleaned up test folders, fixed wrong Logger used in ProjectMan…
laim2003 May 26, 2026
e03949d
#1802: cleaned up test folders #2
laim2003 May 26, 2026
e4e5bd9
#1802: cleaned up test folders nr. 3
laim2003 May 26, 2026
44791ef
Merge branch 'main' into #1802-state-management-implementation
laim2003 May 26, 2026
dab292f
#1802: removed caching from ProjectManager
laim2003 May 26, 2026
11a9546
#1802: cleanup
laim2003 May 26, 2026
4781ea5
#1802: cleanup
laim2003 May 26, 2026
9ee0bc2
#1802: cleanup of ide.properties
laim2003 May 26, 2026
9a76bfc
Merge branch 'main' into #1802-state-management-implementation
laim2003 May 29, 2026
f504d35
Merge branch 'main' into #1802-state-management-implementation
hohwille May 29, 2026
99b617d
Merge branch 'main' into #1802-state-management-implementation
hohwille Jun 1, 2026
dbe8c85
#1802: applied recommendations from @hohwille
laim2003 Jun 2, 2026
666f0a7
#1802: applied recommendations from @hohwille
laim2003 Jun 2, 2026
f9422b5
Merge branch 'main' into #1802-state-management-implementation
laim2003 Jun 5, 2026
f2522db
#1802: fix ProjectManagerTest
laim2003 Jun 5, 2026
4cfc84b
Merge branch '#1802-state-management-implementation' into #1784-Imple…
laim2003 Jun 5, 2026
4e19a43
#1784: Added TaskManagerTest
laim2003 Jun 5, 2026
3711a64
#1784: Added test for TaskWindow
laim2003 Jun 9, 2026
d9ea676
#1784: Added test for status label
laim2003 Jun 9, 2026
e5187c2
#1784: fixed window positioning logic
laim2003 Jun 9, 2026
24ff9b1
#1784: updated Changelog
laim2003 Jun 10, 2026
3d6d064
Merge branch 'main' into #1784-Implement-JavaFX-based-progress-bars-f…
laim2003 Jun 16, 2026
3ac55f5
#1784: Changed singleton design to DI for GuiStateManager and TaskMan…
laim2003 Jun 16, 2026
afd910f
#1784: moved gui documentation UML files into seperate branch/PR #2035
laim2003 Jun 16, 2026
1f64d4f
#1987: Refactored use of TaskManager in App.java.
laim2003 Jun 23, 2026
20cd303
#1987: Refactored listener setup into separate method.
laim2003 Jun 23, 2026
cd389f9
Merge branch 'main' into #1784-Implement-JavaFX-based-progress-bars-f…
laim2003 Jun 23, 2026
6f4c54f
#1987: updated CHANGELOG.adoc
laim2003 Jun 23, 2026
c71855d
Merge remote-tracking branch 'origin/main' into #1784-Implement-JavaF…
maybeec Jul 4, 2026
24a24e4
Merge remote-tracking branch 'upstream/main' into #1784-Implement-Jav…
maybeec Jul 4, 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
4 changes: 3 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/1784[#1784]: GUI now supports displaying progress bars

* https://github.com/devonfw/IDEasy/issues/1937[#1937]: Link content from settings into workspaces
* https://github.com/devonfw/IDEasy/issues/1056[#1056]: Improve MSI license display by generating formatted RTF from AsciiDoc
* https://github.com/devonfw/IDEasy/issues/2052[#2052]: Split issue statistics in 2 pie-charts
Expand All @@ -28,7 +30,7 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/1985[#1985]: Fix path traversal errors when extracting ZIPs on Windows
* https://github.com/devonfw/IDEasy/issues/1255[#1255]: Enhance snapshot version recognition in IDEasy
* https://github.com/devonfw/IDEasy/issues/1964[#1964]: Fixed gui not launching with older project java versions
* https://github.com/devonfw/IDEasy/issues/1968[#1968]: Fixed gui blocking the terminal session while running.
* https://github.com/devonfw/IDEasy/issues/1968[#1968]: Fixed gui blocking the terminal session while running
* https://github.com/devonfw/IDEasy/issues/1849[#1849]: Add VSCodium support
* https://github.com/devonfw/IDEasy/issues/1391[#1391]: Fix bashrc messed with terraform completions
* https://github.com/devonfw/IDEasy/issues/1922[#1922]: Add Task CLI to IDEasy commandlets
Expand Down
36 changes: 34 additions & 2 deletions gui/src/main/java/com/devonfw/ide/gui/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
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.stage.Stage;

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

import com.devonfw.ide.gui.context.GuiStateManager;
import com.devonfw.ide.gui.context.TaskManager;
import com.devonfw.ide.gui.modal.IdeDialog;
import com.devonfw.tools.ide.variable.IdeVariables;
import com.devonfw.tools.ide.version.IdeVersion;
Expand All @@ -31,14 +34,19 @@ public class App extends Application {
public void start(Stage primaryStage) throws IOException {

Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
LOG.error("Uncaught exception in thread {}: {}", thread.getName(), throwable.getMessage(), 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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already have this in my PR #2027, wrapping the start method logic in a try-catch would allow startup errors to be caught and shown properly in the dialog

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use proper logging (SLF4J)?
Calling printStackTrace() (and also System.out) directly is typically discouraged.

Platform.runLater(() -> new IdeDialog(IdeDialog.AlertType.ERROR, throwable.getMessage()).showAndWait());
}
);
TaskManager taskManager = new TaskManager();
GuiStateManager guiStateManager = new GuiStateManager(taskManager, null);

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

Expand All @@ -52,6 +60,30 @@ public void start(Stage primaryStage) throws IOException {
primaryStage.setMinWidth(scene.getWidth());
primaryStage.setMinHeight(scene.getHeight());
primaryStage.show();

primaryStage.setOnCloseRequest(event -> {

LOG.info("Closing application");
if (!taskManager.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: 1 addition & 2 deletions gui/src/main/java/com/devonfw/ide/gui/AppLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
*/
public class AppLauncher {

@SuppressWarnings("MissingJavadoc")
public static void main(final String[] args) {
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 running operations on the Fx Application Thread, but only if the call is originating from the Fx Application Thread. Idea: Some tasks that are doing
* (potentially heavy) background work might not originate from the Fx Application (UI) Thread but will still interact with javafx observable collections.
*
* @param runnable code to execute
*/
public static void runFxSafe(Runnable runnable) {
if (Platform.isFxApplicationThread()) {
runnable.run();
} else {
Platform.runLater(runnable);
}
}

}
157 changes: 139 additions & 18 deletions gui/src/main/java/com/devonfw/ide/gui/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,36 @@
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.util.List;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;

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

import com.devonfw.ide.gui.context.IdeGuiStateManager;
import com.devonfw.ide.gui.context.GuiStateManager;
import com.devonfw.ide.gui.context.ProjectManager;
import com.devonfw.ide.gui.context.TaskManager;
import com.devonfw.ide.gui.modal.IdeDialog;
import com.devonfw.ide.gui.progress.ProgressBarTask;
import com.devonfw.ide.gui.progress.taskwindow.TaskOverviewWindow;

/**
* Controller of the main screen of the dashboard GUI.
*/
public class MainController {

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

private ProjectManager projectManager;
private final GuiStateManager guiStateManager;
private final ProjectManager projectManager;
private final TaskManager taskManager;


@FXML
Expand All @@ -43,19 +54,60 @@ public class MainController {
@FXML
private Button vsCodeOpen;

private final String directoryPath;
@FXML
private Label statusLabel;

@FXML
private ProgressBar statusProgressBar;
private final double PROGRESSBAR_VISIBLE_WIDTH = 150.0;

private final String ideRootPath;
private Path projectValue;
private Path workspaceValue;

/**
* Constructor
*
* @param ideRoot the IDE_ROOT path
* @param guiStateManager the {@link GuiStateManager} to be used in this application instance
*/
public MainController(String directoryPath) {
public MainController(String ideRoot, GuiStateManager guiStateManager) {

LOG.debug("IDE_ROOT path={}", directoryPath);
this.directoryPath = directoryPath;
LOG.debug("IDE_ROOT path={}", ideRoot);
this.ideRootPath = ideRoot;
this.guiStateManager = guiStateManager;
this.taskManager = guiStateManager.getTaskManager();
this.projectManager = guiStateManager.getProjectManager();

this.projectManager = IdeGuiStateManager.getInstance().getProjectManager();
setUpTaskListListener();
}

private void setUpTaskListListener() {

ListChangeListener<ProgressBarTask> taskListChangeListener = change -> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion: Maybe we could extract this listener setup into a separate method to improve readability

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed this in the latest commits

List<ProgressBarTask> tasks = taskManager.getTasks();

while (change.next()) {
if (change.wasAdded()) {
LOG.debug("Added: {}", change.getAddedSubList());

for (ProgressBarTask product : change.getAddedSubList()) {
product.currentProgressProperty().addListener((_, _, _) ->
updateStatusLabel(tasks)
);
}
updateStatusLabel(tasks);
} else if (change.wasRemoved()) {
LOG.debug("Removed: {}", change.getRemoved());

updateStatusLabel(tasks);
} else if (change.wasUpdated()) {

updateStatusLabel(tasks);
}
}
};
taskManager.getTasks().addListener(taskListChangeListener);
}

@FXML
Expand Down Expand Up @@ -90,7 +142,7 @@ private void openVsCode() {

private void setProjectsComboBox() {

assert (directoryPath != null) : "directoryPath is null! Please check the setup of your environment variables (IDE_ROOT)";
assert (ideRootPath != null) : "directoryPath is null! Please check the setup of your environment variables (IDE_ROOT)";

List<String> projects = projectManager.getProjectNames();

Expand All @@ -107,7 +159,7 @@ private void setProjectsComboBox() {

private void setWorkspaceComboBox() {

List<String> workspaces = null;
List<String> workspaces;
try {
workspaces = projectManager.getWorkspaceNames(selectedProject.getValue());
} catch (NotDirectoryException e) {
Expand All @@ -129,20 +181,89 @@ private void setWorkspaceComboBox() {

private void openIDE(String inIde) {

IdeGuiStateManager
.getInstance()
.getCurrentContext()
.getCommandletManager()
.getCommandlet(inIde)
.run();
Task<Void> downloadTask = runIdeCommandTask(inIde);

new Thread(downloadTask).start();
}

private Task<Void> runIdeCommandTask(String inIde) {

try (ProgressBarTask task = (ProgressBarTask) guiStateManager.getCurrentContext()
.newProgressBarIndeterminate("Starting " + inIde)) {
Task<Void> downloadTask = new Task<>() {
@Override
protected Void call() {
guiStateManager
.getCurrentContext()
.getCommandletManager()
.getCommandlet(inIde)
.run();
task.close();
return null;
}
};

downloadTask.setOnFailed(_ -> Platform.runLater(() -> {
task.close();
IdeDialog errorDialog = new IdeDialog(AlertType.ERROR, "Error occurred while launching " + inIde);
errorDialog.showAndWait();
}));
return downloadTask;
}
}

private void updateContext(String selectedProjectName, String selectedWorkspaceName) {

try {
IdeGuiStateManager.getInstance().switchContext(selectedProjectName, selectedWorkspaceName);
guiStateManager.switchContext(selectedProjectName, selectedWorkspaceName);
} catch (FileNotFoundException e) {
IdeDialog errorDialog = new IdeDialog(IdeDialog.AlertType.ERROR, e.getMessage());
IdeDialog errorDialog = new IdeDialog(AlertType.ERROR, e.getMessage());
errorDialog.showAndWait();
}
}

private void updateStatusLabel(List<ProgressBarTask> taskList) {

Platform.runLater(() -> {

if (taskList.size() > 1) {
statusLabel.setOnMouseClicked(e -> TaskOverviewWindow.getInstance(taskManager).showRelativeToReferenceNode(statusLabel));

statusProgressBar.setVisible(false);
statusProgressBar.setPrefWidth(0);
statusLabel.setText(taskList.size() + " tasks running...");

statusLabel.setUnderline(true);
statusLabel.setStyle(
"-fx-text-fill: blue;"
+ "-fx-cursor: hand"
);
} else if (taskList.size() == 1) {
statusLabel.setOnMouseClicked(null);

ProgressBarTask task = taskList.getFirst();
statusLabel.setText(String.format(
ProgressBarTask.TASK_DESCRIPTION_STRING_FORMAT,
task.getTitle(),
task.getCurrentProgress(),
task.getMaxSize(),
task.getUnitName())
);
statusLabel.setUnderline(false);
statusLabel.setStyle("");

statusProgressBar.setVisible(true);
statusProgressBar.setPrefWidth(PROGRESSBAR_VISIBLE_WIDTH);
statusProgressBar.setProgress((double) (task.getCurrentProgress()) / task.getMaxSize());
} else {
statusLabel.setOnMouseClicked(null);
statusLabel.setText("IDEasy is ready.");
statusProgressBar.setVisible(false);
statusProgressBar.setPrefWidth(0);

statusLabel.setUnderline(false);
statusLabel.setStyle("");
}
});
}
}
Loading
Loading