de.metadocks.lambdaui.swt.SwtUI.java Source code

Java tutorial

Introduction

Here is the source code for de.metadocks.lambdaui.swt.SwtUI.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Erdal Karaca 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:
 *     Erdal Karaca - initial API and implementation
 *******************************************************************************/
package de.metadocks.lambdaui.swt;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.ComputedValue;
import org.eclipse.jface.databinding.swt.DisplayRealm;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.IWidgetValueProperty;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

import de.metadocks.xprbinds.binding.Binder;

public abstract class SwtUI<T extends Control> {

    private static final String PREFIX = SwtUI.class.getName();
    protected static final String VIEWER = PREFIX + ".viewer";
    protected static final String DATA_CONTEXT = PREFIX + ".dataContext";
    protected static String ID = PREFIX + ".id";

    // no need to be thread-safe as UI is always constructed within the UI
    // thread
    protected static Object currentParent;

    public abstract T control();

    public SwtUI<T> id(String id) {
        tag(ID, id);
        return this;
    }

    private <P> void tag(String key, P value) {
        control().setData(key, value);
    }

    protected <P> P findTagged(Class<P> key) {
        return findTagged(key, null);
    }

    private <P> P findTagged(Class<P> key, P defaultValue) {
        return findTagged(key.getName(), defaultValue);
    }

    @SuppressWarnings("unchecked")
    protected <P> P findTagged(String key, P defaultValue) {
        Control ctx = control();

        do {
            P data = (P) ctx.getData(key);
            if (data != null) {
                return data;
            }
            ctx = ctx.getParent();
        } while (ctx != null);

        // check for null default value?
        tag(key, defaultValue);
        return defaultValue;
    }

    public SwtUI<T> text(String text) {
        return prop(WidgetProperties.text(), text);
    }

    public SwtUI<T> text(int event, String text) {
        return prop(WidgetProperties.text(event), text);
    }

    public SwtUI<T> prop(Supplier<IWidgetValueProperty> propSupplier, Object value) {
        return prop(propSupplier.get(), value);
    }

    public SwtUI<T> prop(IWidgetValueProperty prop, Object value) {
        if (value instanceof String) {
            bind(prop, (String) value);
        } else {
            // no binding expression
            prop.setValue(control(), value);
        }

        return this;
    }

    private void bind(IWidgetValueProperty prop, String expr) {
        Object dataContext = findTagged(DATA_CONTEXT, null);
        Binder dbc = findTagged(Binder.class);
        ISWTObservableValue observableValue = prop.observe(control());
        org.eclipse.core.databinding.Binding binding = dbc.bind(observableValue, dataContext, expr);

        if (binding == null) {
            // no observables have been parsed, just use the value
            prop.setValue(control(), expr);
        } else {
            // set non editable if this was a multi binding
            if (binding.getModel() instanceof ComputedValue) {
                try {
                    WidgetProperties.editable().setValue(control(), false);
                } catch (Exception e) {
                    // ignore, not a supported editable widget
                }
            }
        }
    }

    public SwtUI<T> dataContext(Object dataContext) {
        tag(DATA_CONTEXT, dataContext);
        return this;
    }

    public SwtUI<T> layout(Layout layout) {
        ((Composite) control()).setLayout(layout);
        return this;
    }

    public SwtUI<T> layoutData(Object layoutData) {
        ((Control) control()).setLayoutData(layoutData);
        return this;
    }

    public SwtUI<T> childControl(Supplier<SwtUI<? extends Control>> supplier) {
        currentParent = control();
        supplier.get();
        return this;
    }

    public <C extends Viewer> SwtUI<T> childViewer(Supplier<ViewerUI<? extends Viewer>> supplier) {
        currentParent = control();
        supplier.get();
        return this;
    }

    public SwtUI<T> customize(Consumer<T> consumer) {
        consumer.accept(control());
        return this;
    }

    public SwtUI<T> customizeUI(Consumer<SwtUI<T>> consumer) {
        consumer.accept(this);
        return this;
    }

    public SwtUI<T> on(int swtEvent, Listener listener) {
        control().addListener(swtEvent, listener);
        return this;
    }

    public <C extends Control> SwtUI<T> withControl(String id, Consumer<C> consumer) {
        C found = find(id, control());

        if (found == null) {
            throw new IllegalArgumentException("Control not found: " + id);
        }

        syncExec(() -> consumer.accept(found));
        return this;
    }

    public <V extends Viewer> SwtUI<T> withViewerUI(String id, Consumer<ViewerUI<V>> consumer) {
        V viewer = findViewer(id);
        consumer.accept(ViewerUI.wrapViewer(viewer));
        return this;
    }

    public SwtUI<T> syncExec(Runnable code) {
        T control = control();

        if (Thread.currentThread() != control.getDisplay().getThread()) {
            control.getDisplay().syncExec(code);
        } else {
            code.run();
        }

        return this;
    }

    public SwtUI<T> asyncExec(Runnable code) {
        T control = control();

        control.getDisplay().asyncExec(() -> {
            if (!control.isDisposed()) {
                code.run();
            }
        });

        return this;
    }

    public SwtUI<T> timerExec(int delay, Runnable code) {
        T control = control();

        control.getDisplay().timerExec(delay, () -> {
            if (!control.isDisposed()) {
                code.run();
            }
        });

        return this;
    }

    public <C extends Control> C find(String id) {
        return find(id, control());
    }

    @SuppressWarnings("unchecked")
    private <C extends Control> C find(String id, T context) {
        String contextId = (String) context.getData(ID);

        if (id.equals(contextId)) {
            return (C) context;
        }

        if (context instanceof Composite) {
            for (Control c : ((Composite) context).getChildren()) {
                Control found = find(id, (T) c);

                if (found != null) {
                    return (C) found;
                }
            }
        }

        return null;
    }

    @SuppressWarnings("unchecked")
    public <V extends Viewer> V findViewer(String id) {
        Control control = control();
        Control found = find(id, (T) control);

        if (found.getData(VIEWER) instanceof Viewer) {
            return (V) found.getData(VIEWER);
        }

        throw new IllegalArgumentException("Viewer not found:" + id);
    }

    public static <T extends Control> SwtUI<T> create(BiFunction<Composite, Integer, T> ctor) {
        return create(ctor, SWT.None);
    }

    public static <T extends Control> SwtUI<T> create(BiFunction<Composite, Integer, T> ctor, int style) {
        if (currentParent == null) {
            throw new IllegalStateException("Invalid child creation context.");
        }

        Composite parent = (Composite) currentParent;

        try {
            return create(ctor, parent, style);
        } finally {
            currentParent = null;
        }
    }

    public static <T extends Control> SwtUI<T> create(BiFunction<Composite, Integer, T> ctor, Composite parent) {
        return create(ctor, parent, SWT.None);
    }

    public static <T extends Control> SwtUI<T> create(BiFunction<Composite, Integer, T> ctor, Composite parent,
            int style) {
        T widget = ctor.apply(parent, style);
        return wrap(widget);
    }

    public static <T extends Control> SwtUI<T> wrap(T control) {
        SwtUI<T> builder = new SwtUI<T>() {
            @Override
            public T control() {
                return control;
            }
        };

        // TODO get ObservableFactoriesRegistry from OSGi
        Binder bindingContext = Binder.create(null);
        builder.findTagged(Binder.class, bindingContext);
        control.addDisposeListener(evt -> {
            bindingContext.dispose();
        });
        return builder;
    }

    public static void openInShell(Consumer<SwtUI<Shell>> uiConsumer) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());

        Realm.runWithDefault(DisplayRealm.getRealm(display), () -> {
            uiConsumer.accept(SwtUI.wrap(shell));
            shell.open();
            shell.layout();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch())
                    display.sleep();
            }
            display.dispose();
        });
    }
}