diff options
| author | Stephan Wahlbrink | 2021-08-23 20:10:28 +0000 |
|---|---|---|
| committer | Stephan Wahlbrink | 2021-08-23 20:47:52 +0000 |
| commit | 77e089765e7afefbbd3d5e23faa8a9a222ddc0d5 (patch) | |
| tree | 4952692ec65d3479e8840cabddbbfbcd14cd1145 | |
| parent | 175c7f1cd8f3e835fe249df30ebe15efb625c697 (diff) | |
| download | org.eclipse.statet-commons-77e089765e7afefbbd3d5e23faa8a9a222ddc0d5.tar.gz org.eclipse.statet-commons-77e089765e7afefbbd3d5e23faa8a9a222ddc0d5.tar.xz org.eclipse.statet-commons-77e089765e7afefbbd3d5e23faa8a9a222ddc0d5.zip | |
Bug 575578: [UI-SWT] Add ExpandableComposite including styling for
dark theme
Change-Id: I20abda6d1743b9add088b204fbae998c28d2ee1c
8 files changed, 1674 insertions, 2 deletions
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF b/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF index fb076ff4..2082894a 100644 --- a/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.statet.ecommons.runtime.core;bundle-version="[4.5.0,4.6.0)", org.eclipse.core.runtime;bundle-version="3.19.0", org.eclipse.e4.core.contexts;bundle-version="1.7.0";resolution:=optional, + org.eclipse.statet.ecommons.preferences.core;bundle-version="[4.5.0,4.6.0)", org.eclipse.ui, org.eclipse.e4.ui.services, org.eclipse.jface.text;resolution:=optional, @@ -22,10 +23,15 @@ Require-Bundle: org.eclipse.statet.ecommons.runtime.core;bundle-version="[4.5.0, org.eclipse.core.variables;resolution:=optional, org.eclipse.core.resources;resolution:=optional, org.eclipse.core.filesystem;resolution:=optional, - org.eclipse.statet.ecommons.preferences.core;bundle-version="[4.5.0,4.6.0)" + org.eclipse.ui.forms;resolution:=optional Import-Package: com.ibm.icu.lang;version="67.1.0", com.ibm.icu.text;version="67.1.0", org.eclipse.debug.ui;resolution:=optional, + org.eclipse.e4.ui.css.core.dom;resolution:=optional, + org.eclipse.e4.ui.css.core.engine;resolution:=optional, + org.eclipse.e4.ui.css.swt.dom;resolution:=optional, + org.eclipse.e4.ui.css.swt.properties;resolution:=optional, + org.eclipse.e4.ui.css.swt.theme;resolution:=optional, org.eclipse.statet.ecommons.collections;version="4.5.0", org.eclipse.statet.ecommons.commands.core;version="4.5.0", org.eclipse.statet.ecommons.databinding, @@ -39,7 +45,6 @@ Import-Package: com.ibm.icu.lang;version="67.1.0", org.eclipse.statet.ecommons.variables.core, org.eclipse.statet.jcommons.collections;version="4.5.0", org.eclipse.statet.jcommons.lang;version="4.5.0", - org.eclipse.e4.ui.css.swt.theme;resolution:=optional, org.eclipse.ui.dialogs;resolution:=optional, org.eclipse.ui.model;resolution:=optional, org.eclipse.ui.views.contentoutline;resolution:=optional, @@ -53,6 +58,7 @@ Export-Package: org.eclipse.statet.ecommons.databinding.jface, org.eclipse.statet.ecommons.ui.dialogs, org.eclipse.statet.ecommons.ui.mpbv, org.eclipse.statet.ecommons.ui.swt, + org.eclipse.statet.ecommons.ui.swt.expandable, org.eclipse.statet.ecommons.ui.util, org.eclipse.statet.ecommons.ui.viewers, org.eclipse.statet.ecommons.ui.viewers.breadcrumb, diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/css/e4_dark.css b/ecommons/org.eclipse.statet.ecommons.uimisc/css/e4_dark.css new file mode 100644 index 00000000..a7921b2a --- /dev/null +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/css/e4_dark.css @@ -0,0 +1,19 @@ +/* + #=============================================================================# + # Copyright (c) 2021 Stephan Wahlbrink and others. + # + # This program and the accompanying materials are made available under the + # terms of the Eclipse Public License 2.0 which is available at + # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + # which is available at https://www.apache.org/licenses/LICENSE-2.0. + # + # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + # + # Contributors: + # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation + #=============================================================================*/ + +.MPart ExpandableComposite Table { + background-color: inherit; + color: inherit; +} diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml b/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml index 28b541b9..16c161c8 100644 --- a/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml @@ -229,4 +229,27 @@ </command> </extension> + <extension + point="org.eclipse.e4.ui.css.core.elementProvider"> + <provider + class="org.eclipse.statet.internal.ecommons.ui.swt.css.dom.ECommonsSwtElementProvider"> + <widget + class="org.eclipse.statet.ecommons.ui.swt.expandable.ExpandableComposite"/> + </provider> + </extension> + <extension + point="org.eclipse.e4.ui.css.core.propertyHandler"> + <handler + adapter="org.eclipse.statet.internal.ecommons.ui.swt.css.dom.ExpandableCompositeElement" + handler="org.eclipse.statet.internal.ecommons.ui.swt.css.properties.ExpandableCompositeHandler"> + <property-name + name="swt-titlebar-color"/> + </handler> + </extension> + <extension + point="org.eclipse.e4.ui.css.swt.theme"> + <stylesheet + uri="css/e4_dark.css"/> + </extension> + </plugin> diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java new file mode 100644 index 00000000..af1f4240 --- /dev/null +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java @@ -0,0 +1,1318 @@ +/*=============================================================================# + # Copyright (c) 2000, 2021 IBM Corporation and others. + # + # This program and the accompanying materials are made available under the + # terms of the Eclipse Public License 2.0 which is available at + # https://www.eclipse.org/legal/epl-2.0. + # + # SPDX-License-Identifier: EPL-2.0 + # + # Contributors: + # IBM Corporation - org.eclipse.ui.forms: initial API and implementation + # Kai Nacke - org.eclipse.ui.forms: Bug 202382 + # Bryan Hunt - org.eclipse.ui.forms: Bug 245457 + # Didier Villevalois - org.eclipse.ui.forms: Bug 178534 + # Alena Laskavaia - org.eclipse.ui.forms: Bug 481604 + # Ralf Petter <ralf.petter@gmail.com> - org.eclipse.ui.forms: Bug 183675 + # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation + #=============================================================================*/ + +// org.eclipse.ui.forms.widgets.ExpandableComposite +// a2318aea7aeb731a6b1cadf0e85dd1e1287f4dd1 +// removed textClient +// without changed layout for wrapping controls (2016-2017) + +package org.eclipse.statet.ecommons.ui.swt.expandable; + +import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.action.LegacyActionTools; +import org.eclipse.osgi.service.environment.Constants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.events.IExpansionListener; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.Hyperlink; +import org.eclipse.ui.forms.widgets.ILayoutExtension; +import org.eclipse.ui.forms.widgets.Section; +import org.eclipse.ui.forms.widgets.SharedScrolledComposite; +import org.eclipse.ui.forms.widgets.SizeCache; +import org.eclipse.ui.forms.widgets.Twistie; + +import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet; +import org.eclipse.statet.jcommons.collections.ImIdentityList; +import org.eclipse.statet.jcommons.lang.NonNullByDefault; +import org.eclipse.statet.jcommons.lang.Nullable; + + +/** + * This composite is capable of expanding or collapsing a single client that is + * its direct child. The composite renders an expansion toggle affordance + * (according to the chosen style), and a title that also acts as a hyperlink + * (can be selected and is traversable). The client is laid out below the title + * when expanded, or hidden when collapsed. + * <p> + * The widget can be instantiated as-is, or subclassed to modify some aspects of + * it. * + * <p> + * Since 3.1, left/right arrow keys can be used to control the expansion state. + * If several expandable composites are created in the same parent, up/down + * arrow keys can be used to traverse between them. Expandable text accepts + * mnemonics and mnemonic activation will toggle the expansion state. + * + * <p> + * While expandable composite recognize that different styles can be used to + * render the title bar, and even defines the constants for these styles + * (<code>TITLE_BAR</code> and <code>SHORT_TITLE_BAR</code>) the actual painting + * is done in the subclasses. + * + * @see Section + */ +@NonNullByDefault +public class ExpandableComposite extends Canvas { + + + private static class Toggle extends Twistie { + + public Toggle(final Composite parent, final int style) { + super(parent, style); + } + + public void setHover(final boolean on) { + this.hover= on; + } + + public boolean getHover() { + return this.hover; + } + + @Override + protected void paint(final PaintEvent e) { + // don't paint focus + final GC gc= e.gc; + final Rectangle clientArea= getClientArea(); + if (clientArea.width == 0 || clientArea.height == 0) { + return; + } + paintHyperlink(gc); + } + + } + + + /** + * If this style is used, a twistie will be used to render the expansion + * toggle. + */ + public static final int TWISTIE= 1 << 1; + + /** + * If this style is used, the title text will be rendered as a hyperlink + * that can individually accept focus. Otherwise, it will still act like a + * hyperlink, but only the toggle control will accept focus. + */ + public static final int FOCUS_TITLE= 1 << 3; + + /** + * If this style is used, the client origin will be vertically aligned with + * the title text. Otherwise, it will start at x= 0. + */ + public static final int CLIENT_INDENT= 1 << 4; + + /** + * If this style is used, computed size of the composite will take the + * client width into consideration only in the expanded state. Otherwise, + * client width will always be taken into account. + */ + public static final int COMPACT= 1 << 5; + + /** + * If this style is used, the control will be created in the expanded state. + * This state can later be changed programmatically or by the user if + * TWISTIE or TREE_NODE style is used. + */ + public static final int EXPANDED= 1 << 6; + + /** + * If this style is used, title bar decoration will be painted behind the + * text. + */ + public static final int TITLE_BAR= 1 << 8; + + /** + * If this style is used, a short version of the title bar decoration will + * be painted behind the text. This style is useful when a more discrete + * option is needed for the title bar. + * + * @since 3.1 + */ + public static final int SHORT_TITLE_BAR= 1 << 9; + + /** + * By default, text client is right-aligned. If this style is used, it will + * be positioned after the text control and vertically centered with it. + */ + public static final int LEFT_TEXT_CLIENT_ALIGNMENT= 1 << 13; + + /** + * By default, a focus box is painted around the title when it receives focus. + * If this style is used, the focus box will not be painted. This style does + * not apply when FOCUS_TITLE is used. + * @since 3.5 + */ + public static final int NO_TITLE_FOCUS_BOX= 1 << 14; + + public static final int IMAGE= 1 << 30; + + + /** + * The toggle widget used to expand the composite. + */ + protected @Nullable Toggle toggle; + + /** + * The text label for the title. + */ + protected @Nullable Control textLabel; + + private static final int IGAP= 4; + private static final int IVGAP= 3; + + private static final Point NULL_SIZE= new Point(0, 0); + + private static final int VSPACE= 3; + + private static final int SEPARATOR_HEIGHT= 2; + + private int expansionStyle= TWISTIE | FOCUS_TITLE | EXPANDED; + + private boolean expanded; + + private @Nullable Label imageLabel; + + private Control client; + + private final CopyOnWriteIdentityListSet<IExpansionListener> listeners= new CopyOnWriteIdentityListSet<>(); + + private @Nullable Color titleBarForeground; + + private final Rectangle titleHeaderRegion= new Rectangle(0, 0, 0, 0); + + + private class ExpandableLayout extends Layout implements ILayoutExtension { + + /** + * Width of the margin that will be added around the control (default is 0). + */ + private int marginWidth= 0; + + /** + * Height of the margin that will be added around the control (default is + * 0). + */ + private int marginHeight= 0; + + /** + * Horizontal margin around the inside of the title bar area when TITLE_BAR + * or SHORT_TITLE_BAR style is used. This variable is not used otherwise. + */ + private int titleBarTextMarginWidth= 6; + + /** + * Vertical spacing between the title area and the description control + * (default is 0). The description control is normally placed at the new + * line as defined in the font used to render it. This value will be added + * to it. + */ + public int descriptionVerticalSpacing= 0; + + /** + * Vertical spacing between the title area and the composite client control + * (default is 3). + */ + public int clientVerticalSpacing= 3; + + + private final SizeCache toggleCache= new SizeCache(); + + private final SizeCache textClientCache= new SizeCache(); + + private final SizeCache textLabelCache= new SizeCache(); + + private final SizeCache descriptionCache= new SizeCache(); + + private final SizeCache clientCache= new SizeCache(); + + + private void initCache(final boolean shouldFlush) { + this.toggleCache.setControl(ExpandableComposite.this.toggle); + this.textLabelCache.setControl(ExpandableComposite.this.textLabel); + this.descriptionCache.setControl(getDescriptionControl()); + this.clientCache.setControl(ExpandableComposite.this.client); + + if (shouldFlush) { + this.toggleCache.flush(); + this.textClientCache.flush(); + this.textLabelCache.flush(); + this.descriptionCache.flush(); + this.clientCache.flush(); + } + } + + @Override + protected void layout(final Composite parent, final boolean changed) { + initCache(changed); + + final var toggle= ExpandableComposite.this.toggle; + final var imageLabel= ExpandableComposite.this.imageLabel; + final var textLabel= ExpandableComposite.this.textLabel; + + final Rectangle clientArea= parent.getClientArea(); + int thmargin= 0; + int tvmargin= 0; + + if (hasTitleBar()) { + thmargin= this.titleBarTextMarginWidth; + tvmargin= IVGAP; + } + int x= this.marginWidth + thmargin; + int y= this.marginHeight + tvmargin; + Point toggleSize= NULL_SIZE; + if (toggle != null) { + toggleSize= this.toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); + } + Point imageSize= NULL_SIZE; + if (imageLabel != null) { + imageSize= imageLabel.computeSize(16, 16); + } + int toggleWidth= clientArea.width - this.marginWidth - this.marginWidth - thmargin - thmargin; + if (toggleSize.x > 0) { + toggleWidth-= toggleSize.x + IGAP; + } + if (imageSize.x > 0) { + toggleWidth-= imageSize.x; + } + + Point size= NULL_SIZE; + if (textLabel != null) { + size= this.textLabelCache.computeSize(toggleWidth, SWT.DEFAULT); + + if (textLabel instanceof Label) { + final Point defSize= this.textLabelCache.computeSize(SWT.DEFAULT, + SWT.DEFAULT); + if (defSize.y == size.y) { + // One line - pick the smaller of the two widths + size.x= Math.min(defSize.x, size.x); + } + } + } + int theaderHeight; + { final GC gc= new GC(ExpandableComposite.this); + gc.setFont(getFont()); + final FontMetrics fm= gc.getFontMetrics(); + theaderHeight= fm.getHeight(); + gc.dispose(); + + if (imageSize.y > theaderHeight) { + theaderHeight= imageSize.y; + } + } + if (toggle != null) { + int ty= theaderHeight / 2 - toggleSize.y / 2 + 1; + ty= Math.max(ty, 0); + ty+= this.marginHeight + tvmargin; + toggle.setLocation(x, ty); + toggle.setSize(toggleSize); + x+= toggleSize.x + IGAP; + } + if (imageLabel != null) { + final int iy= theaderHeight / 2 - imageSize.y / 2; + imageLabel.setBounds(x, iy, imageSize.x, imageSize.y); + } + if (textLabel != null) { + int ty= theaderHeight / 2 - size.y / 2; + int tx= x; + if (imageLabel != null) { + tx+= imageSize.x + IGAP; + } + if (Constants.WS_GTK.equals(Platform.getWS())) { + size.x+= 1; // See Bug 342610 + } + + this.textLabelCache.setBounds(tx, ty, size.x, size.y); + } + int tbarHeight= theaderHeight; + if (size.y > tbarHeight) { + tbarHeight= size.y; + } + y+= tbarHeight; + if (hasTitleBar()) { + y+= tvmargin; + } + final Control separatorControl= getSeparatorControl(); + if (separatorControl != null) { + y+= VSPACE; + separatorControl.setBounds(this.marginWidth, y, + clientArea.width - this.marginWidth - this.marginWidth, + SEPARATOR_HEIGHT); + y+= SEPARATOR_HEIGHT; + } + if (ExpandableComposite.this.expanded + && ExpandableComposite.this.client != null) { + int areaWidth= clientArea.width - this.marginWidth - thmargin; + int cx= this.marginWidth + thmargin; + if ((ExpandableComposite.this.expansionStyle & CLIENT_INDENT) != 0) { + cx= x; + } + areaWidth-= cx; + if (getDescriptionControl() != null) { + if (ExpandableComposite.this.expanded) { + y+= VSPACE; + } + final Point dsize= this.descriptionCache.computeSize(areaWidth, SWT.DEFAULT); + y+= this.descriptionVerticalSpacing; + this.descriptionCache.setBounds(cx, y, areaWidth, dsize.y); + y+= dsize.y + this.clientVerticalSpacing; + } + y+= this.clientVerticalSpacing; + final int cwidth= areaWidth; + final int cheight= clientArea.height - this.marginHeight - + this.marginHeight - y; + this.clientCache.setBounds(cx, y, cwidth, cheight); + } + + ExpandableComposite.this.titleHeaderRegion.x= this.marginWidth; + ExpandableComposite.this.titleHeaderRegion.y= this.marginHeight; + ExpandableComposite.this.titleHeaderRegion.width= clientArea.width - this.marginWidth - this.marginWidth; + ExpandableComposite.this.titleHeaderRegion.height= theaderHeight; + } + + @Override + protected Point computeSize(final Composite parent, final int wHint, final int hHint, + final boolean changed) { + initCache(changed); + + final var toggle= ExpandableComposite.this.toggle; + final var imageLabel= ExpandableComposite.this.imageLabel; + final var textLabel= ExpandableComposite.this.textLabel; + + int width= 0, height= 0; + Point toggleSize= NULL_SIZE; + int toggleWidth= 0; + if (toggle != null) { + toggleSize= this.toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); + toggleWidth= toggleSize.x + IGAP; + } + int thmargin= 0; + int tvmargin= 0; + + Point imageSize= NULL_SIZE; + if (imageLabel != null) { + imageSize= imageLabel.computeSize(16, 16); + } + + if (hasTitleBar()) { + thmargin= this.titleBarTextMarginWidth; + tvmargin= IVGAP; + } + int innerwHint= wHint; + if (innerwHint != SWT.DEFAULT) { + innerwHint-= toggleWidth + this.marginWidth + this.marginWidth + thmargin + thmargin; + if (imageSize.x > 0) { + innerwHint-= imageSize.x + IGAP; + } + } + + int innertHint= innerwHint; + + Point size= NULL_SIZE; + if (textLabel != null) { + size= this.textLabelCache.computeSize(innertHint, SWT.DEFAULT); + + if (textLabel instanceof Label) { + final Point defSize= this.textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); + if (defSize.y == size.y) { + // One line - pick the smaller of the two widths + size.x= Math.min(defSize.x, size.x); + } + } + } + + if (toggleWidth > 0) { + width+= toggleWidth; + } + if (imageSize.x > 0) { + width+= imageSize.x + IGAP; + } + if (size.x > 0) { + width+= size.x; + } + if (toggleSize.y > height) { + height= toggleSize.y; + } + if (imageSize.y > height) { + height= imageSize.y; + } + if (size.y > height) { + height= size.y; + } + final Control separatorControl= getSeparatorControl(); + if (separatorControl != null) { + height+= VSPACE + SEPARATOR_HEIGHT; + } + + if (wHint < width && size.x > 50) { + width-= size.x - 50; + } + + // if (hasTitleBar()) + // height+= VSPACE; + if ((ExpandableComposite.this.expanded || (ExpandableComposite.this.expansionStyle & COMPACT) == 0) + && ExpandableComposite.this.client != null) { + int cwHint= wHint; + int clientIndent= 0; + if ((ExpandableComposite.this.expansionStyle & CLIENT_INDENT) != 0) { + clientIndent= toggleWidth; + } + + if (cwHint != SWT.DEFAULT) { + cwHint-= this.marginWidth + this.marginWidth + thmargin + thmargin; + } + final Point csize= this.clientCache.computeSize(cwHint, SWT.DEFAULT); + if (getDescriptionControl() != null) { + int dwHint= cwHint; + if (dwHint == SWT.DEFAULT) { + dwHint= csize.x; + if ((ExpandableComposite.this.expansionStyle & CLIENT_INDENT) != 0) { + dwHint-= toggleWidth; + } + } + final Point dsize= this.descriptionCache.computeSize(dwHint, SWT.DEFAULT); + width= Math.max(width, dsize.x + clientIndent); + if (ExpandableComposite.this.expanded) { + if (separatorControl != null) { + height+= VSPACE; + } + height+= this.descriptionVerticalSpacing + dsize.y; + } + } + width= Math.max(width, csize.x + clientIndent); + if (ExpandableComposite.this.expanded) { + height+= this.clientVerticalSpacing; + height+= csize.y; + } + } + + final Point result= new Point(width + this.marginWidth + this.marginWidth + + thmargin + thmargin, height + this.marginHeight + this.marginHeight + + tvmargin + tvmargin); + return result; + } + + @Override + public int computeMinimumWidth(final Composite parent, final boolean changed) { + return computeSize(parent, 0, SWT.DEFAULT, changed).x; + } + + @Override + public int computeMaximumWidth(final Composite parent, final boolean changed) { + return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x; + } + + } + + + /** + * Creates an expandable composite using a TWISTIE toggle. + * + * @param parent + * the parent composite + * @param style + * SWT style bits + */ + public ExpandableComposite(final Composite parent, final int style) { + this(parent, style, TWISTIE); + } + + /** + * Creates the expandable composite in the provided parent. + * + * @param parent + * the parent + * @param style + * the control style (as expected by SWT subclass) + * @param expansionStyle + * the style of the expansion widget (TREE_NODE, TWISTIE, + * CLIENT_INDENT, COMPACT, FOCUS_TITLE, + * LEFT_TEXT_CLIENT_ALIGNMENT, NO_TITLE) + */ + public ExpandableComposite(final Composite parent, final int style, final int expansionStyle) { + super(parent, style); + this.expansionStyle= expansionStyle; + if ((expansionStyle & TITLE_BAR) != 0) { + setBackgroundMode(SWT.INHERIT_DEFAULT); + } + super.setLayout(new ExpandableLayout()); + if (hasTitleBar()) { + addPaintListener(this::onPaint); + } + + final Toggle toggle; + final Label imageLabel; + final Control textLabel; + if ((expansionStyle & TWISTIE) != 0) { + toggle= new Toggle(this, SWT.NULL); + } + else { + toggle= null; + this.expanded= true; + } + if ((expansionStyle & EXPANDED) != 0) { + this.expanded= true; + } + this.toggle= toggle; + if (toggle != null) { + toggle.setExpanded(this.expanded); + toggle.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(final HyperlinkEvent e) { + toggleState(); + } + }); + toggle.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + if (e.keyCode == SWT.ARROW_UP) { + verticalMove(false); + e.doit= false; + } + else if (e.keyCode == SWT.ARROW_DOWN) { + verticalMove(true); + e.doit= false; + } + } + }); + if ((getExpansionStyle() & FOCUS_TITLE) == 0) { + toggle.addFocusListener(new FocusListener() { + @Override + public void focusGained(final FocusEvent e) { + final var textLabel= ExpandableComposite.this.textLabel; + if (textLabel != null) { + textLabel.redraw(); + } + } + @Override + public void focusLost(final FocusEvent e) { + final var textLabel= ExpandableComposite.this.textLabel; + if (textLabel != null) { + textLabel.redraw(); + } + } + }); + } + } + setToggleHoverColor(computeToggleHoverColor()); + + if ((expansionStyle & IMAGE) != 0) { + imageLabel= new Label(this, SWT.NONE); + } + else { + imageLabel= null; + } + this.imageLabel= imageLabel; + + if ((expansionStyle & FOCUS_TITLE) != 0) { + final Hyperlink link= new Hyperlink(this, SWT.NONE); + link.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(final HyperlinkEvent e) { + programmaticToggleState(); + } + }); + textLabel= link; + } + else { + final Label label= new Label(this, SWT.NONE); + if (!isFixedStyle()) { + final Listener listener= new Listener() { + @Override + public void handleEvent(final Event e) { + switch (e.type) { + case SWT.Paint: + if (ExpandableComposite.this.toggle != null + && (getExpansionStyle() & NO_TITLE_FOCUS_BOX) == 0) { + paintTitleFocus(e.gc); + } + break; + case SWT.Resize: + updateLabelTrim(); + break; + } + } + }; + label.addListener(SWT.Paint, listener); + label.addListener(SWT.Resize, listener); + } + textLabel= label; + } + this.textLabel= textLabel; + if (textLabel != null) { + textLabel.setMenu(getMenu()); + textLabel.addTraverseListener(new TraverseListener() { + @Override + public void keyTraversed(final TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_MNEMONIC) { + // steal the mnemonic + if (!isVisible() || !isEnabled()) { + return; + } + if (e.character != 0 + && e.character == LegacyActionTools.extractMnemonic(getText())) { + e.doit= false; + if (!isFixedStyle()) { + programmaticToggleState(); + } + setFocus(); + } + } + } + }); + } + { // Complete Title header + final Listener listener= new Listener() { + @Override + public void handleEvent(final Event event) { + int x= event.x; + int y= event.y; + if (event.widget != ExpandableComposite.this) { + final Point p= ((Control)event.widget).getLocation(); + x+= p.x; + y+= p.y; + } + switch (event.type) { + case SWT.MouseDown: + if (ExpandableComposite.this.titleHeaderRegion.contains(x, y)) { + final var toggle= ExpandableComposite.this.toggle; + if (toggle != null) { + toggle.setFocus(); + } + } + break; + case SWT.MouseUp: + if (ExpandableComposite.this.titleHeaderRegion.contains(x, y) + && event.button == 1) { +// textLabel.setCursor(FormsResources.getBusyCursor()); + programmaticToggleState(); +// textLabel.setCursor(null); + } + break; + case SWT.MouseEnter: + case SWT.MouseMove: + case SWT.MouseExit: + if (ExpandableComposite.this.titleHeaderRegion.contains(x, y)) { + final var toggle= ExpandableComposite.this.toggle; + if (toggle != null && !toggle.getHover()) { + toggle.setHover(true); + toggle.redraw(); + } + } + else { + final var toggle= ExpandableComposite.this.toggle; + if (toggle != null && toggle.getHover()) { + toggle.setHover(false); + toggle.redraw(); + } + } + break; +// case SWT.Paint: +// if (toggle != null && (getExpansionStyle() & NO_TITLE_FOCUS_BOX) == 0) { +// paintTitleFocus(event.gc); +// } +// break; + } + } + }; + addListener(SWT.MouseDown, listener); + addListener(SWT.MouseUp, listener); + addListener(SWT.MouseEnter, listener); + addListener(SWT.MouseMove, listener); + addListener(SWT.MouseExit, listener); + if (imageLabel != null) { + imageLabel.addListener(SWT.MouseDown, listener); + imageLabel.addListener(SWT.MouseUp, listener); + imageLabel.addListener(SWT.MouseEnter, listener); + imageLabel.addListener(SWT.MouseExit, listener); + } + if (textLabel != null) { + textLabel.addListener(SWT.MouseDown, listener); + textLabel.addListener(SWT.MouseUp, listener); + textLabel.addListener(SWT.MouseEnter, listener); + textLabel.addListener(SWT.MouseExit, listener); + } + } + } + + + @Override + public boolean forceFocus() { + return false; + } + + /** + * Overrides 'super' to pass the menu to the text label. + * + * @param menu + * the menu from the parent to attach to this control. + */ + @Override + public void setMenu(final @Nullable Menu menu) { + final var toggle= this.toggle; + final var imageLabel= this.imageLabel; + final var textLabel= this.textLabel; + if (toggle != null) { + toggle.setMenu(menu); + } + if (imageLabel != null) { + imageLabel.setMenu(menu); + } + if (textLabel != null) { + textLabel.setMenu(menu); + } + + super.setMenu(menu); + } + + /** + * Prevents assignment of the layout manager - expandable composite uses its + * own layout. + */ + @Override + public final void setLayout(final @Nullable Layout layout) { + } + + /** + * Sets the background of all the custom controls in the expandable. + */ + @Override + public void setBackground(final @Nullable Color color) { + super.setBackground(color); + + if ((getExpansionStyle() & TITLE_BAR) == 0) { + final var toggle= this.toggle; + final var imageLabel= this.imageLabel; + final var textLabel= this.textLabel; + if (toggle != null) { + toggle.setBackground(color); + } + if (imageLabel != null) { + imageLabel.setBackground(color); + } + if (textLabel != null) { + textLabel.setBackground(color); + } + } + } + + protected Color computeToggleHoverColor() { + final Display display= getDisplay(); + return display.getSystemColor(SWT.COLOR_LIST_SELECTION); + } + + /** + * Sets the foreground of all the custom controls in the expandable. + */ + @Override + public void setForeground(final @Nullable Color color) { + super.setForeground(color); + + final var toggle= this.toggle; + final var textLabel= this.textLabel; + + if (toggle != null) { + toggle.setForeground(color); + } + if (textLabel != null) { + textLabel.setForeground(color); + } + } + + /** + * Sets the color of the toggle control. + * + * @param color + * the color object + */ + public void setToggleColor(final @Nullable Color color) { + final var toggle= this.toggle; + if (toggle != null) { + toggle.setDecorationColor(color); + } + } + + /** + * Sets the active color of the toggle control (when the mouse enters the + * toggle area). + * + * @param color + * the active color object + */ + public void setToggleHoverColor(final @Nullable Color color) { + final var toggle= this.toggle; + if (toggle != null) { + toggle.setHoverDecorationColor(color); + } + } + + /** + * Sets the fonts of all the custom controls in the expandable. + */ + @Override + public void setFont(final @Nullable Font font) { + super.setFont(font); + + final var toggle= this.toggle; + final var textLabel= this.textLabel; + if (toggle != null) { + toggle.setFont(font); + } + if (textLabel != null) { + textLabel.setFont(font); + } + } + + @Override + public void setEnabled(final boolean enabled) { + final var toggle= this.toggle; + final var textLabel= this.textLabel; + if (toggle != null) { + toggle.setEnabled(enabled); + } + if (textLabel != null) { + textLabel.setEnabled(enabled); + } + + super.setEnabled(enabled); + } + + /** + * Sets the client of this expandable composite. The client must not be + * <samp>null </samp> and must be a direct child of this container. + * + * @param client + * the client that will be expanded or collapsed + */ + public void setClient(final Control client) { + Assert.isTrue(client != null && client.getParent() == this); + this.client= client; + } + + /** + * Returns the current expandable client. + * + * @return the client control + */ + public Control getClient() { + return this.client; + } + + public void setImage(final @Nullable Image image) { + final var imageLabel= this.imageLabel; + if (imageLabel != null) { + imageLabel.setImage(image); + } + } + + public @Nullable Image getImage() { + final var imageLabel= this.imageLabel; + if (imageLabel != null) { + return imageLabel.getImage(); + } + return null; + } + + /** + * Sets the title of the expandable composite. The title will act as a + * hyperlink and activating it will toggle the client between expanded and + * collapsed state. + * + * @param title + * the new title string + * @see #getText() + */ + public void setText(final String title) { + final var textLabel= this.textLabel; + if (textLabel instanceof Label) { + ((Label)textLabel).setText(title); + updateLabelTrim(); + } + else if (textLabel instanceof Hyperlink) { + ((Hyperlink)textLabel).setText(title); + } + else { + return; + } + layout(); + } + + /** + * Returns the title string. + * + * @return the title string + * @see #setText(String) + */ + public String getText() { + final var textLabel= this.textLabel; + if (textLabel instanceof Label) { + return ((Label)textLabel).getText(); + } + else if (textLabel instanceof Hyperlink) { + return ((Hyperlink)textLabel).getText(); + } + else { + return ""; //$NON-NLS-1$ + } + } + + @Override + public void setToolTipText(final @Nullable String string) { + super.setToolTipText(string); + + // Also set on label, otherwise it's just on the background without text. + final var toggle= this.toggle; + final var imageLabel= this.imageLabel; + final var textLabel= this.textLabel; + if (toggle != null) { + toggle.setToolTipText(string); + } + if (imageLabel != null) { + imageLabel.setToolTipText(string); + } + if (textLabel != null) { + textLabel.setToolTipText(string); + } + } + + /** + * Tests the expanded state of the composite. + * + * @return <samp>true </samp> if expanded, <samp>false </samp> if collapsed. + */ + public boolean isExpanded() { + return this.expanded; + } + + /** + * Returns the bitwise-ORed style bits for the expansion control. + * + * @return the bitwise-ORed style bits for the expansion control + */ + public int getExpansionStyle() { + return this.expansionStyle; + } + + /** + * Programmatically changes expanded state. + * + * @param expanded + * the new expanded state + */ + public void setExpanded(final boolean expanded) { + internalSetExpanded(expanded); + + final var toggle= this.toggle; + if (toggle != null) { + toggle.setExpanded(expanded); + } + } + + /** + * Performs the expansion state change for the expandable control. + * + * @param expanded + * the expansion state + */ + protected void internalSetExpanded(final boolean expanded) { + if (this.expanded != expanded) { + this.expanded= expanded; + + final var descriptionControl= getDescriptionControl(); + if (descriptionControl != null) { + descriptionControl.setVisible(expanded); + } + if (this.client != null) { + this.client.setVisible(expanded); + } + reflow(); + } + } + + /** + * Adds the listener that will be notified when the expansion state changes. + * + * @param listener + * the listener to add + */ + public void addExpansionListener(final IExpansionListener listener) { + this.listeners.add(listener); + } + + /** + * Removes the expansion listener. + * + * @param listener + * the listener to remove + */ + public void removeExpansionListener(final IExpansionListener listener) { + this.listeners.remove(listener); + } + + /** + * If TITLE_BAR or SHORT_TITLE_BAR style is used, title bar decoration will + * be painted behind the text in this method. The default implementation + * does nothing - subclasses are responsible for rendering the title area. + * + * @param e + * the paint event + */ + protected void onPaint(final PaintEvent e) { + } + + /** + * Returns description control that will be placed under the title if + * present. + * + * @return the description control or <samp>null </samp> if not used. + */ + protected @Nullable Control getDescriptionControl() { + return null; + } + + /** + * Returns the separator control that will be placed between the title and + * the description if present. + * + * @return the separator control or <samp>null </samp> if not used. + */ + protected @Nullable Control getSeparatorControl() { + return null; + } + + /** + * Computes the size of the expandable composite. + * + * @see org.eclipse.swt.widgets.Composite#computeSize + */ + @Override + public Point computeSize(final int wHint, final int hHint, final boolean changed) { + checkWidget(); + final Point size; + final var layout= (ExpandableLayout)getLayout(); + if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { + size= layout.computeSize(this, wHint, hHint, changed); + } else { + size= new Point(wHint, hHint); + } + final Rectangle trim= computeTrim(0, 0, size.x, size.y); + return new Point(trim.width, trim.height); + } + + /** + * Returns <samp>true </samp> if the composite is fixed i.e. cannot be + * expanded or collapsed. Fixed control will still contain the title, + * separator and description (if present) as well as the client, but will be + * in the permanent expanded state and the toggle affordance will not be + * shown. + * + * @return <samp>true </samp> if the control is fixed in the expanded state, + * <samp>false </samp> if it can be collapsed. + */ + protected boolean isFixedStyle() { + return ((this.expansionStyle & TWISTIE) == 0); + } + + /** + * Tests if this expandable composite renders a title bar around the text. + * + * @return <code>true</code> for <code>TITLE_BAR</code> or + * <code>SHORT_TITLE_BAR</code> styles, <code>false</code> + * otherwise. + */ + protected boolean hasTitleBar() { + return ((getExpansionStyle() & TITLE_BAR) != 0 + || (getExpansionStyle() & SHORT_TITLE_BAR) != 0 ); + } + + /** + * Sets the color of the title bar foreground when TITLE_BAR style is used. + * + * @param color + * the title bar foreground + */ + public void setTitleBarForeground(final @Nullable Color color) { + this.titleBarForeground= color; + + final var textLabel= this.textLabel; + if (textLabel != null) { + textLabel.setForeground(color); + } + } + + /** + * Returns the title bar foreground when TITLE_BAR style is used. + * + * @return the title bar foreground + */ + public @Nullable Color getTitleBarForeground() { + return this.titleBarForeground; + } + + // end of APIs + + private void toggleState() { + final boolean newState= !isExpanded(); + fireExpanding(newState, true); + internalSetExpanded(newState); + fireExpanding(newState, false); + if (newState) { + FormToolkit.ensureVisible(this); + } + } + + private void fireExpanding(final boolean state, final boolean before) { + final ImIdentityList<IExpansionListener> listenerList= this.listeners.toList(); + if (listenerList.isEmpty()) { + return; + } + final ExpansionEvent e= new ExpansionEvent(this, state); + for (final IExpansionListener listener : listenerList) { + if (before) { + listener.expansionStateChanging(e); + } + else { + listener.expansionStateChanged(e); + } + } + } + + private void verticalMove(final boolean down) { + final Composite parent= nonNullAssert(getParent()); + final Control[] children= parent.getChildren(); + for (int i= 0; i < children.length; i++) { + final Control child= children[i]; + if (child == this) { + final ExpandableComposite sibling= getSibling(children, i, down); + if (sibling != null && sibling.toggle != null) { + sibling.setFocus(); + } + break; + } + } + } + + private @Nullable ExpandableComposite getSibling(final Control[] children, final int index, + final boolean down) { + int loc= (down) ? index + 1 : index - 1; + while (loc >= 0 && loc < children.length) { + final Control c= children[loc]; + if (c instanceof ExpandableComposite && c.isVisible()) { + return (ExpandableComposite)c; + } + loc= (down) ? loc + 1 : loc - 1; + } + return null; + } + + private void programmaticToggleState() { + final var toggle= this.toggle; + if (toggle != null) { + toggle.setExpanded(!toggle.isExpanded()); + } + toggleState(); + } + + private void paintTitleFocus(final GC gc) { + final var toggle= this.toggle; + final var textLabel= this.textLabel; + if (textLabel != null) { + final Point size= textLabel.getSize(); + gc.setBackground(textLabel.getBackground()); + gc.setForeground(textLabel.getForeground()); + if (toggle != null && toggle.isFocusControl()) { + gc.drawFocus(0, 0, size.x, size.y); + } + } + } + + void reflow() { + Composite c= this; + while (c != null) { + c.setRedraw(false); + c= c.getParent(); + if (c instanceof SharedScrolledComposite || c instanceof Shell) { + break; + } + } + try { + c= this; + while (c != null) { + c.requestLayout(); + c= c.getParent(); + if (c instanceof SharedScrolledComposite) { + ((SharedScrolledComposite)c).reflow(true); + break; + } + if (c instanceof Shell) { + break; + } + } + } + finally { + c= this; + while (c != null) { + c.setRedraw(true); + c= c.getParent(); + if (c instanceof SharedScrolledComposite || c instanceof Shell) { + break; + } + } + } + } + + private void updateLabelTrim() { +// if (textLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).x > textLabel.getSize().x) { +// textLabel.setToolTipText(((Label)textLabel).getText()); +// } +// else { +// textLabel.setToolTipText(null); +// } + } + +} diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableRowsList.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableRowsList.java new file mode 100644 index 00000000..2aca2474 --- /dev/null +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableRowsList.java @@ -0,0 +1,135 @@ +/*=============================================================================# + # Copyright (c) 2012, 2021 Stephan Wahlbrink and others. + # + # This program and the accompanying materials are made available under the + # terms of the Eclipse Public License 2.0 which is available at + # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + # which is available at https://www.apache.org/licenses/LICENSE-2.0. + # + # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + # + # Contributors: + # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation + #=============================================================================*/ + +package org.eclipse.statet.ecommons.ui.swt.expandable; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.forms.FormColors; +import org.eclipse.ui.forms.events.ExpansionAdapter; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.events.IExpansionListener; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.SharedScrolledComposite; + +import org.eclipse.statet.jcommons.lang.NonNullByDefault; +import org.eclipse.statet.jcommons.lang.Nullable; + + +@NonNullByDefault +public class ExpandableRowsList extends SharedScrolledComposite { + + + private static @Nullable FormToolkit gDialogsFormToolkit; + + private static FormToolkit getViewFormToolkit() { + FormToolkit toolkit= gDialogsFormToolkit; + if (toolkit == null) { + final FormColors colors= new FormColors(Display.getCurrent()); + colors.setBackground(null); + colors.setForeground(null); + toolkit= new FormToolkit(colors); + gDialogsFormToolkit= toolkit; + } + return toolkit; + } + + + private final FormToolkit toolkit; + + private @Nullable IExpansionListener expansionListener; + + private int delayReflowCounter; + + + public ExpandableRowsList(final Composite parent) { + this(parent, SWT.H_SCROLL | SWT.V_SCROLL); + } + + public ExpandableRowsList(final Composite parent, final int style) { + super(parent, style); + + if ((style & SWT.H_SCROLL) == 0) { + setExpandHorizontal(true); + } + + setFont(parent.getFont()); + + this.toolkit= getViewFormToolkit(); + + setExpandHorizontal(true); + setExpandVertical(true); + + final Composite body= new Composite(this, SWT.NONE); + setContent(body); + + setFont(parent.getFont()); + setBackgroundMode(SWT.INHERIT_FORCE); + setBackground(getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); + setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND)); + } + + + @Override + @SuppressWarnings("null") + public Composite getContent() { + return (Composite)super.getContent(); + } + + @Override + public void setDelayedReflow(final boolean delayedReflow) { + if (delayedReflow) { + this.delayReflowCounter++; + } + else { + this.delayReflowCounter--; + } + super.setDelayedReflow(this.delayReflowCounter > 0); + } + + public void adaptChild(final Control childControl) { + if (childControl instanceof ExpandableComposite) { + final var expandableComposite= (ExpandableComposite)childControl; + this.toolkit.adapt(expandableComposite, true, false); + var expansionListener= this.expansionListener; + if (expansionListener == null) { + expansionListener= new ExpansionAdapter() { + @Override + public void expansionStateChanged(final ExpansionEvent e) { + expandedStateChanged(); + } + }; + this.expansionListener= expansionListener; + } + expandableComposite.addExpansionListener(expansionListener); + } + else if (childControl instanceof Composite) { + final var composite= (Composite)childControl; + this.toolkit.adapt(composite, false, false); + for (final Control child : composite.getChildren()) { + this.toolkit.adapt(child, true, false); + } + } + else { + this.toolkit.adapt(childControl, true, false); + } + } + + protected void expandedStateChanged() { + reflow(true); + } + +} diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java new file mode 100644 index 00000000..a3528b5b --- /dev/null +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java @@ -0,0 +1,42 @@ +/*=============================================================================# + # Copyright (c) 2017 Stephan Wahlbrink and others. + # All rights reserved. This program and the accompanying materials + # are made available under the terms of the Eclipse Public License v1.0 + # which accompanies this distribution, and is available at + # http://www.eclipse.org/legal/epl-v10.html + # + # Contributors: + # Stephan Wahlbrink <stephan.wahlbrink@walware.de> - initial API and implementation + #=============================================================================*/ + +package org.eclipse.statet.internal.ecommons.ui.swt.css.dom; + +import org.eclipse.e4.ui.css.core.dom.IElementProvider; +import org.eclipse.e4.ui.css.core.engine.CSSEngine; + +import org.w3c.dom.Element; + +import org.eclipse.statet.jcommons.lang.NonNullByDefault; +import org.eclipse.statet.jcommons.lang.Nullable; + +import org.eclipse.statet.ecommons.ui.swt.expandable.ExpandableComposite; + + +@NonNullByDefault +@SuppressWarnings("restriction") +public class ECommonsSwtElementProvider implements IElementProvider { + + + public ECommonsSwtElementProvider() { + } + + + @Override + public @Nullable Element getElement(final Object element, final CSSEngine engine) { + if (element instanceof ExpandableComposite) { + return new ExpandableCompositeElement((ExpandableComposite)element, engine); + } + return null; + } + +} diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ExpandableCompositeElement.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ExpandableCompositeElement.java new file mode 100644 index 00000000..b3514d0a --- /dev/null +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ExpandableCompositeElement.java @@ -0,0 +1,52 @@ +/*=============================================================================# + # Copyright (c) 2017 Stephan Wahlbrink and others. + # All rights reserved. This program and the accompanying materials + # are made available under the terms of the Eclipse Public License v1.0 + # which accompanies this distribution, and is available at + # http://www.eclipse.org/legal/epl-v10.html + # + # Contributors: + # Stephan Wahlbrink <stephan.wahlbrink@walware.de> - initial API and implementation + #=============================================================================*/ + +package org.eclipse.statet.internal.ecommons.ui.swt.css.dom; + +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.swt.dom.CompositeElement; + +import org.eclipse.statet.jcommons.lang.NonNullByDefault; + +import org.eclipse.statet.ecommons.ui.swt.expandable.ExpandableComposite; + + +@NonNullByDefault +@SuppressWarnings("restriction") +public class ExpandableCompositeElement extends CompositeElement { + + + public ExpandableCompositeElement(final ExpandableComposite composite, final CSSEngine engine) { + super(composite, engine); + this.hasMouseHover= true; + } + + + @Override + protected String computeLocalName() { + return "ExpandableComposite"; //$NON-NLS-1$ + } + + @Override + public ExpandableComposite getComposite() { + return (ExpandableComposite)getNativeWidget(); + } + + @Override + public void reset() { + super.reset(); + final var composite= getComposite(); + composite.setTitleBarForeground(null); + composite.setToggleColor(null); + composite.setToggleHoverColor(null); + } + +} diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositeHandler.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositeHandler.java new file mode 100644 index 00000000..7d749d0b --- /dev/null +++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositeHandler.java @@ -0,0 +1,77 @@ +/*=============================================================================# + # Copyright (c) 2017, 2018 Stephan Wahlbrink and others. + # + # This program and the accompanying materials are made available under the + # terms of the Eclipse Public License 2.0 which is available at + # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + # which is available at https://www.apache.org/licenses/LICENSE-2.0. + # + # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + # + # Contributors: + # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation + #=============================================================================*/ + +package org.eclipse.statet.internal.ecommons.ui.swt.css.properties; + +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.swt.properties.AbstractCSSPropertySWTHandler; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Control; + +import org.w3c.dom.css.CSSValue; + +import org.eclipse.statet.jcommons.lang.NonNullByDefault; +import org.eclipse.statet.jcommons.lang.Nullable; + +import org.eclipse.statet.ecommons.ui.swt.expandable.ExpandableComposite; + + +@NonNullByDefault +@SuppressWarnings("restriction") +public class ExpandableCompositeHandler extends AbstractCSSPropertySWTHandler { + + + private static final String TITLE_BAR_FOREGROUND= "swt-titlebar-color"; //$NON-NLS-1$ + + + @Override + protected @Nullable String retrieveCSSProperty(final Control control, final String property, + final @Nullable String pseudo, final CSSEngine engine) throws Exception { + return null; + } + + @Override + protected void applyCSSProperty(final Control control, final String property, + final CSSValue value, + final @Nullable String pseudo, final CSSEngine engine) throws Exception { + if (!(control instanceof ExpandableComposite) + || property == null + || value.getCssValueType() != CSSValue.CSS_PRIMITIVE_VALUE) { + return; + } + final var expandableComposite= (ExpandableComposite)control; + + switch (property.toLowerCase()) { + case TITLE_BAR_FOREGROUND: + if (pseudo == null) { + expandableComposite.setTitleBarForeground( + (Color)engine.convert(value, Color.class, control.getDisplay()) ); + } + return; +// case CSSPropertyFormHandler.TB_TOGGLE: +// if (pseudo == null) { +// expandableComposite.setToggleColor( +// (Color)engine.convert(value, Color.class, control.getDisplay()) ); +// } +// else if (pseudo.equalsIgnoreCase("hover")) { //$NON-NLS-1$ +// expandableComposite.setToggleHoverColor( +// (Color)engine.convert(value, Color.class, control.getDisplay()) ); +// } +// return; + default: + return; + } + } + +} |
