<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you 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.
-->

<document id="meters">
    <properties>
        <title>Meters</title>
    </properties>

    <body>
        <p>
            Meters are used to report progress to the user, generally the completion percentage of
            a background task such as a file load operation. An example is shown below. Clicking
            the Start button initiates a background task that simulates a long-running operation.
            Periodically, the task updates the meter to reflect its progress:
        </p>

        <application class="org.apache.pivot.wtk.ScriptApplication"
            width="300" height="100">
            <libraries>
                <library>core</library>
                <library>wtk</library>
                <library>wtk-terra</library>
                <library>tutorials</library>
            </libraries>
            <startup-properties>
                <src>/org/apache/pivot/tutorials/progress/meters.bxml</src>
            </startup-properties>
        </application>

        <p>
            The BXML source for the example is shown below:
        </p>

        <source type="xml" location="org/apache/pivot/tutorials/progress/meters.bxml">
            <![CDATA[
            <progress:Meters title="Meters" maximized="true"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:progress="org.apache.pivot.tutorials.progress"
                xmlns="org.apache.pivot.wtk">
                <TablePane>
                    <columns>
                        <TablePane.Column width="1*"/>
                    </columns>

                    <TablePane.Row height="1*">
                        <Border styles="{padding:2}">
                            <BoxPane styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
                                <Label text="Progress:"/>
                                <Meter bxml:id="meter" preferredWidth="200" preferredHeight="16"/>
                            </BoxPane>
                        </Border>
                    </TablePane.Row>

                    <TablePane.Row height="-1">
                        <BoxPane styles="{horizontalAlignment:'center', padding:6}">
                            <PushButton bxml:id="progressButton" styles="{minimumAspectRatio:3}"/>
                        </BoxPane>
                    </TablePane.Row>
                </TablePane>
            </progress:Meters>
            ]]>
        </source>

        <p>
            The Java source is as follows. The main window class defines an inner class that
            extends the <tt>org.apache.pivot.util.concurrent.Task</tt> class to simulate a background
            operation. This task simply sleeps for 100ms at a time and updates the meter when it
            wakes up to reflect the current count. Since all UI updates must be performed on the
            UI thread, the task queues a callback to set the meter's percent complete; a
            <tt>org.apache.pivot.wtk.TaskAdapter</tt> is used to process the task notifications for
            the same reason:
        </p>

        <source type="java" location="org/apache/pivot/tutorials/progress/Meters.java">
            <![CDATA[
            package org.apache.pivot.tutorials.progress;

            import java.net.URL;

            import org.apache.pivot.beans.Bindable;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.util.Resources;
            import org.apache.pivot.util.concurrent.Task;
            import org.apache.pivot.util.concurrent.TaskExecutionException;
            import org.apache.pivot.util.concurrent.TaskListener;
            import org.apache.pivot.wtk.ApplicationContext;
            import org.apache.pivot.wtk.Button;
            import org.apache.pivot.wtk.ButtonPressListener;
            import org.apache.pivot.wtk.Meter;
            import org.apache.pivot.wtk.PushButton;
            import org.apache.pivot.wtk.TaskAdapter;
            import org.apache.pivot.wtk.Window;

            public class Meters extends Window implements Bindable {
                public class SampleTask extends Task<Void> {
                    private int percentage = 0;

                    @Override
                    public Void execute() throws TaskExecutionException {
                        // Simulate a long-running operation
                        percentage = 0;

                        while (percentage < 100
                            && !abort) {
                            try {
                                Thread.sleep(100);
                                percentage++;

                                // Update the meter on the UI thread
                                ApplicationContext.queueCallback(new Runnable() {
                                    @Override
                                    public void run() {
                                        meter.setPercentage((double)percentage / 100);
                                    }
                                });
                            } catch(InterruptedException exception) {
                                throw new TaskExecutionException(exception);
                            }
                        }

                        return null;
                    }
                }

                private Meter meter = null;
                private PushButton progressButton = null;

                private SampleTask sampleTask = null;

                @Override
                public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
                    meter = (Meter)namespace.get("meter");
                    progressButton = (PushButton)namespace.get("progressButton");

                    progressButton.getButtonPressListeners().add(new ButtonPressListener() {
                        @Override
                        public void buttonPressed(Button button) {
                            if (sampleTask == null) {
                                // Create and start the simulated task; wrap it in a
                                // task adapter so the result handlers are called on the
                                // UI thread
                                sampleTask = new SampleTask();
                                sampleTask.execute(new TaskAdapter<Void>(new TaskListener<Void>() {
                                    @Override
                                    public void taskExecuted(Task<Void> task) {
                                        reset();
                                    }

                                    @Override
                                    public void executeFailed(Task<Void> task) {
                                        reset();
                                    }

                                    private void reset() {
                                        // Reset the meter and button
                                        sampleTask = null;
                                        meter.setPercentage(0);
                                        updateProgressButton();
                                    }
                                }));
                            } else {
                                // Cancel the task
                                sampleTask.abort();
                            }

                            updateProgressButton();
                        }
                    });

                    updateProgressButton();
                }

                private void updateProgressButton() {
                    if (sampleTask == null) {
                        progressButton.setButtonData("Start");
                    } else {
                        progressButton.setButtonData("Cancel");
                    }
                }
            }
            ]]>
        </source>
    </body>
</document>
