Bug 375069 - [CSS] Elements in parented shells are selected with their parent
Summary: [CSS] Elements in parented shells are selected with their parent
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.2   Edit
Hardware: PC Mac OS X - Carbon (unsup.)
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform-UI-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-03-22 11:19 EDT by Brian de Alwis CLA
Modified: 2013-07-24 07:17 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Brian de Alwis CLA 2012-03-22 11:19:02 EDT
Currently our CSS selectors traverse parented shells as children of the parent shell.  It's thus impossible to create a selector that affects only the child widgets within a top-level shell (e.g., an .MTrimmedWindow) separate from those elements within a parented shells.  For example, the following property:

   .MTrimmedWindow Text { color: red; background-color: black; }

affects both text items within the main window as well as in any dialogs, such as the Preferences dialog.

Although implementing a CSS3-style :not() dynamic pseudo element could help, it would still require creating expressions such as:

   .MTrimmedWindow :not(Shell.topLevel) Text { color: red; background-color: black; }

Which isn't too helpful.
Comment 1 Brian de Alwis CLA 2012-03-22 12:35:47 EDT
ShellElement inherits ControlElement's getParentNode() which returns the parent shell.


	public Node getParentNode() {
		Control control = getControl();
		Composite parent = control.getParent();
		if (parent != null) {
			Element element = getElement(parent);
			return element;
		}
		return null;
	}

We can specialize this in ShellElement to return null.
Comment 2 Brian de Alwis CLA 2012-03-23 09:21:15 EDT
Consider a CSS Rule like ".MTrimmedWindow Label { color: red; }"  When a dialog is opened, what happens is:

1. The dialog appears and is laid out or is activated, triggering a SWT.Reskin event for all skinnable widgets in the display.

2. The per-widget SWT.Reskin event is handled by CSSWTApplyStylesListener, which calls CSSEngine#applyStyle() on the widget

3. CSSSWTEngineImpl's applyStyles() gets the computed style of the element.  This computes the applicable rules be evaluating whether the element is matched by the rule's selector.

4. The CSSDescendantSelectorImpl's match() walks up the parent nodes of the element, verifying if the selector matches from each ancestor.  Because ShellElement currently inherits ControlElement#getParentNode() which just returns the element for the control's parent, a dialog's shell returns the parent, and will eventually match.

My proposal is that ShellElement should override getParentNode() to always return null, thus treating any Shell as a root node.  Arguments for:

  * In HTML, CSS applies to the current document, and not across tabs or windows. I believe developers and designers would consider each Shell in a similar manner.

  * Rules written for any type of Shell will still match. E.g., "Shell { color: red; }"

  * ShellElement's getChildren() doesn't include child shells (i.e., doesn't call Shell#getShells())
Comment 4 Bogdan Gheorghe CLA 2012-03-23 14:37:36 EDT
I can see merits to this approach, but I wonder if we are losing anything by severing the parent/child relationship? Dialog shells are parented off of the main shell, shouldn't that relationship be maintained in the CSS code?

The complicating issue here is that we are applying the styling in a mixed world where some elements won't have classnames/ids set. The only way to refer to these elements would be to use their element type selector. 

For example:

.MTrimmedWindow Text { color: red; background-color: black; } will select all Texts with an ancestor of .MTrimmedWindow (which will be all Text including the ones in dialogs)

.MTrimmedWindow Shell Text { color: black; background-color:white; } will select all Texts with ancestors element type Shell and .MTrimmedWindow (ie. Texts in a dialog)
 
I suppose if you wanted to style 3.x dialogs after the changes introduced here, you could do something like this (same thing I guess):

Shell Text { etc } -> style all shells including dialogs

.MTrimmedWindow Text { etc } -> override previous styling
Comment 5 Brian de Alwis CLA 2012-03-23 16:38:50 EDT
It's a good point, Bogdan, and I worry about that too.  But we were seeing bleed through into system dialogs (e.g., '.MTrimmedWindow Label') and more.  We also had a model inconsistency where the parented shells report their parent shell as their parent, but the parent shell does not include the child shells as their children.

I was thinking that we could expose the parent hierarchy through an attribute.  For example,

  Shell[parentage ~= 'org.eclipse.ui.X']
Comment 6 Brian de Alwis CLA 2012-03-24 10:27:19 EDT
To help narrow Shell selection, I added two new attributes and two new pseudoelement selectors.

The new attributes are:

  *  'title' that provides the shell's title.  Example: Shell[title~="Contacts"]

  * 'parentage' that provides a space-separated CSS ids of the shell's parents.
     Example: Shell[parenage~='org.eclipse.e4.demo.contacts.main'] to identify
     dialogs off the main E4 Contacts demo window.

The new pseudeoelement selectors are "swt-parented" and "swt-unparented" to identify shells with and without a parent.  I prefixed these with "swt-" to be on the safe side.  Example: Shell:swt-parented.

Commit as: http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=d903adb5abe4bd3d7b6a4ebf152399f18ce4b9fb
Comment 7 Brian de Alwis CLA 2012-03-25 15:50:03 EDT
For identifying dialogs, I have come up with a bit of a cool hack (in the good sense of the word).  JFace Windows (including Dialogs) set the Shell data element to the window/dialog class.  I've added a new attribute 'swt-data-class' to WidgetElement that generates the list of superclasses of the data element.  So a JFace dialog shell could then be identified as:

    Shell[swt-data-class~='org.eclipse.jface.dialogs.Dialog']

Committed to http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=aa7441b0d111c72c0eb5cf68f499eb19a3d94cb2
Comment 8 Matthias F CLA 2013-07-22 07:31:16 EDT
I actually need my dialogs to be children of their parent. I use selectors for the parent like *[foo='bar'] Button{ font-size: 8; }
So all Buttons which parent has 'foo' set to 'bar' use the style. When I open a dialog the style is not applied, because the parent of the button is the dialog shell without the foo attribute.

Is there an easy way or workaround to get the original functionality back?
Comment 9 Brian de Alwis CLA 2013-07-24 06:46:14 EDT
So I can think of a couple of approaches.

You could use a ShellListener
We could add a property to restore the old consider-child-shells-as-children behaviour.  The downside is it will bring back problems like the '.MTrimmedWindow Label' issue described in comment 5, and themes that assumed otherwise may not work correctly.

We could add the ability to select on parent attribute values.  For example, the following would also cause 'foo=bar' to be looked up in the parent:

   *[foo='bar'] Button, *[parent-foo='bar'] Button { ... }

You could try this by modifying org.eclipse.e4.ui.css.swt.dom.ShellElement#getAttribute() like the following:

	@Override
	public String getAttribute(String attr) {
		if (attr.startsWith("parent-")) {
			Element e = getElement(getShell().getParent());
			if (e instanceof ShellElement) {
				ShellElement parent = (ShellElement)e;
				String realAttr = attr.substring("parent-".length());
				String result = parent.getAttribute(realAttr);
				if (result == null) {
					result = parent.getAttribute(attr);
				}
				return result;
			}
		}

Or we could have some other prefix to cause the search to start from the local, such as "inherited-" or something similar.


Can you give us some examples of the 'foo=bar' expressions you're keying off from?
Comment 10 Matthias F CLA 2013-07-24 07:17:25 EDT
Our customer wants a per window zoom function which should also work for all sub components of the window (including ALL dialogs).
After a lot of back and forth we came to the current solution which sets the zoomlevel as an attribute of the window(shell) and every children of the window changes its size. So the 'bleeding' should work in this case. (I don't like the per window zooming, but we couldn't talk our customer out of it...)
In the CSS we have different styles for each zoomlevel/attribute value like [zoomlevel='large'] Button{}

Checking the parent attribute will only work for on layer up. Going through the tree would probably be to time consuming but I currently cannot see any other way.