Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Best practice/Missing API: SectionStack skinning (Sections

    Hello Isomorphic,

    please see the attached screenshot.
    I want to achieve a similar design. I think it is easily possible, but will need some additional APIs. The areas are:
    1. Spacing between SectionStackSections:
      Here I tried editing CSS and using SectionStack.setMembersMargin(...) (inherited from Layout), which did not have the expected effect.

    2. SectionStackSection iconSize:
      Here I tried messing with the icons width and height tags in Chrome which solves this very easily -> missing API SectionStackSection.setIconWidth() and SectionStackSection.setIconHeight().

    3. Border(-bottom) for SectionStackSection (whether open or closed):
      Least important, can most likely already be faked.


    Could you add those APIs for 4.1p or suggest best practice to achieve the wanted result if it is already possible?

    Thank you & best regards,
    Blama
    Attached Files

    #2
    The simplest thing for all requirements is just to use a custom SectionHeader class via sectionStack.sectionHeaderClass.

    Top and bottom space and border can be achieved with just CSS.

    All base SectionHeader classes extend from StatefulCanvas so they already have properties for controlling the size of the icon.

    Comment


      #3
      Hi Isomorphic,

      I searched the client Showcase source for setSectionHeaderClass but didn't find an example. Also the forum's search does not provide much help, as most of the hits refer come from the SmartClient forums.
      So I went ahead and tried myself. As I directly got a JS error / not showing page in FF26 and Chrome 34 and a JS error in FF-DevMode, I tried with modified builtInDs (using today's v9.1p_2014-05-21), which shows the same JS Error. My BuiltInDS code is as follows.

      BuiltInDS.java:
      Code:
      package com.smartgwt.sample.client;
      
      import com.google.gwt.core.client.EntryPoint;
      import com.smartgwt.client.core.KeyIdentifier;
      import com.smartgwt.client.util.PageKeyHandler;
      import com.smartgwt.client.util.Page;
      import com.smartgwt.client.util.SC;
      import com.smartgwt.client.widgets.Label;
      import com.smartgwt.client.widgets.layout.SectionStack;
      import com.smartgwt.client.widgets.layout.SectionStackSection;
      
      public class BuiltInDS implements EntryPoint {
      	public void onModuleLoad() {
      		KeyIdentifier debugKey = new KeyIdentifier();
      		debugKey.setCtrlKey(true);
      		debugKey.setKeyName("D");
      
      		Page.registerKey(debugKey, new PageKeyHandler() {
      			public void execute(String keyName) {
      				SC.showConsole();
      			}
      		});
      
      		SectionStack ss = new SectionStack();
      		ss.setSectionHeaderClass("com.smartgwt.sample.client.ImgSectionHeaderTest");
      		SectionStackSection sss1 = new SectionStackSection("Section 1");
      		Label l1 = new Label("Label 1");
      		sss1.setItems(l1);
      		SectionStackSection sss2 = new SectionStackSection("Section 2");
      		Label l2 = new Label("Label 2");
      		sss2.setItems(l2);
      		ss.setSections(sss1, sss2);
      		ss.setHeight("50%");
      		ss.setWidth("50%");
      		ss.draw();
      	}
      }
      ImgSectionHeaderTest.java:
      Code:
      package com.smartgwt.sample.client;
      
      import com.google.gwt.core.client.JavaScriptObject;
      import com.smartgwt.client.widgets.layout.ImgSectionHeader;
      
      public class ImgSectionHeaderTest extends ImgSectionHeader {
      	public ImgSectionHeaderTest() {
      		super();
      		setStyleName("Imgfoobar");
      	}
      
      	public ImgSectionHeaderTest(JavaScriptObject jsObj) {
      		super(jsObj);
      		setStyleName("ImgfoobarJSO");
      	}	
      }
      SectionHeaderTest.java:
      Code:
      package com.smartgwt.sample.client;
      
      import com.google.gwt.core.client.JavaScriptObject;
      import com.smartgwt.client.widgets.layout.SectionHeader;
      
      public class SectionHeaderTest extends SectionHeader {
      	public SectionHeaderTest() {
      		super();
      		setBaseStyle("foobar");
      	}
      
      	public SectionHeaderTest(JavaScriptObject jsObj) {
      		super(jsObj);
      		setBaseStyle("foobarJSO");
      	}	
      }

      I tried in BuildInDS with:
      • ss.setSectionHeaderClass("SectionHeaderTest");
      • ss.setSectionHeaderClass("ImgSectionHeaderTest")
      • ss.setSectionHeaderClass("com.smartgwt.sample.client.SectionHeaderTest");
      • ss.setSectionHeaderClass("com.smartgwt.sample.client.ImgSectionHeaderTest");


      Exception in builtInDS is:
      Code:
      16:26:43.167 [ERROR] [builtinds] Unable to load module entry point class com.smartgwt.sample.client.BuiltInDS (see associated exception for details)
      
      com.google.gwt.core.client.JavaScriptException: (TypeError) @com.smartgwt.client.widgets.BaseWidget::draw()([]): _8 is null
          at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:249)
          at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:576)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNativeVoid(ModuleSpace.java:304)
          at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:107)
          at com.smartgwt.client.widgets.BaseWidget.draw(BaseWidget.java)
          at com.smartgwt.sample.client.BuiltInDS.onModuleLoad(BuiltInDS.java:35)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at com.google.gwt.dev.shell.ModuleSpace.onLoad(ModuleSpace.java:411)
          at com.google.gwt.dev.shell.OophmSessionHandler.loadModule(OophmSessionHandler.java:200)
          at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:526)
          at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
          at java.lang.Thread.run(Unknown Source)
      Exception for my code is (copy&paste from Eclipse Dev Mode):
      Code:
      17:02:15.600 [ERROR] [lms] Uncaught exception escaped
      
      com.google.gwt.event.shared.UmbrellaException: Exception caught: (TypeError) @com.smartgwt.client.widgets.layout.SectionStack::create()([]): _8 is null
          at com.google.gwt.event.shared.HandlerManager.fireEvent(HandlerManager.java:129)
          at com.google.gwt.user.client.ui.Widget.fireEvent(Widget.java:129)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
          at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
          at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
          at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:338)
          at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:219)
          at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:576)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:284)
          at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
          at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
          at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:356)
          at sun.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
          at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
          at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
          at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
          at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
          at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
          at java.lang.Thread.run(Unknown Source)
      Caused by: com.google.gwt.core.client.JavaScriptException: (TypeError) @com.smartgwt.client.widgets.layout.SectionStack::create()([]): _8 is null
          at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:249)
          at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:576)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:284)
          at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
          at com.smartgwt.client.widgets.layout.SectionStack.create(SectionStack.java)
          at com.smartgwt.client.widgets.BaseWidget.getOrCreateJsObj(BaseWidget.java:456)
          at com.smartgwt.client.util.JSOHelper.convertToJavaScriptArray(JSOHelper.java:893)
          at com.smartgwt.client.util.JSOHelper.convertToJavaScriptArray(JSOHelper.java:853)
          at com.smartgwt.client.widgets.BaseWidget.setAttribute(BaseWidget.java:862)
          at com.smartgwt.client.widgets.layout.Layout.setMembers(Layout.java:686)
          at com.lmscompany.lms.client.menu.toptabs.LeadMangementVLayout.<init>(LeadMangementVLayout.java:197)
          at com.lmscompany.lms.client.menu.navigation.MainArea.openModule(MainArea.java:142)
          at com.lmscompany.lms.client.ui.button.NavigationButtonField.click(NavigationButtonField.java:122)
          at com.lmscompany.lms.client.ui.button.NavigationButtonField$5.onClick(NavigationButtonField.java:106)
          at com.smartgwt.client.widgets.events.ClickEvent.dispatch(ClickEvent.java:111)
          at com.smartgwt.client.widgets.events.ClickEvent.dispatch(ClickEvent.java:1)
          at com.google.gwt.event.shared.GwtEvent.dispatch(GwtEvent.java:1)
          at com.google.web.bindery.event.shared.EventBus.dispatchEvent(EventBus.java:40)
          at com.google.web.bindery.event.shared.SimpleEventBus.doFire(SimpleEventBus.java:193)
          at com.google.web.bindery.event.shared.SimpleEventBus.fireEvent(SimpleEventBus.java:88)
          at com.google.gwt.event.shared.HandlerManager.fireEvent(HandlerManager.java:127)
          at com.google.gwt.user.client.ui.Widget.fireEvent(Widget.java:129)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
          at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
          at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
          at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:338)
          at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:219)
          at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:576)
          at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:284)
          at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
          at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
          at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:356)
          at sun.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
          at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
          at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
          at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
          at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
          at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
          at java.lang.Thread.run(Unknown Source)
      Without setSectionHeaderClass it works as expected. Could you please verify or tell me how to use setSectionHeaderClass?

      Thank you & Best regards,
      Blama
      Last edited by Blama; 21 May 2014, 07:09. Reason: Added exception for my code

      Comment


        #4
        Follow up

        Hi Isomorphic,

        just to let you know, using the code from this user solved it for me a little bit. In the load_skin.js I added:

        Code:
        isc.ClassFactory.defineClass("TableSectionHeader", "SectionHeader").addProperties({
                    baseStyle:"tableSectionHeader"
                });
        Note the difference from user stuchy's code ("ClassFactory"). I suppose this is correct, as all other calls to defineClass in load_skin.js use the same scheme. Most likely it changed between the time of the post (Jul '11) and now. In the java code and in the skin_styles.css I applied his changes verbatim.

        Nevertheless I think that this is, what the call to setSectionHeaderClass should have done and that my java classes were not used(?). So the bug report is most likely still valid, I was only able to work around it.

        Best regards,
        Blama

        Comment


          #5
          Hi Isomorphic,

          sorry to clutter the thread with different problem areas, but now I worked through the rest of your tips.

          Originally posted by Isomorphic View Post
          The simplest thing for all requirements is just to use a custom SectionHeader class via sectionStack.sectionHeaderClass.
          Wrote about that before.




          Originally posted by Isomorphic View Post
          Top and bottom space and border can be achieved with just CSS.
          I tried and I'm afraid it can't (or I can't). Setting paddings or margins in CSS for .sectionHeaderopened, .sectionHeaderclosed, .sectionHeaderDisabledopened, .sectionHeaderDisabledclosed only affects the SectionStackSectionHeader. That means I can't set a bottom below a expanded Section, as there is no "SectionStackSection"-markup (there is only markup for its contents). Although it might be possible to fake it when applying a border-bottom to the last element of the section.

          For gaps between SectionStackSections - it does not work at all with padding or margin in the CSS classes mentioned above - again, the only DOM elements I have are the header and the contents. All the header-elements like
          Code:
          <div id="isc_2B" eventproxy="isc_SectionHeader_1"
          	style="position: absolute; left: 0px; top: 291px; width: 1698px; height: 39px; z-index: 200756; overflow: hidden; cursor: pointer; -webkit-margin-before-collapse: collapse; -webkit-margin-after-collapse: collapse;"
          	onscroll="return isc_SectionHeader_1.$lh()" role="heading">
          	<table cellspacing="0" cellpadding="0" width="1698px" height="39px" style="table-layout:fixed">
          		<tbody>
          			<tr>
          				<td class="sectionHeaderclosed" style="padding-top:0px;padding-bottom:0px;padding-right:720px;text-align:left;vertical-align:middle;white-space:nowrap">
          					<img src="http://localhost:8080/lms/lms/sc/skins/Simplicity/images/main-icon_39px/Leadmanagement-Lead-rejected_closed.png" width="16" height="16"
          						align="TEXTTOP" style="margin-right:6px;vertical-align:middle" name="isc_2Bicon" eventpart="icon" border="0" suppress="TRUE" draggable="true">
          						<div id="isc_2A"
          							style="display:inline-block;margin-left:-22px;:border-box;max-width:100%;padding-left:22px;vertical-align:middle;overflow:hidden;text-overflow:ellipsis">Abgelehnte Leads</div>
          				</td>
          			</tr>
          		</tbody>
          	</table>
          	<div id="isc_2C" eventproxy="isc_SectionHeader_1_controlsLayout" class="normal"
          ...
          ...
          seem to be positioned absolute.




          Originally posted by Isomorphic View Post
          All base SectionHeader classes extend from StatefulCanvas so they already have properties for controlling the size of the icon.
          I think I understood what you mean and searched the javadocs. setIconSize() / setIconHeight() / setIconWidth() is available in
          ImgSectionHeader only, not in SectionHeader. I use simplicity, which uses the latter HeaderClass. Testing with
          Code:
          SectionStack ss = new SectionStack() {
          	{
          		setSectionHeaderClass("ImgSectionHeader");
          	}
          };
          SectionStack.setDefaultProperties(ss);
          
          ImgSectionHeader ish = new ImgSectionHeader() {
          	{
          		setIconHeight(39);
          		setIconWidth(39);
          	}
          };
          ImgSectionHeader.setDefaultProperties(ish);
          showed that setIconSize() works when using such skin. Could you add setIconSize() / setIconHeight() / setIconWidth() to SectionHeader as well?

          Best regards,
          Blama

          Comment


            #6
            Both approaches (load_skin.js and setSectionHeaderClass() with a Java class name) work. The problem with your all-Java approach was that you did not enable Reflection. Aside from the ultimate failure to instantiate the class, you should have seen an earlier warning in the logs about inability to look up your class.

            isc.defineClass() is just a shortcut for isc.ClassFactory.defineClass(), covered in the SmartClient docs. They are used identically.

            SectionHeader is a subclass of Label so look in the superclass docs for setIconSize() et al, so there is no need for new APIs. You probably do not want to use ImgSectionHeader as skinning it requires replacing images rather than just using CSS.

            As far as the spacing you want, the point is to add leading spacing to all SectionHeaders, which will achieve the equivalent of trailing spacing for all sections. If you're saying you want *only* trailing spacing or some other variation where spacing is not consistent between all SectionHeaders, sorry, you will need to use the approach of setting layoutMember.extraSpace on the last member of each section.

            Comment


              #7
              Hi Isomorphic,

              thanks for the pointer to reflection.
              The Label.setIconSize() worked as well as suggested by you. I don't know how I missed that while text-searching the docs.

              Regarding the spacing:
              I'm fine with spacing either above or below the SectionStackSections. I could simulate some effect by setting a larger-than-needed SectionStack.setHeaderHeight()+margin-top in CSS, but it did not come out as expected. Most importantly, if you look at the image from the 1st post, you see that there is a difference in color between the SectionHeader and the spacing.

              Best regards,
              Blama

              Comment


                #8
                In your first screenshot, you're basically treating each section almost as though it were a discrete component with not just spacing but a background color and border delineating each section.

                To do that, use a Layout as the sole component provided sectionStackSection.items, and have all components that you think of as items in the section be members of the Layout instead. Then placing your styling on the Layout.

                Comment


                  #9
                  Hi Isomorphic,

                  thanks again for the pointer. I'll do so.

                  Best regards,
                  Blama

                  Comment


                    #10
                    Hi Isomorphic,

                    thanks again for the help. I solved my case using a normal unmodified SectionHeader and an own SectionStackSection-Subclass with two Layouts for styling.

                    For reference my code, if someone else finds this thread.

                    Subclass:
                    Code:
                    import com.google.gwt.core.client.JavaScriptObject;
                    import com.smartgwt.client.widgets.layout.SectionStackSection;
                    import com.smartgwt.client.widgets.layout.VLayout;
                    
                    public class LMSSectionStackSection extends SectionStackSection {
                    	protected VLayout contentContainer;
                    	protected VLayout bottomMargin;
                    	private boolean showing;
                    
                    	public LMSSectionStackSection() {
                    		super();
                    		configure();
                    	}
                    
                    	public LMSSectionStackSection(JavaScriptObject jsObj) {
                    		super(jsObj);
                    		configure();
                    	}
                    
                    	public LMSSectionStackSection(String title) {
                    		super(title);
                    		configure();
                    	}
                    
                    	private void configure() {
                    		contentContainer = new VLayout(0) {
                    			{
                    				setStyleName("sectionStackContentContainer");
                    				setWidth100();
                    			}
                    		};
                    
                    		bottomMargin = new VLayout(0) {
                    			{
                    				setStyleName("sectionStackBottomMargin");
                    				setWidth100();
                    				setHeight(20);
                    			}
                    		};
                    		contentContainer.hide();
                    		showing = false;
                    	}
                    
                    	protected void showContent() {
                    			contentContainer.show();
                    			showing = true;
                    	}
                    
                    	protected void hideContent() {
                    			contentContainer.hide();
                    			showing = false;
                    	}
                    
                    	protected boolean isShowing() {
                    		return showing;
                    	}
                    }
                    Usage:
                    Code:
                    mySection = new LMSSectionStackSection("title") {
                    	{
                    		setExpanded(true);
                    		contentContainer.setMembers(...);
                    		setItems(contentContainer, bottomMargin);
                    		showContent();
                    	}
                    If you want to show some closed at the beginning, you have to use hideContent() after sectionStack.setMembers(...). You also need to have setVisibilityMode(VisibilityMode.MULTIPLE); and manage "opening" and "closing" of the sections yourself in an own SectionHeaderClickHandler (from SmartGWT's point of view, the sections are open all the time).

                    CSS:
                    Code:
                    .sectionStackContentContainer {
                    	background-color: #F5F5F5;
                    	border-top: 1px solid #CCC;
                    	padding: 10px 10px;
                    }
                    
                    .sectionStackBottomMargin {
                    	background-color: #E7E7E7;
                    	border-top: 5px solid #CCC;
                    }
                    You can see the result in the attachment.

                    Best regards,
                    Blama
                    Attached Files

                    Comment

                    Working...
                    X