com.diffplug.common.swt.jface.ImageDescriptors.java Source code

Java tutorial

Introduction

Here is the source code for com.diffplug.common.swt.jface.ImageDescriptors.java

Source

/*
 * Copyright 2016 DiffPlug
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.diffplug.common.swt.jface;

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

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Widget;

import com.diffplug.common.base.Box;
import com.diffplug.common.base.Errors;
import com.diffplug.common.base.Unhandled;
import com.diffplug.common.swt.OnePerWidget;

/** Utilities for using {@link ImageDescriptor}s correctly. */
public class ImageDescriptors {
    /**
     * {@link ImageDescriptor} allows an {@link Image} to be shared in a pool using reference counting. In order to not screw-up the reference
     * counting, you need to be pretty careful with how you use them.
     * <p>
     * This creates a {@link com.diffplug.common.base.Box.Nullable Box.Nullable&lt;ImageDescriptor&gt;} which sets and gets images in a way that will keep the reference counting happy.
     * <p>
     * <b>NO ONE MUST SET THE IMAGE EXCEPT THIS SETTER.</b>
     *
     * @param lifecycle   Any outstanding images will be destroyed with the lifecycle of this Widget.
     * @param imageGetter Function which returns the image currently on the Widget (used to ensure that nobody messed with it).
     * @param imageSetter Function which sets the image on the Widget.
     * @return A `Box.Nullable<ImageDescriptor>` for setting the {@link Image} using an {@link ImageDescriptor}.
     */
    public static Box.Nullable<ImageDescriptor> createSetter(Widget lifecycle, Supplier<Image> imageGetter,
            Consumer<Image> imageSetter) {
        return new Box.Nullable<ImageDescriptor>() {
            private ImageDescriptor lastDesc;
            private Image lastImg;

            {
                // when the control is disposed, we'll clear the image
                lifecycle.addListener(SWT.Dispose, e -> {
                    if (lastDesc != null) {
                        lastDesc.destroyResource(lastImg);
                    }
                });
            }

            @Override
            public ImageDescriptor get() {
                return lastDesc;
            }

            @Override
            public void set(ImageDescriptor newDesc) {
                // make sure nobody else messed with the image
                if (imageGetter.get() != lastImg) {
                    // if someone else did mess with it, we can probably survive, so best to just
                    // log the failure and continue with setting the image
                    Errors.log().accept(
                            new IllegalStateException("Setter must have exclusive control over the image field."));
                }

                // set the new image
                Image newImg;
                if (newDesc != null) {
                    newImg = (Image) newDesc.createResource(lifecycle.getDisplay());
                } else {
                    newImg = null;
                }
                imageSetter.accept(newImg);

                // if an image was already set, destroy it
                if (lastDesc != null) {
                    lastDesc.destroyResource(lastImg);
                }

                // save the fields for the next go-round
                lastDesc = newDesc;
                lastImg = newImg;
            }
        };
    }

    /** Global cache of widget -> image descriptor setters. */
    private static final OnePerWidget<Widget, Box.Nullable<ImageDescriptor>> globalSetter = OnePerWidget
            .from((Widget widget) -> {
                if (widget instanceof Item) {
                    Item cast = (Item) widget;
                    return createSetter(cast, cast::getImage, cast::setImage);
                } else if (widget instanceof Button) {
                    Button cast = (Button) widget;
                    return createSetter(cast, cast::getImage, cast::setImage);
                } else if (widget instanceof Label) {
                    Label cast = (Label) widget;
                    return createSetter(cast, cast::getImage, cast::setImage);
                } else {
                    throw Unhandled.classException(widget);
                }
            });

    /** Sets the given {@link Item} to have the image described by the given descriptor, maintaining proper reference counting. */
    public static void set(Item widget, ImageDescriptor image) {
        globalSetter.forWidget(widget).set(image);
    }

    /** Sets the given {@link Button} to have the image described by the given descriptor, maintaining proper reference counting. */
    public static void set(Button widget, ImageDescriptor image) {
        globalSetter.forWidget(widget).set(image);
    }

    /** Sets the given {@link Label} to have the image described by the given descriptor, maintaining proper reference counting. */
    public static void set(Label widget, ImageDescriptor image) {
        globalSetter.forWidget(widget).set(image);
    }
}