Sometimes we want to give the user a little feedback. "Saved!", "Please wait...", "Loading..", without having to resort to those bulky dialogs.
For these use cases, we have invented MiniMessage. MM can display a small message at the current mouse pointer or above a specific control.
Just paste the source code below into a UI project package and run as java application. It would be nice if this would make it into Nebula
It would be nice if the message box could be round/square/balloon/closable, etc..
Let me know what you think.
/*******************************************************************************
* Copyright (c) 2021 Remain Software -
http://remainsoftware.com * All rights reserved. This program is made available under the terms
* of the Eclipse Public License v1.0 which does not accompanies this
* distribution but is available at
http://www.eclipse.org/legal/epl-v10.html *
* Contributors:
* Wim Jongman - Initial Impl
*******************************************************************************/
package com.remainsoftware.common.licensing.util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
* Shows a mini message for those cases when you want to supply a quick feedback
* to the user without going through the workflow of showing a dialog and
* letting the user press Ok.
* <p/>
* Usage:
* <p/>
* <code>
* // Show mini message just above cursor position
* MiniMessage.get(2000).open("Saved!");
* // Show mini message just above this control
* MiniMessage(2000).get(1000).above(Control).open("Saved!");
* </code>
*
* @author Wim Jongman
*
*/
public class MiniMessage {
public static void main(String[] args) {
Display display = new Display();
MiniMessage.get(2000).openWait("Hello, World!");
display.dispose();
}
/** Error message */
public static final String ERROR = "E";
/** Info message (default) */
public static final String INFO = "I";
/** Warning message */
public static final String WARNING = "W";
private StyledText fStyledText;
private Shell fShell;
private int fWaitBeforeFade = 0;
private Control fControl;
private String fType = INFO;
/**
* Constructs a {@link MiniMessage}.
*
* @param waitBeforeFade waits n milliseconds before the message starts to fade.
* Fading itself takes about a second.
*/
private MiniMessage(int waitBeforeFade) {
this.fWaitBeforeFade = waitBeforeFade;
}
/**
* Factory method to get a new MiniMessage.
*
* @param pWaitBeforeFade
* @return
*/
public static MiniMessage get(int pWaitBeforeFade) {
return new MiniMessage(pWaitBeforeFade);
}
public MiniMessage above(Control pControl) {
this.fControl = pControl;
return this;
}
/**
* Opens the {@link MiniMessage} just above the current cursor position showing
* the passed text. It does not wait until the message has faded.
*
* @param text the text to show as the mini message.
*
* @see #openWait(String)
*/
public void openWait(String message) {
doOpen(message, true);
}
/**
* Submits the mini message to the UI queue so that it is processed at the next
* convenience. Use this is you want to show some message after for example the
* createPartControl method in a view has finished.
*
* Must be called from the UI thread.
*
* @param text the text to show as the mini message.
*
* @see #open(String)
* @see #openWait(String)
*/
public void submit(final String text) {
Runnable runner = () -> doOpen(text, false);
Display.getCurrent().asyncExec(runner);
}
/**
* Opens the {@link MiniMessage} just above the current cursor position showing
* the passed text. It does not wait until the message has faded.
*
* @param text the text to show as the mini message.
*
* @see #openWait(String)
*/
public void open(String text) {
doOpen(text, false);
}
private void doOpen(String text, boolean wait) {
try {
Display display = Display.getDefault();
fShell = new Shell(display, SWT.NO_TRIM | SWT.ON_TOP | SWT.NO_FOCUS);
GridLayout gridLayout = new GridLayout(1, false);
gridLayout.marginWidth = 1;
gridLayout.marginHeight = 1;
gridLayout.verticalSpacing = 0;
gridLayout.horizontalSpacing = 0;
createContents(fShell, SWT.NONE);
setText(text);
Control focusControl = display.getFocusControl();
fShell.setLayout(gridLayout);
fShell.pack();
fShell.setLocation(getLocation());
fShell.open();
fShell.layout();
if (focusControl != null) {
focusControl.forceFocus();
}
Thread thread = new Thread(new Fader(fWaitBeforeFade));
thread.run();
while (wait && !fShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private Point getLocation() {
if (fControl == null || fControl.isDisposed() || fControl.getParent() == null) {
Display display = Display.getDefault();
return display.getCursorLocation();
}
return fControl.getParent().toDisplay(
fControl.getLocation().x + (fControl.getSize().x / 2) - (fShell.getSize().x / 2),
fControl.getLocation().y - fControl.getSize().y - 10);
}
private void setText(String string) {
fStyledText.setText(string);
}
/**
* Create the composite.
*
* @param parent
* @param style
*/
private void createContents(Composite parent, int style) {
GridLayout gridLayout = new GridLayout();
gridLayout.marginWidth = 9;
gridLayout.marginHeight = 9;
parent.setLayout(gridLayout);
fStyledText = new StyledText(parent, SWT.BORDER);
fStyledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
fStyledText.setEditable(false);
fStyledText.setEnabled(false);
if (ERROR.equals(fType)) {
fStyledText.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_RED));
fStyledText.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
}
if (WARNING.equals(fType)) {
fStyledText.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_YELLOW));
fStyledText.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
} else {
fStyledText.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN));
fStyledText.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
}
}
private class Fader implements Runnable {
int fAlpha = 255;
private int fTimerWait = 10;
private int fWaitBeforeFade = 0;
public Fader(int waitBeforeFade) {
this.fWaitBeforeFade = waitBeforeFade;
}
@Override
public void run() {
if (fWaitBeforeFade > 0) {
fShell.getDisplay().timerExec(fWaitBeforeFade, this);
fWaitBeforeFade = 0;
} else if (fAlpha <= 5) {
fShell.dispose();
} else {
fAlpha -= 5;
if (fAlpha < 255) {
fShell.setAlpha(fAlpha);
}
fShell.getDisplay().timerExec(fTimerWait, this);
}
}
}
/**
* Sets the type. If the type is not any of the valid values then
* {@link MiniMessage#INFO} will be assumed.
*
* @param pType
* @return this object for chaining
*
* @see #ERROR
* @see #WARNING
* @see #INFO
*/
public MiniMessage setType(String pType) {
fType = pType;
return this;
}
}