{"id":146,"date":"2020-12-16T23:52:56","date_gmt":"2020-12-16T21:52:56","guid":{"rendered":"http:\/\/www.mbarsinai.com\/blog\/?p=146"},"modified":"2021-04-10T20:22:09","modified_gmt":"2021-04-10T18:22:09","slug":"panelmatic-101-reprint","status":"publish","type":"post","link":"https:\/\/www.mbarsinai.com\/blog\/2020\/12\/16\/panelmatic-101-reprint\/","title":{"rendered":"PanelMatic 101 (Reprint)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Long long time ago, I can still remember, how I used to do Java Swing applications for living. Swing has a lot of strong points, and I really admire its design and architecture. When one needs to make a desktop application in Java, it still rocks. Of course, Swing is not bereft of annoyances. One of these is creating panels with complex layouts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To my pleasant surprise, I had some Swing-related projects recently. So I revived an old open-source library of mine, which was originally hosted on Kenai.com (yes, that was <em>that<\/em> long ago). It was called &#8220;PanelMatic&#8221;, and it enjoyed a mild success back then.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/codeworth-gh\/PanelMatic\" target=\"_blank\">PanelMatic is back<\/a>. It&#8217;s using Java 11, it&#8217;s on <a href=\"https:\/\/github.com\/codeworth-gh\/PanelMatic\">GitHub<\/a>, and it&#8217;s on <s>its way to<\/s> Maven Central. Below is an slightly modified intro that was published originally at Java.net (yes, that was <em>that<\/em> long ago).<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">PanelMatic 101<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every Swing developer knows this feeling: you&#8217;ve got the design of a UI panel. It&#8217;s the &#8216;right&#8217; design. It &#8216;works&#8217;. It&#8217;s what the user would expect. Hell, it&#8217;s even what you would expect had you been the user. But it is going to be an awful lotta&#8217; coding to lay it out in Swing &#8211; even before you take into consideration issue like panel re-sizing and localization.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Some developers nest numerous panels to get it working. Some try to &#8220;fake it&#8221; using the&nbsp;<code>null<\/code>&nbsp;layout (please, don&#8217;t). Others try to fit everything into a single panel and use the&nbsp;<code>GridBagLayout<\/code>&nbsp;&#8211; this involves quite a bit of trial-and-error, as can be seen in&nbsp;<a rel=\"noreferrer noopener\" href=\"http:\/\/madbean.com\/anim\/totallygridbag\/\" target=\"_blank\">this<\/a>&nbsp;documentary. Some even turn to graphical UI builders, which does not support dynamic panel creation, and whose designs are hard to maintain.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;d like to suggest a slightly better solution: PanelMatic.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"625\" height=\"297\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/sketch-code-gui.png?resize=625%2C297\" alt=\"\" class=\"wp-image-148\" srcset=\"https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/sketch-code-gui.png?w=803&amp;ssl=1 803w, https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/sketch-code-gui.png?resize=300%2C142&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/sketch-code-gui.png?resize=768%2C364&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/sketch-code-gui.png?resize=624%2C296&amp;ssl=1 624w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><figcaption>Figure1: Sketch =&gt; Code =&gt; GUI<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">PanelMatic allows swing developers to create common UI panels with ease. Panels are built top-to-bottom (or, more precisely, on the page axis). There is an intuitive connection between the way the code looks and the way the created panel will look. Components can be added to the panel with a label and\/or an icon (lines 3-7 in Figure 1), or alone (line 9). By default components stretch to occupy all the space they get, but this can be changed using modifiers (lines 9, 10).&nbsp;<code>L_END<\/code>&nbsp;(stands for &#8220;line end&#8221;) and&nbsp;<code>GROW<\/code>&nbsp;(stands for &#8220;grow&#8221;) are statically imported constants, and are in fact full-blown objects that implement the&nbsp;<code>BehaviorModifier<\/code>&nbsp;interface &#8211; so you can create your own modifiers if you need &#8217;em. Client code can add headers (lines 2, 8) and flexible spaces (not shown). The default implementation uses a pluggable component factory to create all the extra components involved (e.g.&nbsp;<code>JLabel<\/code>s), so you can customize them when the defaults won&#8217;t do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As you&#8217;ve probably guessed, panels are built by builders that are obtained by invoking a static method on the&nbsp;<code>PanelMatic<\/code>&nbsp;class. PanelMatic is designed around an API\/SPI approach &#8211; all the client code gets to hold is an interface, so the implementation can be changed without affecting client code at all. Builders are pooled, so you don&#8217;t have to think twice before asking for one. You can create your own implementation &#8211; just implement the service provider interfaces, and point the&nbsp;<code>PanelMatic<\/code>&nbsp;class to it. This can be done either by code or via a system property.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So this is PanelMatic. Use it to create common UI panels quickly, and use your freed up time to go to more meetings and catch up on some quality powerpoint presentations. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We could stop here, but we&#8217;d be missing half the fun.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Beyond Layout<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">PanelMatic&#8217;s approach to building UI panels allows for some surprising improvements over normal layout-based code. Here are some of my favorites.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Customizers, or Listening to All Components<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Components are passed to the builder, and eventually get added to the produced panel. But before they are, they pass through a chain of&nbsp;<code>PanelMaticComponentCustomizer<\/code>s, or &#8220;customizers&#8221;, for short. Each builder has two customizer chains &#8211; one that applies to all the panels it builds, and one for the current panel being built. The latter is disposed after the&nbsp;<code>get()<\/code>&nbsp;method is called. Customizers have a&nbsp;<code>customize<\/code>&nbsp;method, which gets an returns a&nbsp;<code>JComponent<\/code>. This allows client code to apply uniform customizations to all components in the created panel, or in the application in general. These customizations can be used for, e.g:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Changing the background of the focused component<\/li><li>Automatically wrap&nbsp;<code>Scrollable<\/code>s in a&nbsp;<code>JScrollPane<\/code><\/li><li>Listen to all components in a panel<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s look into the latter example. A very common requirement is to have a &#8220;save&#8221; button enabled if and only if the user changed some data on the screen. This involves listening to all components the user can interact with. Normally, this requires a lot of boilerplate code. Instead, we can create a customizer that adds a change listener to every component added to the panel, and then add a single listener to that customizer. Incidentally, PanelMatic comes with such a customizer out of the box:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\n<strong>ChangeDetectorCustomizer cdc = new ChangeDetectorCustomizer();<\/strong>\npanel = PanelMatic.begin( <strong>cdc<\/strong> )\n\t.addHeader( H1, \"Song Details\")\n\t.add(\"Name\", txfName)\n\t.add(\"Album\",txfAlbum)\n\t.add(\"Artist\",txfArtist)\n\t...\n\t.add( btnSave, L_END)\n\t.get();\n<strong>\ncdc.addListener( new ChangeDetectorCustomizer.Listener() {\n\t@Override\n\tpublic void changeMade(ChangeDetectorCustomizer cdc, Object o) {\n\t\tbtnSave.setEnabled(true);\n}});<\/strong>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<code>ChangeDetectorCustomizer<\/code>&nbsp;adds the appropriate listeners to all common swing components, so any change made to any component makes the save button enabled. It also recurses down the containment hierarchy, so changes made to&nbsp;<code>JCheckboxes<\/code>&nbsp;nested in some sub-sub-sub-panel are detected as well.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Localizations<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">PanelMatic&#8217;s&nbsp;<code>PanelBuilder<\/code>s can pass the labeling strings &#8220;as-is&#8221;, like we&#8217;ve seen so far, or use them as keys of a&nbsp;<code>ResourceBundle<\/code>. Resource bundles are set using static methods of the PanelMatic class itself, and affect all builders generated afterwards. The same goes for&nbsp;<code>ComponentOrientation<\/code>. So it takes only two lines of code to move from an English left-to-rigth UI to a right-to-left UI in Urdu\/Hebrew\/Arabic or any of the other RTL languages. That, and someone to translate the texts.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"625\" height=\"255\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/ltr-to-rtl.png?resize=625%2C255\" alt=\"\" class=\"wp-image-153\" srcset=\"https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/ltr-to-rtl.png?w=707&amp;ssl=1 707w, https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/ltr-to-rtl.png?resize=300%2C123&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/ltr-to-rtl.png?resize=624%2C255&amp;ssl=1 624w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><figcaption>Figure 2: Localizations can be easy (as long as you have someone that can write the texts in the target language)<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Building GUI Using Expressions &#8211; Anonymous Panels<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">PanelMatic allows panels to be created and laid out using a single expression. This is contrary to the normal UI construction process, where one first creates the panel, then applies a layout, and then adds sub-components &#8211; each in a statement of its own. The problem with statements is that they cannot be combined &#8211; they just sit there, one after the other, as if in a shopping list.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Expressions, on the other hand, can be nested and composed with other expressions. They are &#8220;first class citizens&#8221; in java, and can appear anywhere. So, one can create a sub panel&nbsp;<em>while<\/em>&nbsp;adding it to a bigger component:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>JTabbedPane tabs = new JTabbedPane();\ntabs.add( \"Tab 1\", <strong>PanelMatic.begin()\n\t.addHeader(HeaderLevel.H1, \"Tab 1\")\n\t.add(\"User Name\", txfUserName )\n\t.add(\"Password\", txfPassword )\n\t...\n\t.get()<\/strong>);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is somewhat similar to Anonymous Classes (those are the classes with no names that are created, say, when you implement an&nbsp;<code>ActionListener<\/code>). As anonymous panels can go anywhere, the below code works:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>System.out.println(\"A panel with two JLabels would \"\n\t+ \"have a preferred height of \"\n\t+ <strong>PanelMatic.begin()\n\t\t.add( new JLabel(\"Label 1\"))\n\t\t.add( new JLabel(\"Label 2\"))\n\t    .get()<\/strong>.getPreferredSize().height + \" pixels.\");<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On my mac, it says:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A panel with two JLabels would have a preferred height of 40 pixels.<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Advanced Customizations<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">PanelMatic takes the &#8220;convention over configuration&#8221; approach. Everything can be customized, but you normally don&#8217;t need to bother with it. When you do, there are several levels of customizations available: one could subclass a&nbsp;<code>PanelBuilderFactory<\/code>&nbsp;(easy) or implement the entire stack (builders and factories) or get to some level in between. The exact details are beyond the scope of this article. Bottom line &#8211; as long as you can build your panel along the&nbsp;<code>PAGE_AXIS<\/code>, you can customize PanelMatic to build them. The client code should not be affected when you switch from the default implementation to yours.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wrapping up<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">PanelMatic is a small library that allows swing developers to quickly create common UI panels. It helps with localizations and customizations and makes the code creating the UI readable and intuitive. It&#8217;s easy to pick up and hard to put down, though I might be slightly biased. Why not give it a go and see for yourself?<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><a href=\"https:\/\/github.com\/codeworth-gh\/PanelMatic\">https:\/\/github.com\/codeworth-gh\/PanelMatic<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Long long time ago, I can still remember, how I used to do Java Swing applications for living. Swing has a lot of strong points, and I really admire its design and architecture. When one needs to make a desktop application in Java, it still rocks. Of course, Swing is not bereft of annoyances. One [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":148,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1],"tags":[19,21,20],"class_list":["post-146","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-java","tag-library","tag-swing"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.mbarsinai.com\/blog\/wp-content\/uploads\/2020\/12\/sketch-code-gui.png?fit=803%2C381&ssl=1","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p3NnQg-2m","_links":{"self":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts\/146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/comments?post=146"}],"version-history":[{"count":5,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions"}],"predecessor-version":[{"id":157,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions\/157"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/media\/148"}],"wp:attachment":[{"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/media?parent=146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/categories?post=146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mbarsinai.com\/blog\/wp-json\/wp\/v2\/tags?post=146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}