Skip to content

Work Spec: Design Modes Support #843

Closed
publickeating opened this Issue Oct 1, 2012 · 19 comments

5 participants

@publickeating
SproutCore member

Rationale

One of the main attractions of web applications, such as those written in SproutCore, is the ability to target multiple devices with vastly different screen sizes using only a single codebase. However, responsive layouts are very difficult to manage in general and in SproutCore require either a lot of complex configuring of views or require maintaining multiple apps/frameworks for different device sizes.

Description

I believe that it is better to focus on making an app's view layer flexible than it is to try and create size specific view layers in multiple apps. My experience has been that size specific view frameworks create a lot of duplicate code, are very difficult to maintain and for environments where the window size can change, we should support all the possible view layouts within the app.

As such, each SC.Pane should accept an array or hash of design modes and have a current design mode that is automatically set dependent on window size. A design mode represents a specific "layout design" of the application for the current state, which will most likely be dependent on screen width, for example, a small screen design mode and a large screen design mode.

When the mode changes, all the visible views should be asked to re-adjust their and their children's layouts to match the new mode. As well, a design mode specific class should be added to views or to the pane to enable mode specific CSS.

This is different than themes, which are not generally size dependent. For example, you may have a 'user' theme and an 'admin' theme and may also have design modes for a small screen and large screen layout of the app in whichever theme is set.

Acceptance Criteria

  • must support multiple design modes (ex. 'small', 'medium' & 'large')
  • must still work with localized layouts
  • must be able to automatically change current mode dependent on window width
  • may be able to automatically change current mode dependent on window height as well
  • may be able to set current mode manually
  • must work with all SC.View subclasses to adjust their layout on mode changes
  • may work with themes (i.e.. the mode may automatically set the themeName on each view)
  • must be optional to use
  • must not be detrimental to performance
  • must include full suite of unit tests
  • must include a demo on http://showcase.sproutcore.com
  • must include a guide on http://guides.sproutcore.com
@publickeating publickeating was assigned Oct 1, 2012
@dcporter
SproutCore member
dcporter commented Oct 2, 2012

4) Width is probably the 80% solution, but height really needs to be supported too, e.g. for landscape vs. portrait stuff. Unfortunately that opens up a can of worms: the simplest implementation I can think of requires the ability to be in multiple design modes at once, which breaks the simplicity of the designMode: [small | medium | large | xlarge] approach.

5) What's the need for manually setting designMode? The designMode property reflects the application's current size at a global level; if you want to set something locally, get your own property.

It would be nice to be able to declaratively specify layouts for different modes on a view, e.g.:

designModeLayouts: {
  small: { top: 0, right: 0, left: 0, height: 20 },
  large: { top: 0, right: 0, left: 0, height: 40 }
}

or something. Not sure if this is easy to accomplish without impacting performance, though.

@mitchless
@publickeating
SproutCore member

Yes, it would be a collection of layouts, but I wouldn't call them static, because each layout may be dynamic (ie. based on percentages like a CSS grid style). What this is, is to solve the hard line problem where a flexible "large" layout no longer works well as a flexible "small" layout and so you want a specific design for "small" (with the ability to support medium, extra large, extra small, etc. designs if really necessary).

Expanding on @dcporter's suggestion above, you might do something like:

myView: SC.View.design({
  designModeLayouts: {
     small: { top: 0, height: 20, left: 0, right: 0 },
     large: { top: 0, height: 40, centerX: 0, width: 200 },
     xlarge: { top: 40, height: 100, left: 0.2, width: 0.4 }
  }
})
@publickeating publickeating added a commit that referenced this issue Oct 16, 2012
@publickeating publickeating Adds design modes support to SC.Pane, allowing the design of an appli…
…cation to very efficiently and easily change depending on the width of the display.

While a design may be flexible for a wide range of display widths, at a certain point it no longer makes sense to keep stretching or compressing views and instead it is better to completely alter the design.  A prime example is that a design that can flex between a medium sized display like a tablet and a large sized display like a desktop, will not likely work for a small display like a smartphone.  In order to very easily alter the design, SC.Pane provides the designModes property which includes the width thresholds that you want to trigger a change to the overall design of the application.  Each time a threshold width is crossed, the child views will be updated with the new design mode.  At this point, any properties dependent on 'designMode' will recompute allowing you to adjust settings such as isVisible.

As well, SC.View has a new property 'designLayouts' that is simply the layouts for the 'designModes' you define.  For example, if the design mode becomes 'small' any child view that has a 'small' designLayout will have its layout automatically set.

This completes work spec #843.
193d98b
@dcporter
SproutCore member

This is set up explicitly to allow different panes to have different design modes, but the common case is going to be app-wide. Should there be an easy way to set design modes once and have them update on all panes (and be available somewhere centrally for controllers to use if needed) (recognizing that in most cases that would be indicative of a bad design)?

@publickeating
SproutCore member

It's possible to have it set app wide… I'm not sure of the implementation, but maybe something you set on the SC.Application? I agree that controllers may need to access it and that would make it easier, plus it is likely that every pane in the app needs to have design modes if even one does.

@dcporter
SproutCore member

I think the next-easiest implementation would be to define designModes and thresholds on the application object, like you suggest; then, instead of mode being calculated in Pane#windowSizeDidChange, it's calculated in whatever code calls Pane#windowSizeDidChange in the first place, and set on each pane in the app directly.

The fanciest way that I can think of would be to create a class called SC.DesignModeDelegate (or something less ... delegatey) which you could instantiate if you wanted, and assign to panes at create time. The design modes class would have the thresholds and do the calculating, and the pane (on create) would know to assign itself to its designModer as one of its "owned panes". The common case would see developers creating a single instance of the mode class at MyApp.designModes, and assigning it to all of their panes at create time. (I would even put an isUniversal flag on the design modes class, in which case all panes in the application would get it on create.) The developer could also assign a custom design mode class to a pane which needed different behavior.

@publickeating
SproutCore member

This is nearly complete, minus the Guide. The demo is complete, but because of an issue with SC.ImageView rendering, it appears broken and so that has to be fixed first.

Also, I want to sweat this code in practice some more to be sure that the API is correct.

@dcporter
SproutCore member

Problem

Per the note on #853, the API was not correct, as it only supports consideration of width, not height.

Per @topherfangio, a good example of this an iPhone 5+ turned sideways in landscape mode – this becomes dramatically wider than it is tall, and a width-based mode that looks good on at 568 pixels may work terribly when less than 320 pixels tall.

Solution 1

@topherfangio suggested that we stick with defining modes by width, but add a set of hard-coded aspect ratios. The developer would then be able to specify layouts either for the mode itself, or for mode + aspect ratio. Topher, I'm not sure what this API would actually look like, can you chime in with an example?

Solution 2

My idea starts simple for the common case, then gets eye-bleeding for the uncommon case, but has the advantage of being (I believe) infinitely flexible. My suggestion is that the developer specify modes as an ordered list of height + width rectangles (or optionally just one or the other), arranged by the developer from roughly smallest to roughly largest; for any given viewport dimensions, SC checks the list and finds the first mode that entirely contains the current dimensions (both height and width). That's the mode! (If the current dimensions are larger than the last mode, that mode is used.) It's the developer's responsibility to order the modes intelligently, but that should be very easy for the common case and not really that bad except for the most insane cases.

So something like:

// in my Pane...
designModes: [
  { name: 'smartphonePortrait', width: 400, height: 600 },
  { name: 'smartphoneLandscape', width: 600, height: 400 },
  { name: 'medium', width: 800 }, // height is compared as Infinity.
  { name: 'wide' } // height and width are compared as Infinity. The universal mode, useful if you've got some weird special-case modes like very-short-very-wide. Remember that the last mode is defaulted to if no other mode fits.
]

If I rotate my phone sideways, the viewport changes, and SC checks down the list: smartphonePortrait doesn't fit, but smartphoneLandscape does so that's it.

We could provide a list of common modes for the common case.

@ebow
ebow commented Nov 14, 2013
@dcporter
SproutCore member

@ebow I've run into that need before too, it's very convenient for the developer and occasionally nice for the user. That's going to still have to be implemented by the developer though – for performance reasons, design modes are watched and mediated at the level of the window and pane.

@topherfangio
@dcporter
SproutCore member
  1. I agree that there should be a way to specify app-wide design modes. Individual panes may also want their own, and we don't want to deal with figuring out how to merge them, so I think a pane either specifies all of its own or gets all of the application's modes. (The dev is welcome to manually specify her own merging if she wants.)
  2. Your point about some apps completely changing what's shown depending on the design mode is a good one, and having an observable property somewhere is a good idea for enabling that. SC.Application#currentDisplayMode and SC.Pane#currentDisplayMode?
  3. It's counterintuitive but I'm not sure that offering orientation is that useful. First you'd only be able to specify two of the three – height and orientation, or width and orientation. And if I have 320 wide and portrait then turn my phone sideways, I end up with 500 wide and landscape, which is a totally different thing. I dunno, maybe it's enough of a conceptual convenience that folks would use it.
  4. Once design modes are in place, AlertPane should absolutely become responsive. I'm not sure about anything else though. Anyway, that's a discussion for later, and can be case-by-case.
@dcporter
SproutCore member

@publickeating is there a way to specify design mode visibility along with layout? That seems very important.

@topherfangio
@dcporter
SproutCore member

three. Orientation isn't a real thing, it's based on height and width (even when it's provided by the browser). If I've specified width of 500 and landscape, that's the same as specifying width of 500 and height of 500 or less.

@topherfangio

But you could have a width/height specified at 500x500 to watch for orientation change, but your window could be at 600x800 which is still portrait. Only width/height doesn't definitively give you orientation.

@topherfangio

Note: you can obviously determine orientation by only width/height, but I think we still need to allow them to specify it regardless of width /height :-)

@dcporter
SproutCore member

Including orientation as an optional constraint makes sense. Thanks for talking me through it on IRC.

@publickeating publickeating added a commit that referenced this issue Nov 20, 2013
@publickeating publickeating Adds design modes support to SC.Pane, allowing the design of an appli…
…cation to very efficiently and easily change depending on the width of the display.

While a design may be flexible for a wide range of display widths, at a certain point it no longer makes sense to keep stretching or compressing views and instead it is better to completely alter the design.  A prime example is that a design that can flex between a medium sized display like a tablet and a large sized display like a desktop, will not likely work for a small display like a smartphone.  In order to very easily alter the design, SC.Pane provides the designModes property which includes the width thresholds that you want to trigger a change to the overall design of the application.  Each time a threshold width is crossed, the child views will be updated with the new design mode.  At this point, any properties dependent on 'designMode' will recompute allowing you to adjust settings such as isVisible.

As well, SC.View has a new property 'designLayouts' that is simply the layouts for the 'designModes' you define.  For example, if the design mode becomes 'small' any child view that has a 'small' designLayout will have its layout automatically set.

This completes work spec #843.
3441d2f
@publickeating publickeating added a commit that referenced this issue Dec 5, 2013
@publickeating publickeating Adds design modes support to SC.Pane, allowing the design of an appli…
…cation to very efficiently and easily change depending on the width of the display.

While a design may be flexible for a wide range of display widths, at a certain point it no longer makes sense to keep stretching or compressing views and instead it is better to completely alter the design.  A prime example is that a design that can flex between a medium sized display like a tablet and a large sized display like a desktop, will not likely work for a small display like a smartphone.  In order to very easily alter the design, SC.Pane provides the designModes property which includes the width thresholds that you want to trigger a change to the overall design of the application.  Each time a threshold width is crossed, the child views will be updated with the new design mode.  At this point, any properties dependent on 'designMode' will recompute allowing you to adjust settings such as isVisible.

As well, SC.View has a new property 'designLayouts' that is simply the layouts for the 'designModes' you define.  For example, if the design mode becomes 'small' any child view that has a 'small' designLayout will have its layout automatically set.

This completes work spec #843.
a130c28
@publickeating
SproutCore member

Submitting PR for this.

@publickeating publickeating added a commit that referenced this issue Dec 18, 2013
@publickeating publickeating Adds design modes support to SC.Pane, allowing the design of an appli…
…cation to very efficiently and easily change depending on the width of the display.

While a design may be flexible for a wide range of display widths, at a certain point it no longer makes sense to keep stretching or compressing views and instead it is better to completely alter the design.  A prime example is that a design that can flex between a medium sized display like a tablet and a large sized display like a desktop, will not likely work for a small display like a smartphone.  In order to very easily alter the design, SC.Pane provides the designModes property which includes the width thresholds that you want to trigger a change to the overall design of the application.  Each time a threshold width is crossed, the child views will be updated with the new design mode.  At this point, any properties dependent on 'designMode' will recompute allowing you to adjust settings such as isVisible.

As well, SC.View has a new property 'designLayouts' that is simply the layouts for the 'designModes' you define.  For example, if the design mode becomes 'small' any child view that has a 'small' designLayout will have its layout automatically set.

This completes work spec #843.
0af68d8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.