jsugar

README.md:

- Added a new limitation: Mentioning that certain components don't offer the ability to attach an action to them. Window.java: - Updated removeComponent() so that it updates the window after removal. anotherTest.java: - A little bit of extra testing =3

Author
Vngngdn
Date
July 17, 2016, 10:22 a.m.
Hash
807d67f05369fda64ade9860e2e51368e71a6f7b
Parent
2f8e61037413b00baea109ea8197b58d3758a9ff
Modified files
README.md
Window.java
anotherTest.java

README.md

3 additions and 0 deletions.

View changes Hide changes
1
1
======
2
2
3
3
JSugar is a tiny, stupid framework, in an attempt to hide away the tons of
4
4
useless cruft that you get from working with Java's Swing.
5
5
6
6
Features
7
7
--------
8
8
9
9
Purely speaking, JSugar does not add anything new compared to when you're using
10
10
Swing. It does however, offer some considerable advantages over using Swing
11
11
directly:
12
12
13
13
* Easy creation of windows, that offer a series of convenient methods like
14
14
  createButton(), allowing for easy creation of small GUI programs.
15
15
* Built-in support for action triggering; Just say which method should be
16
16
  triggered where, and you're done.
17
17
* Easy learning curve, compared to manually handling Swing. Create a new Window,
18
18
  slap some components on it, add the methods it needs to call on trigger, and
19
19
  done.
20
20
* Relies mainly on primitive types, like integer arrays, and classes that are
21
21
  available in every recent OpenJDK version, like Strings.
22
22
* Completely [free software](https://www.gnu.org/philosophy/free-sw.html).
23
23
* Very lightweight. It's just a bunch of source files that you can directly link
24
24
  to. Putting it in a seperate jar will cause more harm than good.
25
25
* Only RuntimeExceptions will be thrown, avoiding *exception infection* that
26
26
  you'll get from using self-defined exceptions in Java.
27
27
* Documentation available through JavaDoc. I do my very best to provide clear
28
28
  documentation, so you know how to use this without having to figure it out
29
29
  yourself.
30
30
* Components you add are returned to the caller, so if you do need some more
31
31
  advanced stuff, you can add it yourself.
32
32
33
33
Limitations
34
34
-----------
35
35
36
36
The convenience causes some limitations, but they're fairly minor, and if you're
37
37
using JSugar, it's very unlikely you'd be bothered by them anyway, but here they
38
38
are:
39
39
40
40
* You can't add your own panels to the window, but you'll most likely just want
41
41
  to add some components to the window itself.
42
42
* The panels default to double buffering (which you should do anyway) and the
43
43
  flow layout.
44
44
* The window is automatically updated whenever a new component is added. When
45
45
  using Swing 'natively', you could postpone updating, but why did you add a
46
46
  component then in the first place?
47
47
* *Trigger methods* can only have 1 parameter, or none at all. That 1 parameter
48
48
  must be of type java.awt.event.ActionEvent. This should be enough for >80% of
49
49
  use cases, and if you really need more flexibility, you can add your own
50
50
  action handlers manually.
51
51
* Pressing the X in the title bar of the window closes it. >95% of use cases do
52
52
  this anyway.
53
53
* Some silly stuff like adding icons to buttons is not possible, you'll have to
54
54
  do that yourself. Yet for most use cases, you might just do the sane thing and
55
55
  add text.
56
56
+
57
  We're talking about components like labels. But then again, these kind of
+
58
  components shouldn't get much triggers anyway.
+
59

Window.java

1 addition and 0 deletions.

View changes Hide changes
1
1
 * Window.java - Module to create a new window with JSugar.
2
2
 * Copyright © 2016 Maarten "Vngngdn" Vangeneugden
3
3
 * 
4
4
 * This program is free software: you can redistribute it and/or modify
5
5
 * it under the terms of the GNU General Public License as published by
6
6
 * the Free Software Foundation, either version 3 of the License, or
7
7
 * (at your option) any later version.
8
8
 * 
9
9
 * This program is distributed in the hope that it will be useful,
10
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
12
 * GNU General Public License for more details.
13
13
 * 
14
14
 * You should have received a copy of the GNU General Public License
15
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
16
 */
17
17
18
18
/**
19
19
 * Window class for the program.
20
20
 *
21
21
 * Window contains the necessary data and methods to present the user with what
22
22
 * he's familiar with as being a "window". To make it functional, the developer
23
23
 * can make use of a series of methods to add components to said window, remove
24
24
 * components, and so on.
25
25
 * Currently, Window also contains methods to show dialogs. This will be cleaned
26
26
 * in the near future.
27
27
 * @author Maarten Vangeneugden
28
28
 */
29
29
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
30
30
import java.util.NoSuchElementException;
31
31
import java.lang.reflect.Method;
32
32
33
33
class Window {
34
34
	private JPanel panel; // The panel that contains all the components.
35
35
	private JFrame frame; // The "window" being presented to the user.
36
36
37
37
	/**
38
38
	 * Constructor of Window.
39
39
	 * By creating a new Window instance, this constructor will automatically
40
40
	 * start the initialization of the GUI. After doing so, the caller can
41
41
	 * start adding components to the window as pleased.
42
42
	 * @param title The title to be shown in the window's title bar.
43
43
	 */
44
44
	public Window() {
45
45
		this.panel = new JPanel();
46
46
		// TODO: The current title is "Hello world!" but that will become caller
47
47
		// defined soon.
48
48
		JFrame frame = new JFrame("Hello world!");
49
49
		// Makes it so that if the user clicks the X in the titlebar, the window
50
50
		// closes:
51
51
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
52
52
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
53
53
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
54
54
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
55
55
		frame.pack(); 	
56
56
		frame.setVisible(true); // Makes the window visible to the user.
57
57
		this.frame = frame;
58
58
	}
59
59
60
60
	/**
61
61
	 * Resizes the window to fit all components.
62
62
	 * By calling this method, the window will evaluate the currently visible
63
63
	 * components, and resize itself so that all components become properly
64
64
	 * visible.
65
65
	 */
66
66
	private void updateWindow() {
67
67
		this.frame.pack();
68
68
	}
69
69
70
70
	/**
71
71
	 * A series of tests for method and class handling.
72
72
	 * When a caller presents certain methods with data concerning reflection,
73
73
	 * the Java classes you need to use for that are quite opaque, and don't
74
74
	 * offer much safety in any way.
75
75
	 * The solution therefore, is run some validation checks, but these take up
76
76
	 * a decent amount of space in terms of LoC.
77
77
	 * This method takes care of all that. Call this function whenever data
78
78
	 * needs to be validated.
79
79
	 * @param methodName The name of the method, as it is declared in object.
80
80
	 * @param object The class instance in where this method will be called.
81
81
	 * @return The method that could be derived from the supplied data, or null
82
82
	 * if that wasn't possible.
83
83
	 * @throws NullPointerException if either methodName or object are null
84
84
	 * pointers.
85
85
	 * @throws IllegalArgumentException if methodName is empty, or the method
86
86
	 * does not appear to be declared in the given object, or object is not a
87
87
	 * class.
88
88
	 */
89
89
	// All unchecked typecasts are safe, and the use of raw types is taken care
90
90
	// of.
91
91
	@SuppressWarnings({"unchecked","rawtypes"})
92
92
	private Method handleReflectionData(String methodName, Object object) {
93
93
		// Null pointer checking:
94
94
		if (methodName == null || object == null) {
95
95
			throw new NullPointerException("One or more of the given parameters are null pointers.");
96
96
		}
97
97
98
98
		// XXX: Some might say the next line should be in an else{} block. But
99
99
		// Scoping rules require that I'd then have to wrap the rest of the
100
100
		// method in the same else to use it.
101
101
		Class methodClass = object.getClass(); 
102
102
		if (methodName.equals("")) {
103
103
			throw new IllegalArgumentException("The given methodName was empty.");
104
104
		}
105
105
		Method method;
106
106
		try { // First: Look if there's a method without parameters.
107
107
			method = methodClass.getMethod(methodName, null);
108
108
		}
109
109
		catch (NoSuchMethodException exception) {
110
110
			try {
111
111
				// It's possible that the method requires an event parameter, so
112
112
				// check for that as well:
113
113
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
114
114
				method = methodClass.getMethod(methodName, parameters);
115
115
			}
116
116
			catch (NoSuchMethodException e) {
117
117
				throw new IllegalArgumentException("The given method does not appear in the given class. Be aware that the given method mustn't have any parameters, or only 1 parameter, which has to be of type java.awt.event.ActionEvent.");
118
118
			}
119
119
		}
120
120
		// At this stage, the given data has been validated, and we've been able
121
121
		// to retrieve the method itself.
122
122
		return method;
123
123
	}
124
124
125
125
	/**
126
126
	 * Creates a button in the GUI for interaction.
127
127
	 * This function offers a convenient way to create a button, that can be
128
128
	 * directly interacted with by the user. After creation, the button itself
129
129
	 * is returned to the caller, if he wishes to do something else with it.
130
130
	 * @param text The text that will be displayed in the button.
131
131
	 * @param action The action that will be returned to the action listener.
132
132
	 * @param methodName The name of the method that will be called when an
133
133
	 * action is triggered.
134
134
	 * @param objectInstance The object instance that contains the given method.
135
135
	 * This may only be a null pointer if triggerMethod is not an instance
136
136
	 * method.
137
137
	 * performed. This method may accept an ActionEvent parameter as its only
138
138
	 * parameter, or no parameters at all.
139
139
	 * @throws NullPointerException if triggerMethod is a null pointer, or
140
140
	 * the empty String was given.
141
141
	 * @throws IllegalArgumentException if triggerMethod has more than 1
142
142
	 * parameter, or the 1 required parameter is not of type ActionEvent.
143
143
	 * @return The button that was created.
144
144
	 * @see java.awt.event.ActionEvent
145
145
	 * @see java.lang.reflect.Method.invoke()
146
146
	 */
147
147
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
148
148
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
149
149
150
150
		// For starters, we first assert that the given parameters are valid:
151
151
		if (text == null) {
152
152
			text = "";
153
153
		}
154
154
		if (action == null) {
155
155
			action = "";
156
156
		}
157
157
		
158
158
		// When the method gets here, everything's been validated correctly.
159
159
		JButton button = new JButton(text);
160
160
		button.setActionCommand(action);
161
161
		button.addActionListener(
162
162
				new java.awt.event.ActionListener() {
163
163
					public void actionPerformed(java.awt.event.ActionEvent event) {
164
164
						try {
165
165
							triggerMethod.setAccessible(true);
166
166
							if (triggerMethod.getParameterTypes().length == 0) {
167
167
								// FIXME: Next line throws a warning?
168
168
								triggerMethod.invoke(triggerObject, null);
169
169
							}
170
170
							else {
171
171
								triggerMethod.invoke(triggerObject, new Object[]{event});
172
172
							}
173
173
						}
174
174
						catch (Exception useless) {
175
175
							/*
176
176
							 * XXX: Some info on why I don't just throw said
177
177
							 * Exception to the caller:
178
178
							 * Java has this awful language constraint, which
179
179
							 * forces every damn exception that isn't a subclass
180
180
							 * of RuntimeException, to be declared in the method
181
181
							 * declaration. This tends to infect all underlying
182
182
							 * methods as well, and all that for reasons I can't
183
183
							 * comprehend. In order to keep JSugar a simple and
184
184
							 * clean library, I'll rather just handle it here,
185
185
							 * and throw a RuntimeException with appropriate
186
186
							 * details.
187
187
							 */
188
188
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
189
189
						}
190
190
					}
191
191
				});
192
192
		this.panel.add(button);
193
193
		this.frame.pack();
194
194
		return button;
195
195
	}
196
196
197
197
	/**
198
198
	 * Ask the user for input through a dialog box.
199
199
	 * This method presents the user with an input field, that can accept
200
200
	 * textual input. The method will return the given input after the user's
201
201
	 * clicked a button to send.
202
202
	 * @param text The text/question to be asked to the user.
203
203
	 * @return A String, equal to what the user entered.
204
204
	 * @throws NullPointerException if text is a null pointer.
205
205
	 */
206
206
	public String inputDialog(String text) {
207
207
		if (text == null) {
208
208
			throw new NullPointerException("The given text/question was a null pointer.");
209
209
		}
210
210
		return JOptionPane.showInputDialog(text);
211
211
	}
212
212
213
213
	/**
214
214
	 * Give the user a dialog box.
215
215
	 * This method can be used to provide a simple dialog to the user.
216
216
	 * This will show the user the given question, after which a boolean value
217
217
	 * is returned, holding the choice.
218
218
	 * @param text The text/question to be asked to the user.
219
219
	 * @return True if the user confirms, False if he denies.
220
220
	 * @throws NullPointerException if text is a null pointer.
221
221
	 */
222
222
	public boolean confirmDialog(String text) {
223
223
		if (text == null) {
224
224
			throw new NullPointerException("The given text/question was a null pointer.");
225
225
		}
226
226
		final int ACCEPTED = 0;
227
227
		final int DENIED = 1;
228
228
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
229
229
		if (result == ACCEPTED) {
230
230
			return true;
231
231
		}
232
232
		else {
233
233
			return false;
234
234
		}
235
235
	}
236
236
237
237
	/**
238
238
	 * Give the user a choice dialog box.
239
239
	 * This method gives the user a simple dialog with predefined choices.
240
240
	 * These choices are to be provided by the caller in a simple array.
241
241
	 * Tip: This method works extremely well with arbitrary created choices.
242
242
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
243
243
	 * then that value is immediately returned.
244
244
	 * @param text The text/question to be asked to the user.
245
245
	 * @param choices An array of Strings, containing the choices the user can
246
246
	 * pick.
247
247
	 * @return The index value of the picked choice, or -1 if no choices were
248
248
	 * given.
249
249
	 * @throws NullPointerException if text is a null pointer.
250
250
	 */
251
251
	public int choiceDialog(String text, String[] choices) {
252
252
		if (text == null) {
253
253
			throw new NullPointerException("The given text/question was a null pointer.");
254
254
		}
255
255
		// First: handling the trivial cases:
256
256
		if (choices.length == 0) {
257
257
			return -1;
258
258
		}
259
259
		else if (choices.length == 1) {
260
260
			return 0;
261
261
		}
262
262
		int answer = JOptionPane.CLOSED_OPTION;
263
263
		// The dialog needs to be shown again until the user has made a possible
264
264
		// choice, i.e. Chickening out using the close button is not possible
265
265
		// (Because that returns CLOSED_OPTION).
266
266
		while (answer == JOptionPane.CLOSED_OPTION) {
267
267
				JOptionPane.showOptionDialog(
268
268
					null, // The parent component. May become the panel?
269
269
					text, // The text/question to describe the goal
270
270
					"Dialog", // The text in the title bar
271
271
					JOptionPane.DEFAULT_OPTION, // The kind of available options
272
272
					JOptionPane.QUESTION_MESSAGE, // The type of message
273
273
					null, // The icon to show
274
274
					choices, // The possible choices
275
275
					choices[0] // The standard choice
276
276
					);
277
277
		}
278
278
		return answer;
279
279
	}
280
280
		
281
281
282
282
	/**
283
283
	 * Creates a label in the GUI for interaction.
284
284
	 * This function offers a convenient way to create a label, that can be
285
285
	 * directly interacted with by the user. After creation, the label itself
286
286
	 * is returned to the caller, if he wishes to do something else with it.
287
287
	 * @param text The text that will be displayed in the label.
288
288
	 * @return The label that was created.
289
289
	 */
290
290
	public JLabel createLabel(String text) {
291
291
		JLabel label = new JLabel(text);
292
292
		this.panel.add(label);
293
293
		return label;
294
294
	}
295
295
296
296
	/**
297
297
	 * Removes the given component from the GUI.
298
298
	 * This method allows its caller to remove a component from the GUI.
299
299
	 * @param component The component to be removed.
300
300
	 * @throws NoSuchElementException if the given component does not exist in
301
301
	 * the GUI.
302
302
	 * @throws NullPointerException if the given component is a null pointer.
303
303
	 */
304
304
	public void removeComponent(JComponent component) {
305
305
		int originalSize = this.panel.getComponentCount();
306
306
		this.panel.remove(component);
307
307
		int newSize = this.panel.getComponentCount();
308
308
		if (originalSize != newSize+1) {
309
309
			throw new NoSuchElementException("The given component does not exist in the GUI.");
310
310
		}
311
311
	}
+
312
	}
312
313
}
313
314

anotherTest.java

6 additions and 0 deletions.

View changes Hide changes
1
1
import java.lang.reflect.Method;
2
2
public class anotherTest {
3
3
	public anotherTest() {
4
4
		Window window = new Window();
5
5
		JButton button = new JButton(); // Yeah, have to initialize that, because Java.
6
6
		button = window.createButton("Knopje!!", "???", "printSomething",  this);
7
7
		try {
8
8
			Thread.sleep(1000);
9
9
		} catch (InterruptedException e){
10
10
			e.printStackTrace();
11
11
		}
12
12
		button.setText("LOL");
13
13
		System.out.println("Done.");
14
14
15
15
		System.out.println("Okay, that worked...");
16
16
	}
+
17
			Thread.sleep(1000);
+
18
		} catch (InterruptedException e){
+
19
			e.printStackTrace();
+
20
		}
+
21
		window.removeComponent(button);
+
22
	}
17
23
18
24
	public void printSomething() {
19
25
		System.out.println("Okay, I can print stuff.");
20
26
	}
21
27
}
22
28