Sapphire Developer Guide > Releases > 8.3

Enhancements in Sapphire 8.3

  1. SapphireDialog
  2. SapphirePreferencePage
  3. TextSelectionService

SapphireDialog

SapphireDialog has been enhanced with some of the features already present in SapphireWizard. In particular, the model's execute method is now called when the dialog is closed if the root model element is an ExecutableElement and a dialog can now be constructed with a model element type instead of a model instance, thus delegating to the dialog the job of disposing of the model.

public class SapphireDialog extends Dialog
{
    /**
     * Constructs a new SapphireDialog instance. Use the open method to actually open the dialog.
     * 
     * <p>The model will be instantiated when the dialog is constructed and disposed when the dialog is closed. To avoid
     * resource leaks, the open method must be called if this constructor is used.</p>
     * 
     * @param shell the shell
     * @param type the root model element type
     * @param definition the dialog definition
     */
    
    public SapphireDialog( Shell shell, ElementType type, DefinitionLoader.Reference<DialogDef> definition )

    /**
     * Constructs a new SapphireDialog instance. Use the open method to actually open the dialog.
     * 
     * @param shell the shell
     * @param element the root model element
     * @param definition the dialog definition
     */
     
    public SapphireDialog( Shell shell, Element element, DefinitionLoader.Reference<DialogDef> definition )
    
    /**
     * Initializes the dialog. This method is called from the constructors. It can be overridden by extenders.
     * 
     * @param element the root model element
     * @param definition the dialog definition
     */
    
    protected void init( Element element, DefinitionLoader.Reference<DialogDef> definition )

    /**
     * Performs tasks that need to run when user closes the dialog by pressing on the ok button. The default implementation
     * invokes the execute method on the associated model element if this element is an ExecutableElement. 
     *  
     * @return true if the dialog can be dismissed or false if an issue was encountered that requires user's attention
     */
    
    protected boolean performOkOperation()

    /**
     * Called when the execute operation fails with an error status. This is only applicable if the associated model element
     * is an ExecutableElement. The default implementation opens a dialog showing the failure message and leaves the dialog open.
     * 
     * @param status the failure status
     * @return true, if the dialog should be closed; false, otherwise
     */
    
    protected boolean handleExecuteFailure( Status status )
}

In the following example, a dialog is used to ask the user for their name. The answer is used to print a personalized message to the console.

public interface ExampleOp extends ExecutableElement
{
    ElementType TYPE = new ElementType( ExampleOp.class );
    
    // *** Name ***
    
    @Required
    
    ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
    
    Value<String> getName();
    void setName( String value );
    
    // *** execute ***
    
    @DelegateImplementation( ExampleExecuteMethodDelegate.class )
        
    Status execute( ProgressMonitor monitor );
}
public final class ExampleExecuteMethodDelegate
{
    public static Status execute( final ExampleOp op, final ProgressMonitor monitor )
    {
        System.err.println( "Hello, " + op.getName().text() + "!" );
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<definition>
    <dialog>
        <label>example dialog</label>
        <content>
            <property-editor>Name</property-editor>
        </content>
    </dialog>
</definition>
new SapphireDialog( shell, ExampleOp.TYPE, DefinitionLoader.sdef( ExampleOp.class ).dialog() ).open();

SapphirePreferencePage

Creating a preference page using Sapphire is now simpler with the new SapphirePreferencePage class. This class complements SapphirePropertyPage, which exists in previous releases.

public final class ExamplePreferencePage extends SapphirePreferencePage
{
    private ExamplePreferences preferences;

    public ExamplePreferences()
    {
        // Instantiate the model that is able to read and write preferences on disk
        
        this.preferences = ...;
        
        // Initialize the page with the model and the UI definition
        
        init( this.preferences, DefinitionLoader.sdef( ExamplePreferencePage.class ).form() );
    }
    
    @Override
    
    public void dispose()
    {
        this.preferences.dispose();
        super.dispose();
    }
}

TextSelectionService

TextSelectionService is a conduit between the presentation layer and anything that needs to observe or change the selection in a text property editor.

public final class TextSelectionService extends Service
{
    /**
     * Returns the current text selection.
     */
    
    public Range selection()

    /**
     * Places the caret at the specified position. This is equivalent to setting the selection to a zero-length
     * range starting at this position. If selection changes, TextSelectionEvent will be fired.
     * 
     * @param position the desired caret position
     */
    
    public void select( int position )

    /**
     * Selects a text range. If selection changes, TextSelectionEvent will be fired.
     * 
     * @param start the starting position of the text range
     * @param end the ending position of the text range (non-inclusive)
     */
    
    public void select( int start, int end )
    
    /**
     * Selects a text range. If selection changes, TextSelectionEvent will be fired.
     * 
     * @param start the starting position of the text range
     * @param end the ending position of the text range (non-inclusive)
     */
    
    public void select( Range range )
    
    /**
     * Represents an immutable range of characters defined by a starting and an ending position.
     */
    
    public static final class Range
    {
        /**
         * Constructs a new Range object.
         * 
         * @param start the starting position of the text range
         * @param end the ending position of the text range (non-inclusive)
         */
        
        public Range( int start, int end )
        
        /**
         * Returns the starting position of the text range.
         */

        public int start()
        
        /**
         * Returns the ending position of the text range.
         */
        
        public int end()
        
        /**
         * Returns the length of the text range.
         */
        
        public int length()
    }
    
    /**
     * The event that is fired when text selection changes.
     */

    public static final class TextSelectionEvent extends ServiceEvent
    {
        /**
         * Returns the text selection before the selection was changed.
         */

        public Range before()
        
        /**
         * Returns the text selection after the selection was changed.
         */

        public Range after()
    }
}

In this example, an action handler inserts a variable that user chooses at the text selection point. This example and others are available in runnable form in the samples project.

public abstract class InsertVariableActionHandler extends SapphireActionHandler
{
    @Override
    
    protected final Object run( final Presentation context )
    {
        final String variable = choose();
        
        if( variable != null )
        {
            final PropertyEditorPart part = (PropertyEditorPart) context.part();
            final Value<?> property = (Value<?>) part.property();
            final TextSelectionService textSelectionService = part.service( TextSelectionService.class );
            final TextSelectionService.Range initialSelection = textSelectionService.selection();
            final String initialText = property.text();
            
            final StringBuilder modifiedText = new StringBuilder();
            
            if( initialText != null )
            {
                modifiedText.append( initialText.substring( 0, initialSelection.start() ) );
            }
            
            modifiedText.append( "${" );
            modifiedText.append( variable );
            modifiedText.append( '}' );
            
            if( initialText != null )
            {
                modifiedText.append( initialText.substring( initialSelection.end() ) );
            }
            
            property.write( modifiedText.toString() );
            
            textSelectionService.select( initialSelection.start() + variable.length() + 3 );
        }
        
        return null;
    }
    
    protected abstract String choose();
}