Comprehensive widgets:
1. Motivation
In Java some different Graphical User Interfaces (GUI) exists, beginning with the basically AWT (Advanced Widget Toolkit) from the 1990th, which was enhanced with the Swing classes. Swing was powerful, but also designated as "overengineered". But is is currently in use.
With the development of the known and familiar Eclipse GUI (www.eclipse.org) a new approach comes: "SWT", named as "Standard Widget Toolkit". Intermediate Java FX comes, but it is not really substantiated.
Currently SWT seems to be the favor.
All Graphical User Interface approaches in all operation systems in all languages are similar. The differences are in the details. This fact is accepted by SWT: SWT uses the graphical properties of the underlying operation system and adapts it only to Java. Because the approaches of the operation system’s graphical capabilities are similar, this adaption is possible and successfully.
But nevertheless, the problem is:
-
You may need to decide which Graphical System in Java should be used: Simple AWT only, The old Swing, SWT, Java-FX, or …
-
Beside knowledge of the graphic system’s capabilities you need detailed knowledge about Thread usage, specifics for event handler, etc. It is not simple.
An often occurring phenomenon in graphical applications is: Since the handling of events must be prior in the graphic thread, a lot of processing is put into the graphic thread. The PC processor is fast, all runs. But sometimes some waiting conditions occur, for example for file reading in a network connection. Then - unexpected - the graphic thread hangs, the mouse pointer shows a sandglass in the older Windows or a rotating wheel since a few years, all we know that. Hence, the handling of events should be better supported by a proper multi threading. See chapter Threads and callback operations in user threads
The here presented vishia-Gral was developed by me in a time approximately in 2010 with some knowledge of Swing, new knowledge of SWT and the goal to use both, in a better way. The primary idea of Gral was: Select the used Graphical system only in implemenation level, not for user programming. The user programming should be independent of the used implementation Graphic. SWT or AWT or Swing or Java-FX - a not user-related decision. Because all systems are similar, it is possible.
The second approach of Gral is, solve the problems with the thread, simplify the handling.
And a last but not least approach was: Support textual (scripting) definition of the graphic.
In the years from 2010 some developing steps are done, the graphic was elaborately used by some small also industry projects. The system was improved, and this documentation should be for the really proper usable version written in the year 2022.
This GRAL graphic user interface solution is for technical GUI graphics, not for best and smart solutions. It is a simple 2D graphic, not 3D and not too much in nice not need appearance.
2. Approaches
2.1. Position and size of widgets
See also Positions, syntax, possibilities
Often a so named "float layout" is favored. This simplifies the positioning of widgets. But the float layout is simple as also quick and - dirty. The position of widgets in an application should be a little bit substantiated. The float layout is not supported by Gral.
Usual graphic positions are pixel oriented, the pixel of the screen. But from user’s eyes, the pixel are subordinated. It is a high effort to calculate the pixel position, height and width, the numbers are not related to user’s thinking.
That’s why another system is used: Grid positions.
The basic grid is oriented to the normal text size. A normal text with a font in the standard proper read-able size is presented by 2 units of the Gral-position in vertical direction (line) and approximately 1 unit per character in horizontal direction. Of course the horizontal character size depends on the font properties. A text can be presented in a smaller or larger font. The text height depends on the height given in the position of the text. A very small font is presented by 1 vertical gral-unit. Such a text can be used as short title for text input fields (prompt) or adequate.
A button is able to proper present with 3 or 2 vertical gral units. A small check box may be presented with 1 x 1 gral unit. The size of a window in Grid units is approximately 80 x 120 (height x width), this is 720 x 480 pixel on small solution till 1200 x 1800 pixel in a fine solution. It means it fills the screen of a raw solution monitor (640 x 480 is 80 x 106 grid units, 6 pixel/grid) as also on a standard monitor (1920 x 1080 with 12 pixel/grid).
A gral unit should be have the same distance in vertical as in horizontal direction. It depends on the graphical implementation. One gral unit can have 6 to 18 pixel, depending on the requested size of appearance in comparison with the given display pixel size. Any graphic can be shown in several sizes of appearance, given with a start parameter of the application (see {@link org.vishia.gral.area9.GuiCallingArgs#sSize}) respectively the parameter size of {@link GralGridProperties#GralGridProperties(char size)} as character 'A' till 'H'.
Fine positions
Either the positions are given with 2 integer values as 'fundamental positions'. That are the position described above. Or they can be given with a float value or a second int named 'fractional part'. From the float value only the first digit after point is used, a fractional part can be given with a value from 0 to 9.
The fine position divides one gral position into 5 or into 6 fine positions. The odd numbers divide into 6 positions. In this kind a gral position is able to divide by 2, 3 and 6:
-
1: 1/6 = 0.1333
-
3: 1/3 = 0.3333
-
5: 1/2 = 0.5
-
7: 2/3 = 0.6667
-
9: 5/6 = 0.8667
The even numbers divide into 5 positions:
-
2: 1/5 = 0.2
-
4: 2/5 = 0.4
-
6: 3/5 = 0.6
-
8: 4/5 = 0.8
The fine positioning enables a fine positioning of widgets in respect to the fundamental positions.
With a grid size of 10 (represented by letter 'E', see ../../docuSrcJava_vishiaGui/org/vishia/gral/base/GralGridProperties.html) a fine unit is exact one pixel. On a more raw solution (lesser pixel per grid unit) not any fine unit can be really represented. But the division in 1/6 …5/6 is proper (odd number), and also the division in 1/5..4/5 (even number), but not any combination.
Look on the appearance of texts in the Graphic (GralLabel) with different sizes and screen resolutions:
This are texts with different size for the real raw solution, -size=A, for a small monitor for example Standard VGA. The image has original 157 x 101 pixel, it is ~ 1/4 in both directions of the area of a standard VGA monitor with 640x480 pixel. The font size of 1.0 is borderline, but able to guess what is meant. It is only for example known promps for fields. The font size of 2.0 is proper readable also in this solution, it is the standard text font. If you have a monitor with a fine pixel size, you may increase your zoom to read it.
This is -size=C, i guess readable on a monitor with raw solution (greater pixel size).
This is -size=E, one fine grid unit is one pixel.
And this -size=H, is only sensible on a large monitor with fine pixel. The programm is anywhere the same, only the size character as argument on start of the GUI is different.
The script to produce this output is the following, no programming is necessary, using link:
@30+1.0, 2+20: Text("abg^AZ size 1.0"); @31.2+1.2, 2+20: Text("abg^AZ size 1.2"); @32.4+1.5, 2+20: Text("abg^AZ size 1.5"); @34+2.0, 2+20: Text("abg^AZ size 2.0"); @36+2.5, 2+20: Text("abg^AZ size 2.5"); @38.5+3.0, 2+25: Text("abg^AZ size 3.0"); @41.5+4.0, 2+30: Text("abg^AZ size 4.0");
String or numeric given positions
The positions can be specified numerically, absolute or relative. For scripting it is necessary to process position specifications in textual form. This textual form is also usable for normal Java programming. Parsing of the position String is not an high effort, it is better write- and readable.
Usual the name of a widget and the position is joined in one String, for example given as
"@panel,10+2,12..-15=myWidget"
For this example the panel where to place is given. The widget, for example a text input field, should be placed in the 10th line (y) with 2 lines height, and 12 grid units in horizontal direction from left, 15 grid units from right. It means the widget has a width depending from the panel or windows size, and of course, the widget is only visible if the window/panel has at least a width of 28 grid units.
For more capabilities see Position writing style in a gral script
Relative positions
A position of the next widget to define can be derived from the position before, with a relative number. Also the next position in the desired direction can be used automatically without manual given position. This is similar such approaches as float layout.
Resizing
Of course if a window or panel is resized, the pixel positions of the widget are calculated newly from the given grid positions with all relative relations. To get a proper layout on different windows sizes it is possible to define some widgets as distance from left and right, or in percents from the size. Hence the windows can be shifted lesser, only the important widgets are visible to get an overview on the screen, and on demand the window can be increased to the necessary size.
2.2. Definition of the GUI appearance and implementation of the Graphic
In vishiaGui - Gral the definition of the Graphical appearance is separated and independent of the Graphic implementation. All Widgets are simple instances from the Java Gral widget classes, first unrelated to graphic implementation.
All Gral widget classes are derived from org.vishia.gral.base.GralWidget and/or …gral.base.GralWidgetBase. They contains:
-
The position and the size of the widget
-
The type, some properties used for appearance
-
Static and Dynamic data to show (text, values, link to variables, data paths). Especially dynamic data can be read and write independent of the graphic thread. The actualizing is done by standard event handler without user programming effort. To show new given data only ../../docuSrcJava_vishiaGui/org/vishia/gral/base/GralWidget.html#redraw-int-int- should be called from the users thread which is automatically done by the set operations. See chapter Threads and callback operations in user threads. It creates an entry in the /docuSrcJava_vishiaGui/org/vishia/gral/base/GralGraphicThread.html timer event queue which does the work in a standard way without user effort.
-
Some operations (implementation of ../../docuSrcJava_vishiaGui/org/vishia/gral/ifc/GralUserAction.html) which should be executed in some situations. This user actions are executed in the graphic thread, but they can/should deal with other threads.
It means, all appearance and data exists without the implementation graphic. One advantage is: the implementation graphic can be removed, unloaded and built newly, for example if any crash or trouble situation occurs. The implementation can also be existing in a separated device, only connected by communication lines.
This concept was not present in the beginning in 2010. In 2010 the implementation graphic was created in the same moment as the GUI was defined. The interface ../../docuSrcJava_vishiaGui/org/vishia/gral/ifc/GralMngBuild_ifc.html was responsible to creating both, the implementation parts of the GUI and the Gral independent wrapping classes of the gral. Hence the implementing functions should regard already the implementation graphic.
This interface GralMngBuild_ifc
is still existing, but now only the Gral classes are created with.
The other new possibility, firstly developed ~2015..16 is: Creating the Gral classes
by their constructor immediately, not using the GralMngBuild_ifc
.
This seems to be more obviously in programming.
Both approaches are usable, see Templates / examples for creating the graphic appearance
The implementation of the Graphic with the specific graphic classes is done after the definition of the appearance with the Gral Graphic classes . It can be repeated on trouble situations as mentioned above: See Template / example to start the graphic implementation
2.3. Independence of Gral Widgets from the operation system’s capabilities
In Java-Swing only basic capabilities of the operation system are used from Java level. Hence the appearance and the features are full defined on Java level.
The SWT has chosen a different way, just because of bad experience with Swing in calculation time (the computers in the 1990^th^ were a little bit slower) and the better approach: Subordinate under appearance rules of the operation system with other non Java applications.
Both ways have advantages and disadvantages.
Often an Operation system offers appearances of the Graphic which are really not necessary, but nice or smart. This may be interested for users which likes nice and smart, but not for technician. For example rounded corners, smart (but bad distinguishable) colors of title bars and such. That are (partial specific) capabilities of one operation system - with lesser relevance. The advantage of a operation system-related graphic is: You can use it.
On the other hand the basic capabilities to draw proper widgets are given for all operation systems in a relative standardized form. If simple forms are used in an well-thought-out kind, you get a proper appearance which does not need too much calculation time and runs also on simple systems.
Hence drawing some widgets by simple basic solutions are reasonable.
For example, the representations of buttons in Gral are programmed with very simple lines, showing a pressed or not pressed button, also with some colors for checked or not checked buttons. The problem was, that the original MS-Windows button was very nice and smart, but can’t deal with color on the button (in ~2010, XP). Hence the button was replaced by this simple solution, which is still present and proper.
Another problem was operate with tables. Windows has only support selecting a full line, not selecting a cell with a color. That’s why, also in 2010, a table was not built with the SWT access to the Windows OS-API table, but with some simple text fields. The additional advantage was also, the data are completely separated from the graphical table content. The data are stored in ordinary Java container, independent from the Graphic. Only for showing (and also input) data in table cells, the data are transprorted to the graphic widgets and updated from there. This is a very fast operation (for the computers after the 2000th), and it is better for handling. See chapter The GralTable
2.4. What is a widget and a panel
The term "widget" should be known, it is one part of the GUI in a window or a panel. A panel is either the whole window area, or some sub areas, or "tabbed panels" with selection tab. A panel is a container of widgets.
A widget is:
-
a) either an immediately existing widget of the underlying operation system, whereas all operation systems have similar capabilities. This is for example an input field, a simple text (in Swt a Label) or also a button.
-
b) or it is a composition of a few associated widgets of the underlying operation system. This is given for example by a text input field with a prompt. The input field and the prompt builds one GralWidget, but two implementation widgets. For the variant b) the association between the GralWidget and the implementing widgets are done in the implementation level. Hence the implementation can decide how to implement. This is done for example for the GralTable, which has a counterpart, the SwtTable.
-
c) Some Gral Widgets are comprehensive and have not an full programmed counterpart in the implementation. Instead this widgets have a "construction plan" in the GralWidget to access existing implementation widgets in a defined appearance (the position) or specific callback handler at Gral level (for example mouse events). For that the implementing level does not need specific implementation. An example for that is the GralFileSelector.
-
d) or the widget is anything that is drawn on the graphical screen by basic operations. Typical for the last choice is the ../../docuSrcJava_vishiaGui/org/vishia/gral/base/GralCurveView.html. The curves have to be drawn anyway, the grid is drawn specially, and also the cursors and some texts.
For b) only a few widgets from the implementation level are necessary. Some implementation level widget possibilities are not used, instead the Gral widgets are implemented by other standard widgets. This is especially for a table, which is usual supported from the operation system, but not in a sufficient form. Hence it is implemented by a set of simple text fields, which are visible or not (changing the size of the table). This is a proven solution also since 2010, see chapter The GralTable
For d) the operation system should offer a minimal set of graphic capabilities:
-
Background color definition, fill the background with a color.
-
Draw lines in different colors ans sizes
-
Text, selecting fonts, write texts in different directions.
-
A text field with or without frame and background color. Should be editable or non editable.
-
A text drawn without background.
All others can be built by proper draw operations of the implementation widgets.
It is possible to use more capabilities of the operation system and the implementation graphic, if that is proper.
2.5. The Graphic Thread and processing in other threads
See also chapter Threads and callback operations in user threads
In Swing it is sometimes possible to access values from a Widget in another thread.
In SWT this is consequently: All access or set operations can only be executed
in the graphic thread. Elsewhere an Exception is thrown: org.eclipse.swt.SWT.ERROR_THREAD_INVALID_ACCESS
With that decision some possible non obvious thread problems are prevented.
What are the usual approaches for GUI operations:
-
The so named event listeners, in SWT based on
org.eclipse.swt.internal.SWTEventListener
are called by the execution thread of the graphic. This is a specific Graphic thread, or in SWT the user thread which callsswt.widgets.Display#readAndDispatch()
This thread should be always able to run, to accept inputs from the user to the GUI (mouse, selecting, etc.). -
The implementation operations of the different event listener are programmed by the application. This operations are executed primary in the graphic thread.
-
Of course the access to all Widgets is possible in this graphic thread. It means the application can do all necessary things in this graphic thread. It can be interact with other threads with known Java thread mechanism outside of the graphic, for example using the
java.util.ConcurrentLinkedQueue
for information interchanging. That seems to be a good decision. -
But: If the tasks to do in the event handling is too complex,
-
It may be need a too long calculation time, or more concise, the "calculation" do accesses to some operation system resources, which are normally fast, but sometimes needs synchronization and access time. An example is: Access to files on the own hard disk, access to files in a network, access to files which may be locked a small time by other processes. Maybe some people knows the rotating hour glass from Windows 3.11 which is later replaced by a rotating circle, because the users hate the hour glass. The application is blocked in that time.
-
In that time of waiting for execution of one listener operation, another operation is not possible. For example a long algorithm works, it is realized by the user that it is wrong, but the user cannot act. Should wait for finishing, instead abort the wrong stuff.
-
Often, the first small things are programmed immediately in the event handler, than the algorithm are growing, some file accesses are done, the files are in network, the network hangs sometimes, and then you have the rotating wheel in the graphic and you cannot abort your operation which accesses the file in the network. You know, the network is not available. But the computer does not know it (too long timeouts). You cannot react. You are not the master of the disaster, the system is determining you. It’s stupid.
For the Gral graphic, all accesses to the data of the widgets are possible in any thread without access the implementation widgets (which is not possible or complicated). That is done with two mechanism:
-
A changed content of widgets via graphical handling (for example doing any text input to a text field or pressing a button) is transported to the Gral level (outside of the implementation widget) via programmed event handler in the implementation. Sometimes only the complete inputed text is transferred only on leaving the focus from the input field, because only then it may be attempt to use. Sometimes any key stroke transfers the content. That is not a too high calculation effort for the processor, if a real human types. The application can access this data anytime in any thread.
-
A changed content of widgets at Gral level, set another text, or such is transported to the implementation widget via a
redraw()
request. To save calculation time for high frequently changes, which are anyway not visible, a time order is used between to do this. For example a refreshing rate of 100 ms is adequate.
Hence, the user can program evaluating and influencing the graphic operations in any thread, without own effort for thread synchronization and event handling. All is done in the ready coded implementation level of Gral.
Read more in chapter Threads and callback operations in user threads
2.6. Simple example Window with text field and button programmed in Java code
First programming in Java is shown and explained.
You find the example in the sources of testJava_vishiaGui
on https://github.com/JzHartmut in the file
`java/test_vishiaGral/org/vishia/gral/test/basics/Test_SimpleTextButton_GuiCfg.java`:
class Header
package org.vishia.gral.test.basics;
import org.vishia.gral.base.GralButton;
import org.vishia.gral.base.GralTextField;
import org.vishia.gral.base.GuiCallingArgs;
import org.vishia.gral.cfg.GuiCfg;
import org.vishia.gral.ifc.GralUserAction;
import org.vishia.gral.ifc.GralWidget_ifc;
/**This class is a simple example for a Gral GUI application with a hard coded program
* (not textual configured).
* It uses nevertheless the base class {@link GuiCfg} which is firstly for textual graphic configuration.
* But this features don't need to be used.
* The GuiCfg builds a base graphic system which is able to enhance also with program parts.
*
* @author Hartmut Schorrig
* @since 2023-08
*/
public class Test_SimpleTextButton_GuiCfg extends GuiCfg {
Widget references
/**An application widget which should be accessed in the process of working with the GUI.
* Here a text for input/output*/
final GralTextField wdgInputText;
/**An application widget which may be accessed in the process of working with the GUI.
* But if it is not accessed the reference is not necessary to store.
* Here a button with a operation while pressing. The access itself is not necessary. */
final GralButton wdgButton;
Widget references are only necessary if the processing of data need the access to the widgets.
In this example the wdgInputText
is necessary to access, but the wdgButton
isn’t really.
But for enhancements, for example change the text of the button, it may be usefully.
Only widgets which should be never accessed needs a reference to.
The reference is not necessary for the graphic itself, only to reference the widgets for processing data.
The widgets can also be accessed by getting the reference by name from the GralMng. This is shown in the chapter Determine the graphic via script.
Specificating the graphic appearance in the constructor
/**The constructor of the application class organizes some more widgets
* in a programmed way.
* @param cargs Arguments, only {@link GuiCallingArgs#sTitle} is used.
*/
Test_SimpleTextButton_GuiCfg (GuiCallingArgs cargs) {
super(cargs, null, null, null); // It creates the main window
// // with the given title and properties.
this.wdgInputText = new GralTextField(super.refPos, "@2+2, 2+20=input"
, GralTextField.Type.editable); // add a widget to the main panel.
this.wdgInputText.setText("abcd"); // set initial a text.
// // add the button widget
this.wdgButton = new GralButton(this.refPos, "@8-3, 2+10=button"
, "press me", this.actions.actionButton);// with this given action on pressing
}
Firstly the ctor of the super class GuiCfg
is called,
That ctor creates the Window instance at Gral Level with the first panel.
The window’s panel is the current one. Hence the refPos
refer it and can be used immediately.
The GralWidget instances are defined here all with construction and assignment to the final references if necessary.
If a widget don’t need to be referenced, only new Gral…`widget should be written without assignment.
The widget is references in its panel and in the [Java]`super.gralMng
instance mediated by the refPos
.
For the wdgButton
an action is used which is defined below.
Example operation for the button
/**Action for the button, called from {@link Actions#actionButton},
* associated to the #wdgButton.
* It reads the text, change the order and writes back. */
void actionButton() {
String text = this.wdgInputText.getText(); // reads the text
StringBuilder newText = new StringBuilder(text.length());
for(int pos = text.length()-1; pos>=0; --pos) {
newText.append(text.charAt(pos));
} // and writes back in revers order
this.wdgInputText.setText(newText); // only as example
}
This operation is called by the button and accesses the text field, only as example.
Main command line start
public static void main(String[] cmdArgs) {
GuiCallingArgs cargs = new GuiCallingArgs(); // The cargs instance is necessary
boolean bOk = cargs.parseArgs(cmdArgs, System.err); // parse command line args if given, not necessary for this example
if(bOk) { // set a title for the main window if not given
if(cargs.sTitle == null) { cargs.sTitle = "Test_SimpleTextButton_GuiCfg"; }
Test_SimpleTextButton_GuiCfg thiz = new Test_SimpleTextButton_GuiCfg(cargs); //ctor
thiz.execute(); // calls initMain(), stemMain() and waits for closing graphic, then finishMain().
}
}
The main
creates an instance of GuiCallingArgs
which are necessary for the constructor of
GuiCfg (the super class).
Normally this instance can be used to gather command line arguments. But for this example it is not necessary.
If the sTitle
is set (by command line argument, or direct), it is used as title for the window.
Style to define actions
/**Actions may be organized in an extra class, more overview in Outline of the class and debug variables.
*/
private class Actions {
/**Action for the button, calls {@link Test_SimpleTextButton_GuiCfg#actionButton()}*/
GralUserAction actionButton = new GralUserAction("actionButton") {
@Override public boolean exec ( int actionCode, GralWidget_ifc widgd, Object... params ) {
Test_SimpleTextButton_GuiCfg.this.actionButton();
return true;
}
};
Actions() {}
} // class Actions
private Actions actions = new Actions(); // instance of actions
The actions for buttons are instances of
GralUserAction.
Its operation exec(…)
should be overridden. But it is proper to call an operation in the main class there.
It is also recommended to wrap all Actions with this here shown Actions
class.
Then it is more structured to view and understand the software.
That’s all
for a simple application and the pattern of an application.
2.7. Determine the graphic via script
this is old.
The java coded graphic is not too expensive, and it is able to understand (debug) what is done. Using a script is very more similar, most of the necessities are also possible. In the script the access to Java code parts is possible.
Look for the same example as above but with script.
The class can be found as srcJava_vishiaGui/java/vishiaGui/org/vishia/gral/example/ExampleScriptSimpleButton.java
:
class Header and Gui script
public class ExampleScriptSimpleButton {
LogMessage log;
final String guiScript =
"@10+30, 20+80 =mainWin:Window Example ScriptSimpleButton; \n"
+ "@tab1, 2+2, 2+20 =input: InputField(); \n"
+ "@8-3, 2+10 =button1: Button(\"press me\", action = actionButtonCode);"
+ "@tab2, 8-3, 2+10 =button2: Button(\"Button2\");"
+ "@-10..0,0..0 =output: OutputBox();"
;
Here you see the class head and the script in String given form. The script can also come from a file, or it can be prepared by any other Java program, for example with configuration data what should be shown. Only for this simple example it is a simple text.
Specific class for Gui stuff (possible and recommended)
protected class GuiElements {
final GralMng gralMng;
final GralTextField wdgInputText;
final GralTextBox wdgOutput;
GuiElements(CharSequence script, LogMessage log) throws ParseException {
this.gralMng = new GralMng(log); // The GralMng should know the user actions used in the script.
this.gralMng.registerUserAction("actionButtonCode", ExampleScriptSimpleButton.this.actionButtonCode);
//
this.gralMng.initScript(script); // initialize the graphic Gral Widgets (not the implementig graphic).
//
this.wdgInputText = (GralTextField)this.gralMng.getWidget("input");
this.wdgOutput = (GralTextBox)this.gralMng.getWidget("output");
}
}
As also in the example of the chapter before, the Gui stuff is written in an inner class.
One of substantial differences is: The constructor may throw an ParseException
,
because of course the script is not checked on compile time, but on run time.
With the exception a elaborately error message is written, so that the user can fix it.
The application is here terminated, the catch is in main
, because with the error it cannot be run.
Adequate is done if the script does not contain the expected widgets.
The advantage is, this code is independent from the design of the gui appearance.
More fields and ctor of the application class builds the Gral graphic part
final GuiElements gui;
int ctKeyStroke;
ExampleScriptSimpleButton(String[] args) throws ParseException
{
this.log = new LogMessageStream(System.out); // may also write to a file, use calling arguments
this.gui = new GuiElements(this.guiScript, log);
}
Now some fields of the user class are defined, especially the Gui class, and the constructor of the application.
Because the Constructor calls the new GuiElements
, this constructor can also throw, it means the application throws.
It is consequently for this case, in other cases somewhat can be further executed.
Initialization of the implementation Graphic
boolean init(String awtOrSwt) {
this.gui.gralMng.reportGralContent(log);
// // check whether the widgets are existing
if(this.gui.wdgInputText == null) { throw new IllegalArgumentException("missing widget \"input\""); }
if(this.gui.wdgOutput == null) { throw new IllegalArgumentException("missing widget \"output\""); }
this.gui.wdgInputText.setText("any text input");
this.gui.gralMng.createGraphic(awtOrSwt, 'E', this.log);
return true;
}
This is the init() operation after construction. Because the content of the gui script may be syntactically correct but may not contain all necessary widgets, firstly a report is written to console. Then the necessary widgets are checked whether they are existing. Because they are mandatory, also an Exception is thrown. It is also possible, depending from the content of the gui script, to determine which functionality are used. If widgets are not contained in the script, its functionality may be conditional in the application. It means, the script, maybe given by a file, can determine some behavior of the application.
The rest of the example application is identically to the non script version above. Also the steps of the life cycle are similar as described above.
2.8. Menu and context menu
Any widget can have a context menu. A context menu can be defined by
2.9. Info and Help in a GUI application
Content sensitive help is expectable in a well formed GUI application. The GRAL offers a GralInfoBox which is represented by a sub window or only by a special panel content inside the given window. It uses a browser (in SWT available) to show also content from the internet, or local html sides.
For a context sensitive help all widgets have a reference able to set: Gralwidget.setHtmlHelp
3. Positions, syntax, possibilities
see also Approaches: Position and size of widgets
3.1. Positions as user arguments in Java programs
Generally they are two systems to define positions:
-
textual, per String, especially in scripts, but also in programs.
-
per numbers, either as float number or as integer for fundamental position.
The string form from 2010 was basically only used in the scripts, translate to numbers. But later the parsing of the textual given positions is integrated in the basic capability of Gral, so it can be used for programming too. It is a simple understandable form. The next idea was, combination of name of the widget with the position on creation, with the writing style:
"@panel,12+2, 20+16 =textxyz"
A position as reference for a widget can be set immediately, either with string or numerical:
GralPos refPos = new GralPos(gralMng); // should know the gralMng refPos.setPosition("@myPanel,12+2, 20+16"); refPos.setParent(myPanel); refPos.setPosition(null, 12, GralPos.size + 2, 20, 36, 'r', 0);
Both sets the given position. But the other variant is: Set the position with using for a new Widget:
GralPos refPos = new GralPos(gralMng); // should know the gralMng GralTextField wdg1 = new GralTextField(refPos, "@myPanel,12+2++, 20+16=textfield"); GralTextField wdg2 = new GralTextField(refPos, "textfield2");
In this construction the refPos
is changed while the Widget is created,
regarding the position String on creating the Widget.
The next TextField have the proper position below the wdg1
because of the increment in line
and the not specified new position. The position will be incremented on next usage.
This approach comes firstly from the script programming,
but it is also usable and recommended for Java programming.
Sometimes the refPos
should not just changed by usage. This is especially
if the refPos
is a really static reference for a comprehensive widget
and all positions should follow exact this reference.
Then it should be written:
// refPos given as Parameter
refPos.setAsFrame();
GralTextField wdg1 = new GralTextField(refPos, "@+2+2, +20+16=textfield");
GralTextField wdg2 = new GralTextField(refPos, "@+4+2, +20+16=textfield2");
The refPos
remains its value though usage for the widget construction.
All positions are referred to the original given refPos
.
But of course then the capability to increment the position for the next widget cannot be used.
Any widget should have an absolute position regarding to the unchanged refPos
.
See org.vishia.gral.base.GralPos#setAsFrame()
3.2. Position writing style in a gral script
The appearance of a graphic can be given with a script using org.vishia.gral.cfg.GralCfgZbnf. The writing style of positions in the script regards a stinting short style to give positions, because a hand written script should not cause a lot of calculations for positions by the writer. It should be simple. The syntax of a position in the gral script is given in the variable {@link #syntaxZbnf}. The method {@link #setPosition(CharSequence, GralPos)} uses that syntax.
Generally a widget is defined in the script with the following example (see also chapter Possibilities to determine the graphic appearance and functions via a script)
@tab1, 2+2++, 2+20=input: InputField(); input2: InputField();
It means on the tabbed panel in tab1
an input field with the given symbolic name should be placed
on the given position. The next input field is below, because the ++
as line increment is given
on the widget before.
This is simple and obviously. Note that the script is only for one panel in one window, which may be tabbed.
But see in next chapter, also relative positions can be used (regarding to the position before), and positions from right and sizable and proportional to the panel size, as also in Java programmed given positions.
-
@myPanel, 5..7, 8..-1
: This is a full given absolute position. The end column is given with a negative value. It means that the end column is related to the right border of the panel. -
@myPanel, 5+2, 8+10
: This is a full given position using the size. The element should be placed from line 5 to line 7 and from column 8 to 18. The range is given as size. -
@7-2, 18-10
: The panel isn’t given, so the panel of the last position, in the script in order of text, is used. The positions are exactly the same, from 5 to 7 and from 10 to 18. But because the size is given as negative value, the position value is the bottom line and the right column. A user may place elements with a common bottom line. The this form can be used. -
`@7-2, 10+181`: This is the same position too. The '' operator after size means, that the next element is positioning right side after the current in distance of 1 unit (distance feature TODO).
-
@-3,+4
: The position isn’t given yet. It means, the position of the last element is used. Because the size is negative, the bottom position of the last element is used. Because the last position is designated with '++' for column, the column value of position is the right value and the current element is placed right hand from the last one. This is an example of a button right from a text. The button is some times greater (3 units) in relation to the text (2 units), but they have the same button line. -
@,+5
: Here the line isn’t specified. It is taken from the last position: bottom line 7 and height 3. The column isn’t given, it is taken form the last: Because the column position is cumulated, it is the 22 yet. -
If no position is given, the position is the next position. In this example @7-3,27+5.
-
@,&2+10
: The ampersand determines a relative position related to the last one. In this example the new column is 24 to 34. There is 2 units space. -
@ $2-2,$+20
: The dollar determines a relative position related to the last absolute given position. In this example it is line 7 and column 10. The new position is calculated with that values to line 9. The column 10 is the same column related to the last given absolute. This kind of specification allows determining some positions in lines and columns, whereby the absolute position is given only one time. (feature TODO) -
@ %50-2,%10..%90
: The positions are calculated from the size of the panel in percent. (feature TODO)
3.3. Position definition forms
Positions may be given with absolute values of grid units regarded to the actual panel, or also relative to the position before or a related position, and also (not full ready yet) as The position is given in form 'from..to' for the line and column or in form 'from' and 'size'. The size may be given positive or negative. A positive size is counted from top or left to bottom or right. It means that the 'from' position is top or left. But a negative size is counted from right or bottom and the 'from' position is the right or bottom column and line.
3.3.1. Absolute position from left top
-
@myPanel, 5..7, 8..18
: -
refPos.setPosition(5,7,8,18);
This is a full given absolute position. The element should be placed from line 5 to line 7, it is a height of 2 grid units, and from column 8 to 18. The vertical position 7 is the bottom line, the column 18 is the right column exclusive. The horizontal size is 10.
The same can be done also with fractional values:
-
5.2..7.2, 8.5..18
: -
refPos.setPosition(5.2f, 7.2f, 8.5f, 18);
The Positive numbers are in range 0…about 100..200 up to 1000: Grid Unit from left or top.
3.3.2. Absolute positions from right or bottom
Negative numbers or the 0 as right position defines positions from right or bottom
-
2..4, -10..-0.5
: -
refPos.setPosition(2, 5,-10.0f, -0.5f);
In this case a widget is positioned right oriented, but from top. If the panel is resized, the widget remains related to the right border.
3.3.3. Absolute positions between left and right or between top and bottom, resized widgets
-
2..4, 2..-0.5
: -
refPos.setPosition(2, 5, 2, -0.5f);
This widget is positioned in horizontal direction over the full panel, with 2 grid units distance from left (positive value) and only a half grid unit to the right border. On resizing the panel this widget is also resized in horizontal spread. Proper for long text fields.
-
4..0, 2..-0.5
: -
refPos.setPosition(4, 0, 2, -0.5f);
This may be proper for a table which fills the whole panel, only 4 grid units distance from top
(where a text field may be placed for example).
The value 0
for the right and bottom position means the right and bottom border.
3.3.4. fractional positions
This is interesting and todo yet. Also resized with the size of the widget.
3.3.5. Given size instead spread for position
Often not the spread is in focus, but more the size. Because the size in vertical direction influences immediately the font size. For example for the same text field as above:
-
2+2, 2+10
: -
refPos.setPosition(2, GralPos.size+2, 2, GralPos.size + 10);
The definition of the size instead the end position 4
is more obviously.
Of course also float values can be used for fine positioning.
-
4-2, 2+10
: -
refPos.setPosition(4, GralPos.size-2, 2, GralPos.size + 10);
A negative size value determines the field to the bottom line, here 4
.
The advantage is given if more widgets should have the same bottom line.
3.3.6. Reference positions (referenced to the last one)
It is important for some positioning not calculated (manually) the absolute positions, instead given relative ones. This is especially helpfully for scripting but also programming.
The first position should be usual absolute, then relative to the position before:
-
+2+2, +0+10
: -
refPos.setPosition(GralPos.refer+2, GralPos.size+2, GralPos.same, GralPos.size +10);
This position is related to the given before, in the next line (vertical distance 2) with given size and in the same column (x position).
-
+-2+2, +0+10
: -
refPos.setPosition(GralPos.refer-2, GralPos.size+2, GralPos.same, GralPos.size +10);
A negative value for the distance should be written as +-2
because the +
is necessary
for the semantic 'refer'.
3.3.7. Autoincrement positions
This is usual interesting for scripting to reduce writing and calculation effort, but also for programming:
-
2+2++, 2+10
: -
refPos.setPosition(2, GralPos.size+2, 2, GralPos.size +10, 'd', 0);
This means the same as above, given with size (also with absolute position possible), but the next widget is placed automatically to an auto incremented position, it does not need a position. Look for a script:
-
Negative number in range 0, -1…about -200..-200 up to 1000: Gral Unit from right or bottom. 0 for lineEnd or columnEnd means the right or bottom.
-
{@link #same} or {@link #refer} added with a number in range of -1000..1000: This given position refers to the parent position with the given distance. same and refer is equate, the difference is in semantic only. Use {@link #same} without distance, use {@link #refer} +/- distance. If {@link #same} or {@link #refer} is used for the line or column, and the second position is given with '{@link #size} - size' then the bottom or right value of the parent is referred.
-
{@link #size} + number applied at lineEnd or columnEnd: The size is given instead from..to. Because the size value is positive, the line and column value is left or top.
-
{@link #size} - number applied at lineEnd or columnEnd: The size is given negative. The absolute value is the size. Because the size is negative it is measured from right to left respectively bottom to top. It means the given line and column is the right or the bottom line. If the position is given using {@link #same} or {@link #refer}, the related end position is used.
-
{@link GralPos#next} and {@link GralPos#nextBlock}
-
as width or height or as percent value from the panel size.
Fine positions are given always from left or top of the fundamental positions. For example a value -1.3 means, the widget is placed 1 unit from right, and then 1/3 inside this unit. This is 2/3 unit from right. A value for example -0.3 is not admissible, because -0 is not defined.
3.3.8. Absolute positions from right or bottom
or in related to the last or parent position. In that cases the constant values are added to the number, see the following list.
3.4. Resize organization of widgets
The resize starts any time from a Window resize listener. This listener (org.vishia.gral.swt.SwtSubWindow#resizeListener for SWT implementation) calls firstly …base/GralWindow.WindowImplAccess#resizeWidgets(clientArea). For the associated mainPanel of the window and all children then org.vishia.gral.base.GralWidgBase.GralWidgComposite#resizeWidgets(parentPixelRectangle, recursion) is called.
This operation organizes recursively resizing for all widgets, which can be also panels or also composite widgets (consist of more as one member widgets). The size in pixel is calculated controlled by its position values in GralPos. That can contain proportional positions or positions from the right side, and also composite widgets consist of more as one GralWidget but without a panel in the implementation level (uses a part of a implementation panel).
For specific resize approaches the override able operation org.vishia.gral.base.GralWidgetBase#resizePostPreparation() can be do widget-specific preparations after resizing the implementation widget and before redraw. For instance on a table this controls the number of visible lines.
4. Possibilities to determine the graphic appearance and functions via a script
4.1. Small example
This is the same example shown in chapter Simple example Window with text field and button programmed in Java code with a script
…TODO
5. Threads and callback operations in user threads
5.1. SWT
The threading is most consequently on SWT graphic. A simple program using SWT needs only one thread, the given main-thread:
shell.open (); while (!shell.isDisposed()) { while( display.readAndDispatch() ); // dispatch so long as events found boolean bSleep = true; while( (order = queueOrdersToExecute.poll()) !=null) { order.doExecute(); // the order may send an event to the graphic bSleep = false; } if(bSleep) { display.sleep (); } } display.dispose ();
This code snippet shows the activity in the graphic thread, maybe the only one thread in the life time of a displayed window.
The queueOrdersToExecute
is an additional element of the Gral, it executes changing of the graphic appearance
which is forced in other threads using this Gral specific event queue.
The other source of graphic events are the callback operations of the graphic itself, not shown here but attached to the widgets.
For the SWT Gral implementation three threads are present:
-
The main() thread, which can do any things independent of the graphic, but should wait for
shell.isDisposed()
-
A timer thread, see following sub chapter.
-
The Swt graphic thread which executes the above shown loop for dispatching events.
SWT throws an exception if widgets are used in a faulty thread. The widgets can only touched in the SWT graphic thread.
5.2. AWT, Swing(?)
AWT was the first graphic implementation in Java from 1995. In that time the idea of {J]synchronized
was present
as helper overall. The problem with longer waiting times for the operation system scheduler on the synchronization points
were not recognized at that time.
-
The AWT graphic thread is a specific thread which cannot used for user programming. This is the first thread. This thread also executes the callbacks, of course.
In comparison to SWT, in SWT the graphic thread is programmed by the user. The user must only call the SWT-specific
display.readAndDispatch()
. -
The main thread, not used for graphic is the second one.
-
The 3th thread is the timer thread.
-
And now because of an unchanged timer thread the Gral graphic thread is also present. It does the same as in SWT, only does not call
readAndDispatch()
because this is done by the AWT specific graphic thread. Both threads works concurrently, because it is not possible to change a widget outside of a callback operation in the intrinsic AWT graphic thread. But for AWT it is okay, it works withsynchronized
(which needs more calculation time).
5.3. Events to the graphic thread
The events from the application to the graphic thread (as well as also from the timer thread) are all organized
by ordinary GralWidget operations, such as setText(…)
, changeColor(…)
or adequate.
The queue to store this event is a java.util.concurrent.ConcurrentLinkedQueue
,
it means it is thread safe by the atomic access approach which is faster than synchronized
.
The type of events are stored are type of GraphicTimeOrder which are derived from org.vishia.event.TimeOrder. An instance of that refers to an virtual given parameterless void operation
executeOrder()
.
Instances of such events are created statically on instantiation of the appropriate widget.
They are not created on demand on the heap, as often usual. For example inside the GralWidget
:
// in org.vishia.gral.base.GralWidget:
/**This time order calls the {@link #redrawGthread()} in the graphical thread for this widget.
* It is used with delay and wind up whenever {@link #redraw1(int, int)} with an delay is called.
* If its executeOrder() runs, it is dequeued from timer queue in the {@link GralGraphicThread}
* till the next request of {@link #redraw1(int, int)} or {@link #redraw()}.
*/
private final GralGraphicOrder redrawRequ = new GralGraphicOrder("GralWidget.redrawRequ", this.gralMng){
public int processEvent ( EventObject ev) {
// this.gralMng.log.sendMsg(GralMng.LogMsg.evRedraw, "redraw " + super.sInfo);
if(GralWidget.this._wdgImpl !=null) { GralWidget.this._wdgImpl.redrawGthread(); }//Note: exception thrown in GralGraphicThread
return 0;
}
@Override public String toString(){ return GralWidget.this.name + ":" + GralWidget.this.name; }
};
The event is used for redraw, the instance of the event is given, it is not necessary to use it twice. If the event is not necessary in the moment, it remains as instance but it is not in a queue. This is also the concept of the org.vishia.event.TimeOrder.
5.4. The timer thread
This is a graphic independent thread, but helpfully. See ../../docuSrcJava_vishiaBase/org/vishia/event/EventTimerThread.html
Actions to change the graphic appearance are either forced in callback operations of the widgets in the graphic thread, or they are forced in other threads. In both case the question should be able to ask: "Should the reaction be executed immediately, so fast as possible?" For simple actions, change a text in a text field or such, "yes, should be done, why not__". But if the actions are more complicated, for example pressing a button, then some calculations are started, and the results shoud be present in tables or in a curve view, it is not necessary that any action forces change of the graphic. Because the eyes of a human are not so fast. A time of 100 ms may be often possible and not disturbing. If a value of a text field comes from a communication with a frequency of 1/10 ms, then not only any value should be shown. It is possible to suppress fast values (or maybe build a middle value, but it’s a question of the application requirements). If a new value comes,
redraw()
need to be call, to present the value in the graphic appearance.
But redraw
in a time cycle of 10 ms or 1 ms needs unnecessary CPU and graphic calculation time.
Hence it is better to write
redraw(100, 100);
The first value is the delay = 100 ms, the second value is the last action.
Supposed, a new redraw(100, 100)
comes in a lesser time, the redrawing should not be deferred in the future,
again after 100 ms, it should be done from the first redraw in just 100 ms from it.
Both values are typically the same.
On repeated value setting and redraw(100)
calls last not least any 100 ms the last given value is shown.
It needs graphical calculation time only in a 100 ms step, independent of the setValue(…)
and redraw
calls
in the other threads.
That is managed by the timer thread. The timer thread works outside of the graphic. In the event executing operations Events for the graphic thread are sent.
The timer thread and the event system is documented in
5.5. GralUserAction, unified callback
The callback operation types from the implementing graphic widgets are equalized to a virtual operation
exec(…)
in an instance of type GralUserAction.
The original callback operations invokes that ones.
It can be well programmed on user level without knowledge of the graphic specifics.
For example look on a button callback:
//see test_vishiaGral/org/vishia/gral/test/basics/Test_SimpleTextButton.java public class Test_SimpleTextButton { GralUserAction actionClean = new GralUserAction("actionClean") { @Override public boolean exec ( int actionCode, GralWidget_ifc widgd, Object... params ) { Test_SimpleTextButton.this.wdgInputText.setText(""); return true; } }; GralUserAction actionButton = new GralUserAction("actionButton") { @Override public boolean exec ( int actionCode, GralWidget_ifc widgd, Object... params ) { Test_SimpleTextButton.this.actionButton(); return true; } };
It is recommended to call an operation outside of this anonymous class as shown. This operation is called in the graphical thread. If the operation may need a longer time, it should send an event to or weak up a user operation in any other user thread. This is recommended to prevent longer times in the graphic, where the graphic looks like frozen.
5.6. Actions on widgets in any user thread
The operations for the GralWidget
and all derived widgets can be usual done in any user thread.
With that operations firstly only data in the GralWidget
instance are changed.
In specific fields of GralWidget
it is remarked what is changed (color, text etc).
Then a redraw(…)
is invoked. This activates the event described in the chapter above Events to the graphic thread
and hence the event operation which calls the correct _wdgImpl.redrawGthread()
for the implementation level.
In this redraw operation it is tested which is changed, to transport the information
from the GralWidget instance to the implementation graphic widget. This occurs in the graphic thread,
hence is is proper.
It seems to be that all actions are done twice, firstly change data in the GralWidget, then in the implementation widget. That is true. But it is efficient, because setting data in ordinary Java instances are not an calculation effort. The possibility to separate actions from the graphic thread, which may hang (needs waiting times etc), this is the advantage.
5.7. redraw()
The redraw of the implementation widget is anytime executed in the implementation of any widget defined as as gral.base.GralWidgImplAccess_ifc#redrawGthread(). This execution is done in the graphic thread.
The gral.base.GralWidgImplAccess_ifc#redraw() and also
redraw(delay, latest)
can be called in any thread.
It should be called for one widget usual if any data of a widget are changed.
How both works together?
If you are in the graphic thread and redraw(0,0)
is called, means immediate, without delay,
then the redrawGthread()
for the appropriate implementation widgets are called immediately.
This is a special case.
If you call redraw()
then this redrawing is delayed with the constant time of 50 ms, max after 100 ms.
This is because some other changes may also be done in the same thread in a less time after (less milli seconds).
Then only one redrawGthread()
is called for the implementation graphic, which saves calculation time.
For example you change a text for a widget, and also a color. Then both changes invokes redraw()
.
But only one redrawGthread()
is necessary for the widget with the given text and color.
The 50 ms delay are enough to receive both changes, and less enough to visible it fastly.
The delay is organized by the [J]'GralGraphicTimeOrder' GralWidget#redrawRequ, see also chapter Events to the graphic thread.
5.7.1. DynamicData and whatIsChanged in a GralWidget
The GralWidget.DynamicData contains
all dynamic data of a widget (which are usual changed for the appearance.
If this data are changed for example by calling myWidget.setTextColor(colorGreen)
then only the new reference to the color instance is entered in the dynamic data.
For the time being nothing is changed in the graphic appearance.
But one bit is set in the GralWidget.DynamicData#whatIsChanged field. This field is an atomic integer field. The atomic access ensures, that multiple threads can change the bits. So one thread can change the text, and another thread can change to color, or position, or what else concurrently. The values are not thread safe. It means if one thread changes the text, and after them another thread changes also the text, what about. Of course the last text wins.
5.7.2. Organization of redrawGthread with DynamicData
The redrawGthread
operation need be implemented in the different widget types in a special kind.
Look for example in the SwtTextFieldWrapper
:
@Override public void redrawGthread(){
int catastrophicalCount = 0;
int chg;
if(this.textFieldSwt !=null){ //do nothing if the graphic implementation widget is removed.
GralWidget.DynamicData dyda = dyda();
while( (chg = getChanged()) !=0){ //widgg.dyda.whatIsChanged.get();
if(++catastrophicalCount > 10000) {
throw new RuntimeException("acknowledge failed");
}
if((chg & chgText) !=0 && dyda.displayedText !=null){
this.textFieldSwt.setText(dyda.displayedText);
}
.....
if((chg & chgColorBack) !=0){
this.textFieldSwt.setBackground(this.swtWidgHelper.mng.getColorImpl(dyda().backColor));
}
this.textFieldSwt.redraw();
//textFieldSwt.
acknChanged(chg);
}
}
The getChanged()
returns the GralWidget.DynamicData#whatIsChanged which’s bits are set on change the data.
Here it is tested.
The test repeats in a while
loop because during execution on this operation some new changes may come in.
The accident of that is less, but possible. The catastrophicalCount
is only a general check used for all while loops.
All relevant bits are checked here (not all is shown).
At last exact the checked bits are acknowledged.
Another implementation, SwtValueBar
is very more simple, but:
@Override public void redrawGthread(){
this.widgetSwt.redraw();
}
It calls only the original necessary redraw()
of the SWT Control. No more. But how the data are dealt?
The Swt widget is not a standard, but a
private class SwtBarCanvas extends Canvas
{
SwtBarCanvas()
{
super(SwtMng.getSwtParent(SwtValueBar.this.widgg.pos()), 0); //Canvas
SwtValueBar.this.wdgh.widgetSwt = this;
addPaintListener(this.paintListener);
}
final PaintListener paintListener = new PaintListener(){
@Override
public void paintControl(PaintEvent e) { redrawRoutine(SwtBarCanvas.this,e); }
};
}
It has a specific redrawRoutine(…)
routine (see in sources), which regards the given DynamicData
of the GralValueBar
immediately itself.
5.8. setFocus()
Any user thread can call setFocus()
to any widget on Gral level to get it in focus of usage.
For the implementation level it means, that not only the appropriate widget should get the focus
(in Swt: Control#setFocus or Control.#forceFocus), but all parent widgets should be visible
if they are not, and also a primary widget of a focused panel should be focused.
If the focused widget is in a yet hidden tab of a tabbed panel the tab should be activated,
and also a hidden window should be get visible. That is all done in the Gral adaption.
If the focus was set with user action (usually the mouse selects a field)
then a focus event in the implementation graphic should catch this action
and should call
gral.base.GralWidgetBase.setFocus(gained).
This operation marks in the Gral widget the property
gral.base.GralWidgetBase.hasFocus()
which can be used to decide some actions on handling with this widget, for example copy to clipboard by selecting a field
or evaluate the content of a field while selecting not only with the mouse.
Any forced action can quest hssFocus()
and with this information decide about reaction.
If one widget has gained the focus, also all parents (comprehensive widgets, panel, window) are set in the state
hssFocus()
because just its actions should decide also. For example if a cell in a table was focused,
an algorithm of the widget containing the table can evaluate the table’s content.
5.9. getting the focus
If an user handling activates the focus of another widget (by mouse click, by selection keys etc.), then the implementation graphic invokes an event focusGained or also focusLost for the other widget which losses the focus.
8. Overview about the basically classes for Gral
8.1. The GralMng
A graphic needs one instance of the gral.base.GralMng It contains
-
all widgets and panels sorted by name to find out all widgets.
-
a reference position to build the graphic
-
some common operations.
-
The timing thread, see chapter Threads and callback operations in user threads
8.2. GralWidget base classes
GralWidgetBase
The class gral.base.GralWidgetBase is the base class for all widgets, also for comprehensive ones. It contains
-
The
final String name
field of the widget. -
a position,
final String _wdgPos
, accessible viafinal String pos()
, see chapter Positions, syntax, possibilities. For comprehensive widgets the parts have sub positions related to this position. -
the reference to the The GralMng
final String gralMng
-
some abstract operations, should be implemented also for comprehensive widgets.
-
the final operation
checkImplWidgetCreation(…)
and the abstract operationcreateImplWidget_Gthread()
. The last one is implemented in a standard form for [J]'GralWidget', which calls the gral.base.GralMng.ImplAccess.createImplWidget_Gthread(GralWidget wdg). This is the operation, implemented in the implementation manager, to create the implementation widget with the given properties of the Gral widget.
The operation createImplWidget_Gthread()
is or should be overridden by comprehensive widgets,
calling the appropriate gral.base.GralMng.ImplAccess.createImplWidget_Gthread(GralWidget wdg)
for all contained widgets on Gral level. Hence no specific implementation code is necessary.
The operation createImplWidget_Gthread()
is overridden by the GralPanelContent
and GralWindow
to build the contained widgets.
GralWidget
This class gral.base.GralWidget is the base class for that all widgets, which have an counterpart in the implementation level.
Additionally that basic class contains common properties of the widget such as
-
DynamicData
: All data of the widget accessible thread independent. -
Some more properties such as
contextMenu
,sDataPath
,variable
in a common kind, which are necessary for most widgets for most applications. The fields arenull
if unused. -
the adequate operations.
-
The implementation of
createImplWidget_Gthread()
which creates the implementation widget.
9. Overview over Widgets, simple and comprehensive
9.1. GralWindow
A GralWindow is a window of the application. An application can have not only the main window but also some sub windows. All of the are type of gral.base.GralWindow
The first window which is created in a Gral initialization is automatically the main window. Only the main window is unconditionally set to visible, all other windows are set to invisible per default. This is organized by the gral.base.GralMng#runGraphicThread():
protected void runGraphicThread() { long guiThreadId1 = Thread.currentThread().getId(); // should set firstly because in createImplWidget_Gthread it is necesarry. this.graphicThreadId = guiThreadId1; this._mngImpl.initGraphic(); // inits the basics of the Graphic only, not the GralWidgets. //add important properties for the main window, the user should not thing about: this.windPrimary.windProps |= GralWindow.windIsMain | GralWindow.windHasMenu; if((this.windPrimary.windProps & GralWindow_ifc.windMinimizeOnClose)==0) { //it it should not be minimized, then close, never set Invisible, because it is not possible to set visible again. this.windPrimary.windProps |= GralWindow.windRemoveOnClose; } //======================================================= On start create the implementation of all yet known GralWindow for(Map.Entry<String,GralWindow> ewind: this.idxWindows.entrySet()) { GralWindow wind = ewind.getValue(); //boolean bVisible = wind == this.windPrimary; // //======>>>> wind.createImplWidget_Gthread(); // creates all widgets of the window. //wind.setWindowVisible( bVisible ); }
9.2. GralTextField, GralTextBox Text show and input fields
A field to show a text (values) or input a text has a frame with a specific background color and with a dedicated size, also for a show-field. In opposite a simple text output (usual named as Label) has not a frame and not a background color. Secondly a text field can have a label to explain what is it.
image:../../img/todo
Such text fields as show fields can be used especially to show process values as numerics with units etc. as for example used in the Inspector GUI or a operation and monitoring GUI.
9.3. The GralTable
The gral.base.GralTable is a basic widget (which has its specific implementation, see gral.swt.SwtTable) But this implementation does not follow the offer of a table widget of the operation system.
Why not? Some operation system’s table widgets have a too less capability. The additonal capabilities of a GralTable are:
-
Not only the whole line is marked with a color, but also only the selected field.
-
Any column (all fields in the column) can have a specific context menu.
-
Lines of the table can be folded and unfolded, if they are marked as children of a parent’s line. This is similar of the capability of a tree (with sub nodes or just children). It means the table can be also used as tree view, but with not only the tree, with more columns.
The basically decision to build a table was also in 2010:
-
A table consist of some simple text fields from the implementation graphic.
Depending of the visible size of the table not all text fields are visible.
The maximal visible length of a table can be determined by construction.
Usual no more than 100 fields should be used (for a long table), or for example only 10 fields per column
if the table should anytime be shown only by a simple section.
But the data length of the table (number of existing lines) can be any size.
The table lines are not selected by such as a panel with vertical selection slider, which contains a lot of lines.
The selected visible range of the table is always presented in the few text fields.
There content is changed by selection.
It means the content of the table is not persistent present in the implementation fields,
it is present in the data of the GralTable (in the rootline
and all of its nodes
of type gral.base.GralTable.TableLineData.
The nodes are a structure of vishia.util.TreeNodeBase
which is a basically node structure.
On showing a part of the table content the appropriate
gral.base.GralTable.TableLineData.cellTexts are copied in the appropriate text fields of the implementing graphic,
as also the gral.base.GralTable.TableLineData.cellColorBack or the general colorBackMarked
etc. depending of the state of the line.
If the table content is shifted, the cellTexts
etc. are copied just newly.
This copy process from internal Java data to the implementation widgets needs not too much time.
9.4. A canvas
To show any what, lines, curves, polygons, also text, a canvas can be used. It’s for "free drawing".
Commonly, the canvas has its draw-background operation which can be specifically programmed to draw all what is possible with the implementing graphic. In Eclipse SWT it is https://help.eclipse.org/latest/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Canvas.html#drawBackground(org.eclipse.swt.graphics.GC,int,int,int,int). You can use the capabilities of the GC (Graphic Control) class https://help.eclipse.org/latest/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html to draw lines, figures and text.
But programming in eclipse SWT immediately is specific.
If you do the same in AWT, you have the capability of java.awt.Graphics
with similar functionality,
but a little bit (or more) different: https://cr.openjdk.java.net/~iris/se/17/latestSpec/api/java.desktop/java/awt/Graphics.html.
If you use instead Python, or the old pearl, or whatever, similar the same but a little bit other and specific.
And if you have dynamic data outside of the graphic, you have problems to write proper thread safe stuff.
The Gral concept gives another answer:
../../docuSrcJava_vishiaGui/org/vishia/gral/base/GralCanvasStorage.html is a graphic independent storage
for the appearance of the graphic.
It consists of Figure
and each figure has any lines, areas, text etc.
This data are independent of all implementing graphics, only coordinates and GralPos
and GralColor
.
You can modify them in any thread. To apply to a graphic you have two possibilities:
The canvas is represented either with a specifc ../../docuSrcJava_vishiaGui/org/vishia/gral/widget/GralCanvasArea.html or it can be a part of a ../../docuSrcJava_vishiaGui/org/vishia/gral/base/GralPanelContent.html.
The GralCanvasArea
as widget can be placed beside other widgets to show specific forms as vector graphic
instead using a image. Also animated content is possible.
A normal GralPanelContent
contains standard widgets, but can also contain free lines and figures.
9.4.1. Figures and FigureData
Sometimes a figure should be drawn more as on time in a canvas area. The figure is the same, but the position and maybe also a back color may be different. To optimize and prevent too much data (which should be programmed) a class
contains the primary appearance of a figure. It can contain any amount of FigureData deviating instances, which are
You can create firstly (example)
final GralCanvasStorage.FigureDataSet fgdMyFigureX = new GralCanvasStorage.FigureDataSet();
then fill the figure with appearance:
GralColor color = GralColor.getColor("bk");
this.fgdMyFigureX.addPolyline(color).point(0, 0).point(0,2); // |
for(int ix = 0; ix <20; ++ix) {
this.fgdMyFigureX.addPolyline(color)
.point(ix,2)
.point(ix+1,2) // --+ 20 times gives rectangles for each word.
.point(ix+1,0) // |
.point(ix,0); // --+
}
Then you have defined the appearance of a figure, but not yet the figure itself.
You can use this FigureData
for more as one figure:
this.pos.setPosition("10-2,10+1++");
this.canvas.addFigure("dataWordsMaster", this.pos, this.figData_Words, false);
this.pos.setPosition("10-2,40+1++");
this.canvas.addFigure("dataWordsSlave1", this.pos, this.figData_Words, false);
Now you have two figures with same appearance on different positions, which looks like as:
This are two figures as symbols for a memory area to present data words (after them filled with colors).
To add a color to any box, for this example firstly an array of colors was created. Then figures are created to show the inner color:
for(int ix = 0; ix < 20; ++ix) { // color (content) of the data words slave
this.rxSlave1[ix] = this.canvas.addFigure("rxSlave1-" + ix, this.pos
, new GralCanvasStorage.Fillin("X", this.colorWhite), true);
this.pos.setPosition(",+1");
}
Here the FigureData are created with one instance per figure, because the colors are different. To set a specific color it is called (for one figure):
this.rxSlave1[ixColor-1].data.color = this.serialOut1word.data.color; // move content from rx Port to rx data
Because the figure has only one data element it is addressed immediately with myFigure.data
.
Its color is accessed immediately public (without setter, maybe changed in future).
The appearance all in all is then (snapshot from the running graphic):
9.4.2. static and dynamic figures
A GralCanvasStorage
contains figures. On creation they can be remarked as dynamic or not
with the 4th argument of the both constructors of
-
One for the static figures, redrawn after clean,
-
one for dynamic figures, redrawn whenever an update occurs maybe only for one of the figure.
The GralCanvasStorage
contains a list of Figure
.
Some of them can be marked as dynamic by