Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 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
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
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
98a7072
Merge branch 'main' into #1802-state-management-implementation
laim2003 May 5, 2026
c55a3a3
Merge branch 'main' into #1802-state-management-implementation
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
07b7773
#1936: Implement i18n locale-switching
KarimALotfy May 27, 2026
5943b23
#1936: Add to CHANGELOG.adoc
KarimALotfy May 27, 2026
01d696b
Merge "PR-1807" to "feature/1936-improve-localization-of-gui"
KarimALotfy May 28, 2026
df6f21b
Merge main into "feature/1936-improve-localization-of-gui"
KarimALotfy May 28, 2026
519a8bb
#1928: Merge "main" into "feature/1936-improve-localization-of-gui"
KarimALotfy Jun 16, 2026
aaa1763
#1928: Remove duplicated Imports
KarimALotfy Jun 16, 2026
3441ed4
#1928: Align structure of ide-projects with main
KarimALotfy Jun 16, 2026
a63acb0
#1936: Refactor I18n wording to Localization
KarimALotfy Jun 17, 2026
d8b8377
Merge branch 'main' of https://github.com/devonfw/IDEasy into feature…
KarimALotfy Jun 19, 2026
625d407
#2045: Add tool configuration for ide.properties through the GUI
KarimALotfy Jun 22, 2026
9232b7b
#2045:add Tools Configuration dialog to manage tool versions and edit…
KarimALotfy Jun 26, 2026
9f5c58b
#2045: Changed the Tool Settings Editor from a dialog to a tab in Mai…
KarimALotfy Jul 1, 2026
8199836
#2097:Add plugin management to tool settings UI
KarimALotfy Jul 2, 2026
5d5b301
Merge remote-tracking branch 'origin/main' into feature/2045-add-tool…
maybeec Jul 4, 2026
e79536f
Merge remote-tracking branch 'upstream/main' into feature/2045-add-to…
maybeec Jul 4, 2026
2b7554d
#2097:Add an option to add new Plugins to the selected tool in the GU…
KarimALotfy Jul 6, 2026
15c20cb
Merge "main" into "feature/2045-add-tool-selection-and-version-or-edi…
KarimALotfy Jul 7, 2026
828d20c
Merge "main" into "feature/2045-add-tool-selection-and-version-or-edi…
KarimALotfy Jul 7, 2026
b2e10b7
#2045: Align latest changes from Localization Feature merged to main
KarimALotfy Jul 7, 2026
f703e8e
#2045: Add Interaction Tests to AppBaseTest, small change to ToolSett…
KarimALotfy Jul 7, 2026
e029f44
#2045: Fix
KarimALotfy Jul 10, 2026
eca23a5
#2045: Merge main into feature branch
KarimALotfy Jul 10, 2026
8b34cbc
#2045: Fix tool version pattern validation and Tool Config UI issues
KarimALotfy Jul 10, 2026
0cf29a0
#2097: Merge "feature/2045-add-tool-slection-and-version-or-edition-c…
KarimALotfy Jul 12, 2026
d26fae6
#2097: record merge of feature/2045-add-tool-slection-and-version-or-…
KarimALotfy Jul 12, 2026
9d5982c
#2097: Fix tests
KarimALotfy Jul 12, 2026
30a03ed
#2097: add to CHANGELOG.adoc
KarimALotfy Jul 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
10 changes: 6 additions & 4 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE

Release with new features and bugfixes:


The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/47?closed=1[milestone 2026.07.002].

== 2026.07.001
Expand All @@ -23,9 +22,14 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/822[#822] : Added Cwd path limit in shell
* https://github.com/devonfw/IDEasy/issues/1689[#1689]: Fix misleading message in `set-version` when the version is already installed
* https://github.com/devonfw/IDEasy/issues/1517[#1517]: Fix IDEasy MSI does not configure Windows Terminal for git-bash
* https://github.com/devonfw/IDEasy/issues/1227[#1227]: Added Setup instrcutions to the Windows installer
* https://github.com/devonfw/IDEasy/issues/1227[#1227]: Added Setup instructions to the Windows installer
* https://github.com/devonfw/IDEasy/issues/2114[#2114]: Fix false "Cygwin is not supported" warning in Git Bash
* https://github.com/devonfw/IDEasy/issues/865[#865]: az not working on Mac
* https://github.com/devonfw/IDEasy/issues/1936[#1936]: Improve localization of GUI
* https://github.com/devonfw/IDEasy/issues/2045[#2045]: Add tool selection and version or edition configuration to GUI
* https://github.com/devonfw/IDEasy/issues/2097[#2097]: Configure Ide plugins in the GUI


* https://github.com/devonfw/IDEasy/issues/2026[#2026]: Create UvRepository and UvBasedCommandlet

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/46?closed=1[milestone 2026.07.001].
Expand Down Expand Up @@ -100,8 +104,6 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/1904[#1904]: Add Inso CLI to IDEasy commandlets
* https://github.com/devonfw/IDEasy/issues/1952[#1952]: Ability for platform specific dependencies
* https://github.com/devonfw/IDEasy/issues/1950[#1950]: Fix exit autocompletion
* https://github.com/devonfw/IDEasy/issues/1936[#1936]: Improve localization of GUI

* https://github.com/devonfw/IDEasy/issues/1958[#1958]: Fix git pull on settings with local branch without remote

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/44?closed=1[milestone 2026.05.001].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ protected void configureToolArgs(ProcessContext pc, ProcessMode processMode, Lis
}

@Override
protected boolean isPluginUrlNeeded() {
public boolean isPluginUrlNeeded() {

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.slf4j.Logger;
Expand Down Expand Up @@ -87,15 +88,45 @@ private void loadPluginsFromDirectory(ToolPlugins map, Path pluginsPath) {
/**
* @return {@code true} if {@link ToolPluginDescriptor#url() plugin URL} property is needed, {@code false} otherwise.
*/
protected boolean isPluginUrlNeeded() {
public boolean isPluginUrlNeeded() {

return false;
}

/**
* Creates a new plugin properties file in the project settings and returns the resulting {@link ToolPluginDescriptor}. The plugin is created as active by
* default.
*
* @param name the plugin name (used as the properties file name).
* @param id the plugin id (e.g. marketplace or extension identifier).
* @param url the plugin URL (update site / marketplace URL); may be {@code null} when not needed.
* @return the created {@link ToolPluginDescriptor}.
*/
public ToolPluginDescriptor createPlugin(String name, String id, String url, String tags) {

Path pluginsDir = getPluginsConfigPath();
this.context.getFileAccess().mkdirs(pluginsDir);
Path pluginFile = pluginsDir.resolve(name + IdeContext.EXT_PROPERTIES);
Properties props = new Properties();
if (id != null && !id.isBlank()) {
props.setProperty("id", id);
}
props.setProperty("active", "true");
if (url != null && !url.isBlank()) {
props.setProperty("url", url);
}
if (tags != null && !tags.isBlank()) {
props.setProperty("tags", tags);
}
this.context.getFileAccess().writeProperties(props, pluginFile);
this.plugins = null;
return new ToolPluginDescriptor(id, name, url, null, true, Tag.parseCsv(tags));
}

/**
* @return the {@link Path} to the folder with the plugin configuration files inside the settings.
*/
protected Path getPluginsConfigPath() {
public Path getPluginsConfigPath() {

return this.context.getSettingsPath().resolve(this.tool).resolve(IdeContext.FOLDER_PLUGINS);
}
Expand Down Expand Up @@ -129,6 +160,7 @@ protected void postInstall(ToolInstallRequest request) {

/**
* Deletes all installed plugins for this {@link IdeToolCommandlet} by deleting the plugins installation folder and all plugin marker files.
*
* @param pluginsInstallationPath the {@link Path} to the plugins installation folder.
*/
private void deleteAllPlugins(Path pluginsInstallationPath) {
Expand Down Expand Up @@ -304,6 +336,21 @@ public ToolPluginDescriptor getPlugin(String key) {
return pluginDescriptor;
}

/**
* Persists the {@link ToolPluginDescriptor#active() active} flag for the given plugin to its properties file in the project settings.
*
* @param plugin the {@link ToolPluginDescriptor} whose active state to save.
* @param active {@code true} to activate, {@code false} to deactivate.
*/
public void savePluginActive(ToolPluginDescriptor plugin, boolean active) {

Path pluginFile = getPluginsConfigPath().resolve(plugin.name() + IdeContext.EXT_PROPERTIES);
Properties props = this.context.getFileAccess().readProperties(pluginFile);
String activeKey = props.containsKey("plugin_active") ? "plugin_active" : "active";
props.setProperty(activeKey, Boolean.toString(active));
this.context.getFileAccess().writeProperties(props, pluginFile);
}

/**
* @param plugin the in{@link ToolPluginDescriptor#active() active} {@link ToolPluginDescriptor} that is skipped for regular plugin installation.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ protected String getBinaryName() {
}

@Override
protected Path getPluginsConfigPath() {
public Path getPluginsConfigPath() {

if (EDITION_VSCODIUM.equals(getConfiguredEdition())) {
Path vscodiumPluginsPath = this.context.getSettingsPath().resolve(EDITION_VSCODIUM).resolve(IdeContext.FOLDER_PLUGINS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.nio.file.Path;

import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -62,17 +63,18 @@ void testGetPluginsMap() {
void testInstallPluginsWithForce() {

//arrange
context.getStartContext().setForcePlugins(true);
final ExamplePluginBasedCommandlet pluginBasedCommandlet = new ExamplePluginBasedCommandlet(context, TOOL, tags);
IdeTestContext localContext = newContext(PROJECT_BASIC, null, true);
localContext.getStartContext().setForcePlugins(true);
final ExamplePluginBasedCommandlet pluginBasedCommandlet = new ExamplePluginBasedCommandlet(localContext, TOOL, tags);

//act
pluginBasedCommandlet.installPlugins(
List.of(ToolPluginDescriptor.of(context.getSettingsPath().resolve(ANY_EDIT_PLUGIN_PATH), context, false)),
new ProcessContextTestImpl(context));
List.of(ToolPluginDescriptor.of(localContext.getSettingsPath().resolve(ANY_EDIT_PLUGIN_PATH), localContext, false)),
new ProcessContextTestImpl(localContext));

//assert - Check if we skip the markerfile-check because we force the plugins to install
assertThat(context).logAtSuccess().hasMessage("Successfully ended step 'Install plugin anyedit'.");
assertThat(context).log().hasNoMessageContaining("Skipping installation of plugin '{}' due to existing marker file: ");
assertThat(localContext).logAtSuccess().hasMessage("Successfully ended step 'Install plugin anyedit'.");
assertThat(localContext).log().hasNoMessageContaining("Skipping installation of plugin '{}' due to existing marker file: ");
}

@Test
Expand Down Expand Up @@ -101,6 +103,69 @@ void testMarkerFileContainsVersionSegment() {
assertThat(markerFilePath.getFileName().toString()).contains("plugin-name.version-1.2.3_build_4");
}

@Test
void testIsPluginUrlNeeded_returnsFalseByDefault() {

ExamplePluginBasedCommandlet commandlet = new ExamplePluginBasedCommandlet(context, TOOL, null);

assertThat(commandlet.isPluginUrlNeeded()).isFalse();
}

@Test
void testCreatePlugin_createsPropertiesFileWithActiveTrue() throws IOException {

IdeTestContext localContext = newContext(PROJECT_BASIC, null, false);
ExamplePluginBasedCommandlet commandlet = new ExamplePluginBasedCommandlet(localContext, TOOL, null);

ToolPluginDescriptor result = commandlet.createPlugin("create-basic-plugin", "com.example.basic", null, null);

assertThat(result.name()).isEqualTo("create-basic-plugin");
assertThat(result.id()).isEqualTo("com.example.basic");
assertThat(result.active()).isTrue();

Path pluginFile = commandlet.getPluginsConfigPath().resolve("create-basic-plugin.properties");
assertThat(pluginFile).exists();
Properties props = new Properties();
try (var in = Files.newInputStream(pluginFile)) {
props.load(in);
}
assertThat(props.getProperty("active")).isEqualTo("true");
assertThat(props.getProperty("id")).isEqualTo("com.example.basic");
assertThat(props.containsKey("url")).isFalse();
assertThat(props.containsKey("tags")).isFalse();
}

@Test
void testCreatePlugin_writesUrlAndTagsWhenProvided() throws IOException {

IdeTestContext localContext = newContext(PROJECT_BASIC, null, false);
ExamplePluginBasedCommandlet commandlet = new ExamplePluginBasedCommandlet(localContext, TOOL, null);

commandlet.createPlugin("create-full-plugin", "com.example.full", "https://example.com/update", "java,ide");

Path pluginFile = commandlet.getPluginsConfigPath().resolve("create-full-plugin.properties");
Properties props = new Properties();
try (var in = Files.newInputStream(pluginFile)) {
props.load(in);
}
assertThat(props.getProperty("url")).isEqualTo("https://example.com/update");
assertThat(props.getProperty("tags")).isEqualTo("java,ide");
}

@Test
void testCreatePlugin_clearsCacheSoNewPluginAppearsInGetPlugins() {

IdeTestContext localContext = newContext(PROJECT_BASIC, null, true);
ExamplePluginBasedCommandlet commandlet = new ExamplePluginBasedCommandlet(localContext, TOOL, null);

assertThat(commandlet.getPlugins().getByName("create-cache-plugin")).isNull();

commandlet.createPlugin("create-cache-plugin", "com.example.cache", null, null);

assertThat(commandlet.getPlugins().getByName("create-cache-plugin")).isNotNull();
assertThat(commandlet.getPlugins().getByName("create-cache-plugin").active()).isTrue();
}

@Test
void testCreatePluginMarkerFileDeletesOtherVersionMarkers() {

Expand Down
55 changes: 54 additions & 1 deletion gui/src/main/java/com/devonfw/ide/gui/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@
import java.util.Locale;
import java.util.Map;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.Tooltip;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -17,6 +22,7 @@
import com.devonfw.ide.gui.context.ProjectManager;
import com.devonfw.ide.gui.modal.IdeDialog;
import com.devonfw.ide.gui.nls.NlsService;
import com.devonfw.ide.gui.settings.ToolSettingsController;

/**
* Controller of the main screen of the dashboard GUI.
Expand Down Expand Up @@ -49,6 +55,15 @@ public class MainController {
@FXML
private Button vsCodeOpen;

@FXML
private TabPane tabPane;

@FXML
private Tab mainTab;

@FXML
private Tab toolConfigTab;

private final String directoryPath;

private final Map<String, Locale> languageMap;
Expand All @@ -73,6 +88,13 @@ public MainController(String directoryPath, NlsService nlsService) {
private void initialize() {
setProjectsComboBox();
initLanguageComboBox();
toolConfigTab.setDisable(true);
toolConfigTab.setTooltip(new Tooltip(nlsService.get("toolConfigDisabled")));
tabPane.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) -> {
if (newTab == toolConfigTab) {
loadToolConfigContent();
}
});
}

private void initLanguageComboBox() {
Expand Down Expand Up @@ -148,7 +170,6 @@ private void openVsCode() {
openIDE("vscode");
}


private void setProjectsComboBox() {

assert (directoryPath != null) : "directoryPath is null! Please check the setup of your environment variables (IDE_ROOT)";
Expand All @@ -163,6 +184,7 @@ private void setProjectsComboBox() {
setWorkspaceComboBox();

selectedWorkspace.setDisable(false);

});
}

Expand All @@ -185,9 +207,24 @@ private void setWorkspaceComboBox() {
eclipseOpen.setDisable(false);
intellijOpen.setDisable(false);
vsCodeOpen.setDisable(false);
toolConfigTab.setDisable(false);
// If tool config is already open, reload it to reflect the new context
if (toolConfigTab.isSelected()) {
loadToolConfigContent();
}
// Pre-warm ide-urls git repo in background so the first open is fast
Thread preWarm = new Thread(() -> {
try {
IdeGuiStateManager.getInstance().getCurrentContext().getUrls();
} catch (Exception ignored) {
}
});
preWarm.setDaemon(true);
preWarm.start();
});
}


private void openIDE(String inIde) {

IdeGuiStateManager
Expand All @@ -198,6 +235,22 @@ private void openIDE(String inIde) {
.run();
}

private void loadToolConfigContent() {

try {
ToolSettingsController controller = new ToolSettingsController(nlsService);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/devonfw/ide/gui/tools-config.fxml"));
loader.setController(controller);
loader.setResources(nlsService.getResourceBundle());
Parent content = loader.load();
controller.setOnClose(() -> tabPane.getSelectionModel().select(mainTab));
toolConfigTab.setContent(content);
} catch (Exception e) {
LOG.error("Failed to load tool config view", e);
new IdeDialog(IdeDialog.AlertType.ERROR, e.getMessage()).showAndWait();
}
}

private void updateContext(String selectedProjectName, String selectedWorkspaceName) {
try {
IdeGuiStateManager.getInstance().switchContext(selectedProjectName, selectedWorkspaceName);
Expand Down
Loading
Loading