jsugar

README.md:

- Only added '/libre' after the 'free' in 'free software'. Window.java: - Added the ability to create JSpinner components.

Author
Vngngdn
Date
Aug. 10, 2016, 6:03 p.m.
Hash
58a44db6025ff84c6a3a4d23ea0fbc8894143f80
Parent
333bf5782441615fdb5a95187aa4705ff2e4f1dc
Modified files
README.md
Window.java

README.md

1 addition and 1 deletion.

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
-
* Very lightweight. It's just a bunch of source files that you can directly link
+
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
Some other stuff that's inherint to Swing itself, might be fixed in a future
34
34
version, but some might not (Don't expect JSugar to become a thread-safe Swing
35
35
framework, Swing will stay Swing). I reserve the right to be lazy.
36
36
37
37
Limitations
38
38
-----------
39
39
40
40
The convenience causes some limitations, but they're fairly minor, and if you're
41
41
using JSugar, it's very unlikely you'd be bothered by them anyway, but here they
42
42
are:
43
43
44
44
* You can't add your own panels to the window, but you'll most likely just want
45
45
  to add some components to the window itself.
46
46
* The panels default to double buffering (which you should do anyway) and the
47
47
  flow layout.
48
48
* The window is automatically updated whenever a new component is added. When
49
49
  using Swing 'natively', you could postpone updating, but why did you add a
50
50
  component then in the first place?
51
51
* *Trigger methods* can only have 1 parameter, or none at all. That 1 parameter
52
52
  must be of type java.awt.event.ActionEvent. This should be enough for >80% of
53
53
  use cases, and if you really need more flexibility, you can add your own
54
54
  action handlers manually.
55
55
* Pressing the X in the title bar of the window closes it. >95% of use cases do
56
56
  this anyway.
57
57
* Some silly stuff like adding icons to buttons, checkboxes, ... is not
58
58
  possible. You'll have to do that yourself. Yet for most use cases,you might
59
59
  just do the sane thing and add text.
60
60
* Certain components don't offer the ability to attach a trigger action to them.
61
61
  We're talking about components like labels. But then again, these kind of
62
62
  components shouldn't get much triggers anyway.
63
63
* Some components have deliberately been left out, because they don't offer many
64
64
  information/interaction for the user. An example for this, is the
65
65
  JProgressBar. Although in some rare cases it's a very useful thing, but it's
66
66
  mainly eye candy, and you may just show the data in a JLabel anyway.
67
67
  However, if I feel like doing so, and the rest of the library is stable, I may
68
68
  add such components.
69
69

Window.java

104 additions and 1 deletion.

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
 * TODO list:
20
20
 * - JList
21
21
 * - JSlider
22
22
 * - JTable (And a JScrollBar to accompany it)
23
23
 * - JComboBox
24
24
 * - JFileChooser (?)
25
25
 * - JSpinner (Number scoller input widget)
26
-
 * DONE list:
27
26
 * - JLabel
28
27
 * - JText
29
28
 * - JButton
30
29
 * - JDialogBoxes (you know, everything dialog related)
31
30
 * - JCheckbox
32
31
 * - JRadioButton (properly grouping them has been taken care of as well)
33
32
 */
+
33
 */
34
34
35
35
/**
36
36
 * Window class for the program.
37
37
 *
38
38
 * Window contains the necessary data and methods to present the user with what
39
39
 * he's familiar with as being a "window". To make it functional, the developer
40
40
 * can make use of a series of methods to add components to said window, remove
41
41
 * components, and so on.
42
42
 * Currently, Window also contains methods to show dialogs. This will be cleaned
43
43
 * in the near future.
44
44
 * @author Maarten Vangeneugden
45
45
 */
46
46
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
47
47
import java.util.NoSuchElementException;
48
48
import java.lang.reflect.Method;
49
49
50
50
class Window {
51
51
	private JPanel panel; // The panel that contains all the components.
52
52
	private JFrame frame; // The "window" being presented to the user.
53
53
54
54
	/**
55
55
	 * Constructor of Window.
56
56
	 * By creating a new Window instance, this constructor will automatically
57
57
	 * start the initialization of the GUI. After doing so, the caller can
58
58
	 * start adding components to the window as pleased.
59
59
	 * @param title The title to be shown in the window's title bar.
60
60
	 */
61
61
	public Window() {
62
62
		this.panel = new JPanel();
63
63
		// TODO: The current title is "Hello world!" but that will become caller
64
64
		// defined soon.
65
65
		JFrame frame = new JFrame("Hello world!");
66
66
		// Makes it so that if the user clicks the X in the titlebar, the window
67
67
		// closes:
68
68
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
69
69
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
70
70
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
71
71
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
72
72
		frame.pack(); 	
73
73
		frame.setVisible(true); // Makes the window visible to the user.
74
74
		this.frame = frame;
75
75
	}
76
76
77
77
	/**
78
78
	 * Resizes the window to fit all components.
79
79
	 * By calling this method, the window will evaluate the currently visible
80
80
	 * components, and resize itself so that all components become properly
81
81
	 * visible.
82
82
	 */
83
83
	private void updateWindow() {
84
84
		this.frame.pack();
85
85
	}
86
86
87
87
	/**
88
88
	 * A series of tests for method and class handling.
89
89
	 * When a caller presents certain methods with data concerning reflection,
90
90
	 * the Java classes you need to use for that are quite opaque, and don't
91
91
	 * offer much safety in any way.
92
92
	 * The solution therefore, is run some validation checks, but these take up
93
93
	 * a decent amount of space in terms of LoC.
94
94
	 * This method takes care of all that. Call this function whenever data
95
95
	 * needs to be validated.
96
96
	 * @param methodName The name of the method, as it is declared in object.
97
97
	 * @param object The class instance in where this method will be called.
98
98
	 * @return The method that could be derived from the supplied data, or null
99
99
	 * if that wasn't possible.
100
100
	 * @throws NullPointerException if either methodName or object are null
101
101
	 * pointers.
102
102
	 * @throws IllegalArgumentException if methodName is empty, or the method
103
103
	 * does not appear to be declared in the given object, or object is not a
104
104
	 * class.
105
105
	 */
106
106
	// All unchecked typecasts are safe, and the use of raw types is taken care
107
107
	// of.
108
108
	@SuppressWarnings({"unchecked","rawtypes"})
109
109
	private Method handleReflectionData(String methodName, Object object) {
110
110
		// Null pointer checking:
111
111
		if (methodName == null || object == null) {
112
112
			throw new NullPointerException("One or more of the given parameters are null pointers.");
113
113
		}
114
114
115
115
		// XXX: Some might say the next line should be in an else{} block. But
116
116
		// Scoping rules require that I'd then have to wrap the rest of the
117
117
		// method in the same else to use it.
118
118
		Class methodClass = object.getClass(); 
119
119
		if (methodName.equals("")) {
120
120
			throw new IllegalArgumentException("The given methodName was empty.");
121
121
		}
122
122
		Method method;
123
123
		try { // First: Look if there's a method without parameters.
124
124
			method = methodClass.getMethod(methodName, null);
125
125
		}
126
126
		catch (NoSuchMethodException exception) {
127
127
			try {
128
128
				// It's possible that the method requires an event parameter, so
129
129
				// check for that as well:
130
130
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
131
131
				method = methodClass.getMethod(methodName, parameters);
132
132
			}
133
133
			catch (NoSuchMethodException e) {
134
134
				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.");
135
135
			}
136
136
		}
137
137
		// At this stage, the given data has been validated, and we've been able
138
138
		// to retrieve the method itself.
139
139
		return method;
140
140
	}
141
141
142
142
	/**
143
143
	 * Creates a button in the GUI for interaction.
144
144
	 * This function offers a convenient way to create a button, that can be
145
145
	 * directly interacted with by the user. After creation, the button itself
146
146
	 * is returned to the caller, if he wishes to do something else with it.
147
147
	 * @param text The text that will be displayed in the button.
148
148
	 * @param action The action that will be returned to the action listener.
149
149
	 * @param methodName The name of the method that will be called when an
150
150
	 * action is triggered.
151
151
	 * @param objectInstance The object instance that contains the given method.
152
152
	 * This may only be a null pointer if triggerMethod is not an instance
153
153
	 * method.
154
154
	 * performed. This method may accept an ActionEvent parameter as its only
155
155
	 * parameter, or no parameters at all.
156
156
	 * @throws NullPointerException if triggerMethod is a null pointer, or
157
157
	 * the empty String was given.
158
158
	 * @throws IllegalArgumentException if triggerMethod has more than 1
159
159
	 * parameter, or the 1 required parameter is not of type ActionEvent.
160
160
	 * @return The button that was created.
161
161
	 * @see java.awt.event.ActionEvent
162
162
	 * @see java.lang.reflect.Method.invoke()
163
163
	 */
164
164
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
165
165
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
166
166
167
167
		// For starters, we first assert that the given parameters are valid:
168
168
		if (text == null) {
169
169
			text = "";
170
170
		}
171
171
		if (action == null) {
172
172
			action = "";
173
173
		}
174
174
		
175
175
		// When the method gets here, everything's been validated correctly.
176
176
		JButton button = new JButton(text);
177
177
		button.setActionCommand(action);
178
178
		button.addActionListener(
179
179
				new java.awt.event.ActionListener() {
180
180
					public void actionPerformed(java.awt.event.ActionEvent event) {
181
181
						try {
182
182
							triggerMethod.setAccessible(true);
183
183
							if (triggerMethod.getParameterTypes().length == 0) {
184
184
								// FIXME: Next line throws a warning?
185
185
								triggerMethod.invoke(triggerObject, null);
186
186
							}
187
187
							else {
188
188
								triggerMethod.invoke(triggerObject, new Object[]{event});
189
189
							}
190
190
						}
191
191
						catch (Exception useless) {
192
192
							/*
193
193
							 * XXX: Some info on why I don't just throw said
194
194
							 * Exception to the caller:
195
195
							 * Java has this awful language constraint, which
196
196
							 * forces every damn exception that isn't a subclass
197
197
							 * of RuntimeException, to be declared in the method
198
198
							 * declaration. This tends to infect all underlying
199
199
							 * methods as well, and all that for reasons I can't
200
200
							 * comprehend. In order to keep JSugar a simple and
201
201
							 * clean library, I'll rather just handle it here,
202
202
							 * and throw a RuntimeException with appropriate
203
203
							 * details.
204
204
							 */
205
205
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
206
206
						}
207
207
					}
208
208
				});
209
209
		this.addComponent(button);
210
210
		return button;
211
211
	}
212
212
213
213
	/**
214
214
	 * Ask the user for input through a dialog box.
215
215
	 * This method presents the user with an input field, that can accept
216
216
	 * textual input. The method will return the given input after the user's
217
217
	 * clicked a button to send.
218
218
	 * @param text The text/question to be asked to the user.
219
219
	 * @return A String, equal to what the user entered.
220
220
	 * @throws NullPointerException if text is a null pointer.
221
221
	 */
222
222
	public String inputDialog(String text) {
223
223
		if (text == null) {
224
224
			throw new NullPointerException("The given text/question was a null pointer.");
225
225
		}
226
226
		return JOptionPane.showInputDialog(text);
227
227
	}
228
228
229
229
	/**
230
230
	 * Give the user a dialog box.
231
231
	 * This method can be used to provide a simple dialog to the user.
232
232
	 * This will show the user the given question, after which a boolean value
233
233
	 * is returned, holding the choice.
234
234
	 * @param text The text/question to be asked to the user.
235
235
	 * @return True if the user confirms, False if he denies.
236
236
	 * @throws NullPointerException if text is a null pointer.
237
237
	 */
238
238
	public boolean confirmDialog(String text) {
239
239
		if (text == null) {
240
240
			throw new NullPointerException("The given text/question was a null pointer.");
241
241
		}
242
242
		final int ACCEPTED = 0;
243
243
		//final int DENIED = 1; // Not used in the current context.
244
244
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
245
245
		if (result == ACCEPTED) {
246
246
			return true;
247
247
		}
248
248
		else {
249
249
			return false;
250
250
		}
251
251
	}
252
252
253
253
	/**
254
254
	 * Give the user a choice dialog box.
255
255
	 * This method gives the user a simple dialog with predefined choices.
256
256
	 * These choices are to be provided by the caller in a simple array.
257
257
	 *
258
258
	 * Tip: This method works extremely well with arbitrary created choices.
259
259
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
260
260
	 * then that value is immediately returned.
261
261
	 * @param text The text/question to be asked to the user.
262
262
	 * @param choices An array of Strings, containing the choices the user can
263
263
	 * pick.
264
264
	 * @return The index value of the picked choice, or -1 if no choices were
265
265
	 * given.
266
266
	 * @throws NullPointerException if text is a null pointer.
267
267
	 */
268
268
	public int choiceDialog(String text, String[] choices) {
269
269
		if (text == null) {
270
270
			throw new NullPointerException("The given text/question was a null pointer.");
271
271
		}
272
272
		// First: handling the trivial cases:
273
273
		if (choices.length == 0) {
274
274
			return -1;
275
275
		}
276
276
		else if (choices.length == 1) {
277
277
			return 0;
278
278
		}
279
279
		int answer = JOptionPane.CLOSED_OPTION;
280
280
		// The dialog needs to be shown again until the user has made a possible
281
281
		// choice, i.e. Chickening out using the close button is not possible
282
282
		// (Because that returns CLOSED_OPTION).
283
283
		while (answer == JOptionPane.CLOSED_OPTION) {
284
284
				JOptionPane.showOptionDialog(
285
285
					null, // The parent component. May become the panel?
286
286
					text, // The text/question to describe the goal
287
287
					"Dialog", // The text in the title bar
288
288
					JOptionPane.DEFAULT_OPTION, // The kind of available options
289
289
					JOptionPane.QUESTION_MESSAGE, // The type of message
290
290
					null, // The icon to show
291
291
					choices, // The possible choices
292
292
					choices[0] // The standard choice
293
293
					);
294
294
		}
295
295
		return answer;
296
296
	}
297
297
		
298
298
299
299
	/**
300
300
	 * Creates a label in the GUI for interaction.
301
301
	 * This function offers a convenient way to create a label, that can be
302
302
	 * directly interacted with by the user. After creation, the label itself
303
303
	 * is returned to the caller, if he wishes to do something else with it.
304
304
	 * @param text The text that will be displayed in the label.
305
305
	 * @return The label that was created.
306
306
	 */
307
307
	public JLabel createLabel(String text) {
308
308
		JLabel label = new JLabel(text);
309
309
		this.addComponent(label);
310
310
		return label;
311
311
	}
312
312
313
313
	/**
314
314
	 * Adds a checkbox to the window.
315
315
	 * By providing a String, you can use this method to easily
316
316
	 * create a checkbox, and add it to the window. 
317
317
	 */
318
318
	public JCheckBox createCheckbox(String text) {
319
319
		JCheckBox checkbox = new JCheckBox(text);
320
320
		this.addComponent(checkbox);
321
321
		return checkbox;
322
322
	}
323
323
324
324
	/**
325
325
	 * Adds radio buttons to the window.
326
326
	 * Given a list of Strings, this method will create the same amount of radio
327
327
	 * buttons.
328
328
	 *
329
329
	 * The radio buttons will silently be grouped in a ButtonGroup object,
330
330
	 * making them automatically disable each other, so only 1 radio button can
331
331
	 * be enabled. This ButtonGroup is immutable.
332
332
	 *
333
333
	 * If you need a mutable ButtonGroup, create your own, and use the {@link
334
334
	 * #addComponent()} method to add the radio buttons manually.
335
335
	 * @param text An array of Strings. The length of the array will determine
336
336
	 * the amount of radio buttons that will be created.
337
337
	 * @return An array of radio buttons, in the same order as text.
338
338
	 */
339
339
	public JRadioButton[] createRadioButtons(String text[]) {
340
340
		JRadioButton[] radioButtons = new JRadioButton[text.length];
341
341
		ButtonGroup buttonGroup = new ButtonGroup();
342
342
		for (int i=0; i<radioButtons.length; i++) {
343
343
			radioButtons[i].setText(text[i]);
344
344
			buttonGroup.add(radioButtons[i]);
345
345
			this.addComponent(radioButtons[i]);
346
346
		}
347
347
348
348
		assert radioButtons.length == buttonGroup.getButtonCount() : "The amount of radio buttons ("+ radioButtons.length +") differs from the amount of buttons in buttonGroup ("+ buttonGroup.getButtonCount() +").";
349
349
		return radioButtons;
350
350
	}
351
351
352
352
	/**
+
353
	 * Adds a number spinner component to the GUI.
+
354
	 * This method allows the caller to create a so-called "spinner component"
+
355
	 * to the window. This spinner is an input box, in which only integers can
+
356
	 * be put.
+
357
	 *
+
358
	 * The caller can set a range, a start value, and a step size.
+
359
	 *
+
360
	 * The spinner created with this method may modify itself based on the
+
361
	 * parameters.
+
362
	 * For example: If the minimum and maximum value are equal, the spinner will
+
363
	 * be disabled.
+
364
	 *
+
365
	 * @param minimum The minimum value that can be selected.
+
366
	 * @param maximum The maximum value that can be selected.
+
367
	 * @param start The value that will initially be shown in the component.
+
368
	 * @param stepSize The step size when the user increases/decreases the
+
369
	 * value.
+
370
	 * @throws IllegalArgumentException if minimum is larger than maximum, 
+
371
	 * start is not in the range of the selectable values, or stepsize is not a
+
372
	 * natural number.
+
373
	 * @return The JSpinner that was added to the window.
+
374
	 */
+
375
	public JSpinner createSpinner(int minimum, int maximum, int start, int stepSize) {
+
376
		// As usual, we begin with checking the contract:
+
377
		if(minimum > maximum) {
+
378
			throw new IllegalArgumentException("The minimum value ("+ minimum +") was larger than the maximum value ("+ maximum +")");
+
379
		}
+
380
		// The "start ∉ [minimum, maximum]" is done by the SpinnerNumberModel
+
381
		// constructor, which will be constructed later.
+
382
		if(stepSize <= 0) { // stepSize ∉ ℕ¹ (In Belgium: ℕ₀)
+
383
			throw new IllegalArgumentException("The stepSize ("+ stepSize +") is not a natural number (excluding 0).");
+
384
		}
+
385
		// If the contract is valid, we can begin:
+
386
		/*
+
387
		 * I'd like to interject here, because this is a nice example of why
+
388
		 * JSugar was a good idea:
+
389
		 * If you want a spinner, you'll typically want an integer spinner. But
+
390
		 * in Swing, when you create a JSpinner, it creates a JSpinner, with a
+
391
		 * predefined 'SpinnerNumberModel' attached to it.
+
392
		 * It's this model you then have to extract from the created spinner, on
+
393
		 * which you need to apply the configuration.
+
394
		 * What you actually have to do, is create a SpinnerNumberModel
+
395
		 * yourself, put your settings on that, and then, create a JSpinner to
+
396
		 * which you give that SpinnerNumberModel.
+
397
		 * In essence: The entire Java framework is shit.
+
398
		 */
+
399
		SpinnerNumberModel spinnerSettings = new SpinnerNumberModel(
+
400
				start,
+
401
				minimum,
+
402
				maximum,
+
403
				stepSize
+
404
				);
+
405
		JSpinner spinner = new JSpinner(spinnerSettings);
+
406
		if(minimum == maximum) { // Trivial value is set already, --> disable.
+
407
			spinner.setEnabled(false);
+
408
		}
+
409
		this.addComponent(spinner);
+
410
		return spinner;
+
411
	}
+
412
+
413
	/**
+
414
	 * Adds a number spinner component to the GUI.
+
415
	 * This method allows the caller to create a so-called "spinner component"
+
416
	 * to the window. This spinner is an input box, in which only integers can
+
417
	 * be put.
+
418
	 * 
+
419
	 * Tip: This method is a convenience method, and works extremely well with
+
420
	 * arbitrary data.
+
421
	 * For example: The start value is automatically set to the minimal possible
+
422
	 * value, and the step size defaults to 1.
+
423
	 * If the minimum and maximum are equal, the component will be disabled, and
+
424
	 * thus, be locked on the only (trivially) possible value.
+
425
	 * If minimum is larger than maximum, the method will notify you of this,
+
426
	 * but also swap the values. So you can rest assured that the spinner will
+
427
	 * handle errors, but also, inform you about it.
+
428
	 * @param minimum The minimum value that can be selected.
+
429
	 * @param maximum The maximum value that can be selected.
+
430
	 * @return The JSpinner component that was added to the window.
+
431
	 */
+
432
	public JSpinner createSpinner(int minimum, int maximum) {
+
433
		// The disabling of equal values is done in the full createSpinner(), so
+
434
		// this is merely switching values if they need to be swapped.
+
435
		if(minimum > maximum) {
+
436
			System.err.println("minimum ("+ minimum +") was larger than maximum ("+ maximum +").");
+
437
			// FIXME: Consider whether it's appropriate to print a stacktrace
+
438
			// here, which may be convenient for debugging.
+
439
			
+
440
			// XXX: I know you don't need the help variable when swapping
+
441
			// integers, because you can also do basic arithmetics. Change it if
+
442
			// it causes too much eye burn for you.
+
443
			int swapValue = minimum;
+
444
			minimum = maximum;
+
445
			maximum = swapValue;
+
446
		}
+
447
+
448
		// Yeah, these 2 variables make you cringe huh, performance addicts?
+
449
		// Drown me in the tears of your useless performance-related opinions.
+
450
		int startValue = minimum;
+
451
		int stepSize = 1;
+
452
		return this.createSpinner(minimum, maximum, startValue, stepSize);
+
453
	}
+
454
+
455
	/**
353
456
	 * Adds the given component to the GUI.
354
457
	 * This method allows its caller to give a pre-made component, so that it
355
458
	 * can be added to the GUI. Even though its main use is for the Window class
356
459
	 * itself, the user of JSugar can also use it to create components himself,
357
460
	 * and then add them. As such, this method doesn't provide parameters for
358
461
	 * reflection/action triggering purposes.
359
462
	 * @param component The component to be added to the window.
360
463
	 * @throws NullPointerException if the given component is a null pointer.
361
464
	 */
362
465
	public void addComponent(JComponent component) {
363
466
		int originalSize = this.panel.getComponentCount();
364
467
		this.panel.add(component); // Throws the exception if null.
365
468
		this.updateWindow();
366
469
367
470
		assert originalSize == this.panel.getComponentCount()-1 : "A component was supposed to be added to the window, but the total amount of components was unchanged after the addition.";
368
471
	}
369
472
370
473
	/**
371
474
	 * Removes the given component from the GUI.
372
475
	 * This method allows its caller to remove a component from the GUI.
373
476
	 * @param component The component to be removed.
374
477
	 * @throws NoSuchElementException if the given component does not exist in
375
478
	 * the GUI.
376
479
	 * @throws NullPointerException if the given component is a null pointer.
377
480
	 */
378
481
	public void removeComponent(JComponent component) {
379
482
		int originalSize = this.panel.getComponentCount();
380
483
		this.panel.remove(component);
381
484
		int newSize = this.panel.getComponentCount();
382
485
		if (originalSize != newSize+1) {
383
486
			throw new NoSuchElementException("The given component does not exist in the GUI.");
384
487
		}
385
488
		this.updateWindow();
386
489
	}
387
490
}
388
491