Skip to content

Commit cdaf48d

Browse files
authored
Merge pull request #3 from RedlanceMinecraft/progressbars
In-dock progressbars
2 parents abd03d3 + bf02f5f commit cdaf48d

9 files changed

Lines changed: 453 additions & 5 deletions

File tree

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 Redlance
3+
Copyright (c) 2026 Redlance
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.MD

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
* **[File Origin Metadata (Windows & macOS)](#3-file-origin--referrer):** Retrieve the source URL of downloaded files.
1111
* **macOS:** Reads `kMDItemWhereFroms` metadata.
1212
* **Windows:** Reads **Alternate Data Streams (ADS)**, specifically `Zone.Identifier`.
13+
* **[Taskbar Progress Bars](#4-taskbar-progress-bars):** Display progress indicators on the taskbar/dock icon.
14+
* Supports multiple stacked progress bars (up to 8).
15+
* *Note: Windows taskbar support is planned for future releases.*
1316
* **Java 8 Compatible:** Built on Java 17 but distributed with a Java 8 compatible version (Multi-Release JAR).
1417

1518
## 📦 Installation
@@ -114,7 +117,7 @@ public class ReferrerExample {
114117
public void printOrigin(File file) {
115118
try {
116119
Set<String> referrers = PlatformFileReferer.INSTANCE.getFileReferer(file);
117-
120+
118121
if (!referrers.isEmpty()) {
119122
System.out.println("File downloaded from:");
120123
referrers.forEach(System.out::println);
@@ -128,6 +131,46 @@ public class ReferrerExample {
128131
}
129132
```
130133

134+
### 4. Taskbar Progress Bars
135+
Display progress indicators on the taskbar/dock icon. Supports multiple stacked progress bars.
136+
137+
> **Note:** Windows taskbar support is currently in development. On Windows, these methods will currently perform no action.
138+
139+
```java
140+
import org.redlance.platformtools.PlatformProgressBars;
141+
import org.redlance.platformtools.PlatformProgressBars.PlatformProgressBar;
142+
143+
public class ProgressExample {
144+
public void downloadFile() {
145+
// Create a progress bar
146+
try (PlatformProgressBar progress = PlatformProgressBars.INSTANCE.create()) {
147+
progress.setMaxValue(100);
148+
149+
for (int i = 0; i <= 100; i++) {
150+
progress.setValue(i);
151+
Thread.sleep(50);
152+
}
153+
} // Automatically closes and removes from dock
154+
}
155+
156+
public void multipleProgressBars() {
157+
// Create multiple progress bars (up to 8)
158+
PlatformProgressBar bar1 = PlatformProgressBars.INSTANCE.create();
159+
PlatformProgressBar bar2 = PlatformProgressBars.INSTANCE.create();
160+
161+
bar1.setMaxValue(100);
162+
bar2.setMaxValue(50);
163+
164+
bar1.setValue(75);
165+
bar2.setValue(25);
166+
167+
// Clean up when done
168+
bar1.close();
169+
bar2.close();
170+
}
171+
}
172+
```
173+
131174
## 🛠 System Requirements
132175
* **Java:** 8 or higher.
133176
* **Operating Systems:**

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66
}
77

88
base.archivesName = "PlatformTools"
9-
version = "3.1.5"
9+
version = "3.2.0"
1010
group = "org.redlance"
1111

1212
repositories {
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package org.redlance.platformtools;
2+
3+
import org.redlance.platformtools.impl.PlatformProgressBarImpl;
4+
5+
import java.io.Closeable;
6+
7+
/**
8+
* Factory interface for creating platform-specific progress bar indicators.
9+
* <p>
10+
* This interface provides a platform-agnostic way to display progress bars
11+
* in native UI elements (such as the macOS dock icon).
12+
* <p>
13+
* Usage example:
14+
* <pre>{@code
15+
* PlatformProgressBars.PlatformProgressBar progressBar = PlatformProgressBars.INSTANCE.create();
16+
* progressBar.setMaxValue(100);
17+
* progressBar.display();
18+
* progressBar.setValue(50);
19+
* // ... when done
20+
* progressBar.close();
21+
* }</pre>
22+
*
23+
* @see PlatformProgressBar
24+
* @see BasePlatformFeature#isAvailable()
25+
*/
26+
@SuppressWarnings("unused") // API
27+
public interface PlatformProgressBars extends BasePlatformFeature {
28+
/**
29+
* Singleton instance of the platform-specific progress bar factory.
30+
*/
31+
PlatformProgressBars INSTANCE = new PlatformProgressBarImpl();
32+
33+
/**
34+
* Creates a new platform-specific progress bar instance.
35+
* <p>
36+
* The returned progress bar is not visible until {@link PlatformProgressBar#display()} is called.
37+
*
38+
* @return a new {@link PlatformProgressBar} instance
39+
* @throws TooManyProgressBarsException if the maximum number of progress bars has been reached
40+
*/
41+
PlatformProgressBar create() throws TooManyProgressBarsException;
42+
43+
/**
44+
* Represents a platform-specific progress bar that can be displayed
45+
* in native UI elements.
46+
*/
47+
interface PlatformProgressBar extends Closeable {
48+
/**
49+
* Displays the progress bar in the native UI element.
50+
* <p>
51+
* This method should be called after configuring the progress bar
52+
* with {@link #setMaxValue(double)} and optionally {@link #setValue(double)}.
53+
*/
54+
void display();
55+
56+
/**
57+
* Removes the progress bar from the native UI element.
58+
* <p>
59+
* After calling this method, the progress bar will no longer be visible.
60+
*/
61+
@Override
62+
void close();
63+
64+
/**
65+
* Increments the current progress value by the specified amount.
66+
*
67+
* @param progress the amount to add to the current value
68+
*/
69+
void incrementBy(double progress);
70+
71+
/**
72+
* Sets the maximum value of the progress bar.
73+
*
74+
* @param maxValue the maximum progress value
75+
*/
76+
void setMaxValue(double maxValue);
77+
78+
/**
79+
* Sets the current progress value.
80+
*
81+
* @param value the current progress value (should be between 0 and maxValue)
82+
*/
83+
void setValue(double value);
84+
85+
/**
86+
* Sets whether the progress bar is in indeterminate mode.
87+
* <p>
88+
* In indeterminate mode, the progress bar shows an animated state
89+
* indicating that work is in progress but the amount is unknown.
90+
*
91+
* @param indeterminate true for indeterminate mode, false for determinate
92+
*/
93+
void setIndeterminate(boolean indeterminate);
94+
}
95+
96+
/**
97+
* Thrown when attempting to create more progress bars than the platform supports.
98+
*/
99+
class TooManyProgressBarsException extends RuntimeException {
100+
private final int maxAllowed;
101+
102+
public TooManyProgressBarsException(int maxAllowed) {
103+
super("Too many progress bars. Maximum allowed: " + maxAllowed);
104+
this.maxAllowed = maxAllowed;
105+
}
106+
107+
/**
108+
* Returns the maximum number of progress bars allowed on this platform.
109+
*
110+
* @return maximum allowed progress bars
111+
*/
112+
public int getMaxAllowed() {
113+
return this.maxAllowed;
114+
}
115+
}
116+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.redlance.platformtools.impl;
2+
3+
import com.sun.jna.Platform;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.redlance.platformtools.PlatformProgressBars;
6+
import org.redlance.platformtools.impl.macos.MacProgressBar;
7+
import org.redlance.platformtools.impl.unsupported.UnsupportedPlatform;
8+
9+
public class PlatformProgressBarImpl implements PlatformProgressBars {
10+
private final @NotNull PlatformProgressBars nativeProgressBar = switch (Platform.getOSType()) {
11+
case Platform.MAC -> new MacProgressBar();
12+
default -> UnsupportedPlatform.INSTANCE;
13+
};
14+
15+
@Override
16+
public PlatformProgressBar create() {
17+
return this.nativeProgressBar.create();
18+
}
19+
20+
@Override
21+
public boolean isAvailable() {
22+
return this.nativeProgressBar.isAvailable();
23+
}
24+
}

src/main/java/org/redlance/platformtools/impl/TestingApp.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.redlance.platformtools.PlatformAccent;
44
import org.redlance.platformtools.PlatformFileReferer;
55
import org.redlance.platformtools.PlatformFinderFavorites;
6+
import org.redlance.platformtools.PlatformProgressBars;
67

78
import javax.swing.*;
89
import java.awt.*;
@@ -17,7 +18,7 @@ public class TestingApp extends JFrame {
1718

1819
public TestingApp(Color initialColor) {
1920
setTitle("Color Window");
20-
setSize(400, 300);
21+
setSize(640, 480);
2122
setDefaultCloseOperation(EXIT_ON_CLOSE);
2223
setLocationRelativeTo(null);
2324

@@ -35,6 +36,10 @@ public void mouseMoved(MouseEvent e) {
3536
JPanel controlsPanel = new JPanel();
3637
add(controlsPanel, BorderLayout.SOUTH);
3738

39+
JButton progressBarButton = new JButton("Progress Bar");
40+
progressBarButton.addActionListener(this::onProgressBar);
41+
controlsPanel.add(progressBarButton);
42+
3843
JButton pinFolderButton = new JButton("Pin folder");
3944
pinFolderButton.addActionListener(this::onPinFolder);
4045
controlsPanel.add(pinFolderButton);
@@ -168,4 +173,54 @@ private void onRecreate(ActionEvent e) {
168173
setVisible(true);
169174
PlatformAccent.INSTANCE.resubscribe();
170175
}
176+
177+
private void onProgressBar(ActionEvent e) {
178+
showProgressDialog();
179+
}
180+
181+
private void showProgressDialog() {
182+
JDialog dialog = new JDialog(this, "Progress Bars Manager", false);
183+
dialog.setSize(400, 300);
184+
dialog.setLocationRelativeTo(this);
185+
dialog.setLayout(new BorderLayout());
186+
187+
JPanel barsPanel = new JPanel();
188+
barsPanel.setLayout(new BoxLayout(barsPanel, BoxLayout.Y_AXIS));
189+
JScrollPane scrollPane = new JScrollPane(barsPanel);
190+
dialog.add(scrollPane, BorderLayout.CENTER);
191+
192+
JButton addButton = new JButton("Add Progress Bar");
193+
addButton.addActionListener(event -> {
194+
PlatformProgressBars.PlatformProgressBar bar = PlatformProgressBars.INSTANCE.create();
195+
bar.setMaxValue(100);
196+
197+
JPanel barPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
198+
barPanel.setBorder(BorderFactory.createEtchedBorder());
199+
200+
JLabel label = new JLabel("Bar " + (barsPanel.getComponentCount() + 1) + ":");
201+
202+
JSlider slider = new JSlider(0, 100, 0);
203+
slider.setPreferredSize(new Dimension(200, 30));
204+
slider.addChangeListener(ev -> bar.setValue(slider.getValue()));
205+
206+
JButton removeButton = new JButton("X");
207+
removeButton.addActionListener(ev -> {
208+
bar.close();
209+
barsPanel.remove(barPanel);
210+
barsPanel.revalidate();
211+
barsPanel.repaint();
212+
});
213+
214+
barPanel.add(label);
215+
barPanel.add(slider);
216+
barPanel.add(removeButton);
217+
218+
barsPanel.add(barPanel);
219+
barsPanel.revalidate();
220+
barsPanel.repaint();
221+
});
222+
223+
dialog.add(addButton, BorderLayout.SOUTH);
224+
dialog.setVisible(true);
225+
}
171226
}

0 commit comments

Comments
 (0)