その他のコントロール

スピン コントロール

SpinControl を使用すると、一連の値から 1 つの値を表示し選択することができます。特に、昇順または降順に自然にソートされる値セットを使用する場合に特に便利なコントロールです。上向き矢印または下向き矢印をクリックするか、キーボードの上方向キーまたは下方向キーを押して、現在より高い値や低い値を選択できます。

連続値の指定

SpinControl は、Domain オブジェクトを使用して一連の値を定義します。StandardIntDomain などの多くの標準ドメインでは、既定値を指定します。最小値と最大値が存在しない場合は null を使用します。スピン コントロールで使用可能な値を無限にすることはできません。そのため、SpinControlDomain を使用する場合は、最小値と最大値を指定する必要があります。使用可能な値は、最大値と最小値の間にある、指定したデータ型の値です。
次の例は、-10 ~ 10 の整数から値を選択できる基本的な SpinControl を示しています。

例: 単純な SpinControl
{SpinControl
    domain = 
        {StandardIntDomain
            default-value = 0,
            min-allowable = -10,
            max-allowable = 10
        }
}
SpinControl.step プロパティは、ドメイン内の次の値までの増分を制御します。このコントロールは現在値を step の値だけ増加させます。既定値は整数の 1 です。
次の例では、step を 5 に設定します。上向き矢印をクリックして値が 0 から 5 に変わることに注目してください。キーボードを使用して値を編集すると、コントロールはその新しい値から 5 ずつ増加させます。

例: step が 5 の場合
{SpinControl
    domain = 
        {StandardIntDomain
            default-value = 0,
            min-allowable = -100,
            max-allowable = 100
        },
    step = 5
}
SpinControl.editable? プロパティーとともに step を使用すると、使用可能なすべての値をリストしなくても、使用可能な値を制限できます。次の例では、step を 5 に、editable?false に設定します。この組み合わせは値の選択肢を -100 から 100までの 5 の倍数に制限します。

例: 値を 5 の倍数に制限
{SpinControl
    domain = 
        {StandardIntDomain
            default-value = 0,
            min-allowable = -100,
            max-allowable = 100
        },
editable? = false,
    step = 5
}
step プロパティでは、現在の値から次の値までの増分を設定します。そのため、step には、SpinControl で使用する Domain と互換性のあるデータ型の値を指定する必要があります。次の例では、StandardTimeDomain を使用しているため、step に時間値を指定する必要があります。
このコントロールでは、0 秒から 12 時までの時間値はすべて有効です。step プロパティでは、増分として 15 分を指定しています。ただし、step は使用可能な値は制限していません。コントロールを編集して、指定の範囲内で有効な時間値を入力できます。

例: Time による SpinControl の使用
{SpinControl
    width = 3cm,
    domain = 
        {StandardTimeDomain
            default-value = 0s,
            min-allowable = 0s,
            max-allowable = 12h
        },
    step = 15min 
}
SpinControl.wrap? プロパティは、最大値または最小値に達したときのコントロールの動作を制御します。wrap?true の場合は、最大値または最小値を超えて、逆の限界値に折り返します。既定値は false です。
スピン コントロールを使用して、月の日付を選択する例を次に示します。wrap?true に設定しているため、コントロールは 1 から 31 に進み、31 に到達すると 1 に戻ります。

例: wrap? の使用
{SpinControl
    domain = 
        {StandardIntDomain
            default-value = 1,
            min-allowable = 1,
            max-allowable = 31
        },
    wrap? = true
}

許容値の指定

SpinControl.allowable-values プロパティーはそのコントロールで選択可能な値のリストを持ちます。これまでの例では SpinControl で連続した値を使用しており、SpinControl.allowable-values はドメインの Domain.allowable-values から導き出されています。
また、値の配列を指定することもできます。この場合、配列の要素は SpinControl.allowable-values で昇順にソートされます。「プログラムによる SpinControl の操作 」の例では、許容値の配列を使用しています。
列挙型によって指定された値はソートされないので、列挙型を使用して値セットを特定の順序に指定することができます。
次の例は、曜日を含む列挙型を指定します。これらの値は、ソート順ではなく、曜日順に表示する必要があります。ユーザーはコントロール値を直接編集できますが、いずれかの有効な値に正確に一致する文字列を入力する必要があります。たとえば、Friday は有効な入力値ですが、friday は無効です。friday を有効な入力とするには、parse-spec を指定する必要があります。「解析と書式設定」を参照してください。editable? プロパティを false に設定すると、ユーザー入力を無効にできます。

例: SpinControl での列挙型の使用
{define-enum public Days
    Sunday, 
    Monday, 
    Tuesday, 
    Wednesday,
    Thursday, 
    Friday, 
    Saturday
}
  {SpinControl
    width = 3cm,          
|| 以下のコメントをはずすと編集ができなくなります。
||--        editable? = false,
    domain =
        {Domain.from-type
            Days
        }
}
場合によっては、列挙型を使用して得られる以上の柔軟性が必要かもしれません。たとえば、コントロールで使用可能な値が、ユーザーの行った選択によって決定される場合などです。そのような状況では、配列へのインデックス値としてスピン コントロール内で整数の値を使用することができます。その後、配列の中身を動的に決定することができます。スピン コントロールを使用して配列から対応する値を取得する format-spec を作成する必要があります。format-spec は配列から文字列を取得できなければなりません。format-spec がスピン コントロールのテキスト表示フィールドで使用される値を指定するからです。
整数を使用して文字列の配列のインデックスをつけることはもっとも簡単な方法です。この方法は、文字列を他の型のオブジェクトに関連付けるような、より複雑なオブジェクトの配列にも使用できます。
次の例でこの方法を説明します。スピン コントロールで 2 セットの値から選択します。これらの値は2つの配列によって指定されています。月曜から金曜までの曜日名を持つ weekdays または、土曜と日曜の名前を持つ weekend です。
array-spinner プロシージャは StringArray の値に特化された SpinControl を作成します。コントロールを作成する際に配列を指定します。コンストラクタは配列を使用して使用可能な整数の最大値を設定し、整数値を文字列としてフォーマットします。プロシージャの残余引数で SpinControl への追加引数を指定することができます。残余引数の使用についての詳細は「引数」を参照してください。
spin-over-array プロシージャは array-spinner で作成されたコントロールを特定の文字列配列に関連付けます。week-spinner コントロールは array-spinner を使用して用例の表示で表示されるコントロールを作成します。このコントロールは TextDisplay のコンテンツを更新し、配列から整数値とそれに対応する文字列を取得し表示します。これによりコントロールの現在値を見ることができます。
それぞれの CommandButtonweek-spinner で使用された配列を weekend または weekday に設定します。

例: 配列のインデックス作成への SpinControl の使用
{let weekdays:{Array-of String} = 
    {{Array-of String}  
        "Monday", 
        "Tuesday", 
        "Wednesday",
        "Thursday", 
        "Friday"
    }
}
{let weekend:{Array-of String} = 
    {{Array-of String}  
        "Saturday",
        "Sunday"
    }
}
{define-proc {array-spinner a:StringArray, ...}:SpinControl
    {return
        {SpinControl {splice ...},
            domain = 
                {StandardIntDomain
                    default-value = 0,
                    min-allowable = 0,
                    max-allowable = a.size - 1
                },
            format-spec = 
                {proc {data:any, sp:SpinControl}:String
                    {return a[data asa int]}
                }
        }
    }
}

{define-proc {spin-over-array a:StringArray, sp:SpinControl}:void
    set sp.value = 0
    set sp.domain = 
        {StandardIntDomain
            default-value = 0,
            min-allowable = 0,
            max-allowable = a.size - 1
        }
    set sp.format-spec = 
        {proc {data:any, sp:SpinControl}:String
            {return a[data asa int]}
        }
    {sp.set-value-with-events 0}
}
{let display:TextDisplay = {TextDisplay}}
{let week-spinner:SpinControl = 
    {array-spinner 
        weekdays, 
        width = 4cm, 
        wrap? = true,
        {on ValueFinished at sp:SpinControl do
            set display.value = 
                {type-switch sp.format-spec
                 case str:String do 
                    {format str, sp.value}
                 case proc:{proc-type {any, SpinControl}:String} do 
                    {format "%s %s", sp.value, {proc sp.value, sp}}
                 else 
                    {format "%s", sp.value}
                }
        }
    }
}
{Dialog
    margin = 3pt,
    {VBox
        spacing = 2pt,
        week-spinner,
        {HBox 
            "Value =\ ",
            display
        },
        {HBox
            {CommandButton
                width = {make-elastic},
                label = "Weekend",
                {on Action do
                    {spin-over-array weekend, week-spinner}
                }
            },
            {CommandButton
                width = {make-elastic},
                label = "Weekday",
                {on Action do
                    {spin-over-array weekdays, week-spinner}
                }
            }
        }
    }
}

解析と書式設定

SpinControl.parse-spec および SpinControl.format-spec プロパティで、コントロール値をユーザーに表示する方法を制御できます。
次の例では、16 進形式の整数値を解析してフォーマットします。format-spec は、format マクロに適した文字列です。parse-spechex-parse というプロシージャで、文字列入力を 16 進数として解釈します。文字列が有効な入力でない場合、parse-specValidationException をスローしてコントロールに有効な入力でないことを表示します。コントロールは値を変更しません。

例: format-spec および parse-spec の使用
{define-proc package {hex-parse
                         str:String, 
                         sp:SpinControl
                     }:any                        
    let (new-value:int, n-chars-consumed:int, overflow?:bool) =
        {str.to-int radix = 16, error-if-overflow? = false}
    {if not overflow? and 
        new-value >= (sp.min-value asa int) and
        new-value <= (sp.max-value asa int)
     then
        {return new-value}
    }
    {throw
        {ValidationException "Not a Valid Value"}
    }
}
{SpinControl
    width = 3cm,
    domain = 
        {StandardIntDomain
            default-value = 0,
            min-allowable = 0,
            max-allowable = 255
        },
    font-size = 18pt,
    format-spec = "%0X",
    parse-spec = hex-parse
}

プログラムによる操作

SpinControl には、次のメソッドが用意されています。これらのメソッドを使用すると、ユーザーによるコントロールの操作をプログラムでシミュレーションできます。
set-value-with-events を使用して、多数のコントロールを既定値に設定する "リセット" ボタンを実装できます。次の例では、SpinControl.set-value-with-events メソッドを使用して 2 つのスピン コントロールを既定値に設定します。SpinControl オブジェクトのイベント ハンドラのコメントを解除すると、このメソッドにより正常な ValueFinished イベントが発生するのを確認できます。
ドメインに指定された整数配列の値が SpinControl.allowable-values でソートされるため、コントロール値を増減させると、値は数値順に表示されます。値を特定の順序で指定する必要がある場合は、「SpinControl での列挙型の使用」の例に示すように列挙型を使用します。

例: プログラムによる SpinControl の操作
{let arr:{Array-of int} = {{Array-of int} 20, 50, 30, 10, 40}}
{let spin-one:SpinControl =
    {SpinControl
|| 以下のコメントをはずすとイベントが確認できます。
||--            {on e:ValueFinished at x:SpinControl do
||--                {popup-message x.value}
||--            },
        domain = 
            {StandardIntDomain
                default-value = 30,
                allowable-values = arr
            }
    }
}
{let spin-two:SpinControl =
    {SpinControl
|| 以下のコメントをはずすとイベントが確認できます。
||--            {on e:ValueFinished at x:SpinControl do
||--                {popup-message x.value}
||--            },
        domain = 
            {StandardIntDomain
                default-value = 50,
                min-allowable = 0,
                max-allowable = 100
            }
    }
}
{VBox
    spin-one,
    spin-two,
    {HBox
        {CommandButton
            label = "set to default values",
            {on Action do
                {spin-one.set-value-with-events spin-one.domain.default-value}
                {spin-two.set-value-with-events spin-one.domain.default-value}
            }
        }
    }
}

カレンダー コントロール

CalendarControl を使用すると、グラフィカルなカレンダーから日付を選択できます。単純な CalendarControl の例を次に示します。コントロールでは現在の月と年が表示され、現在の日付は、数字を含む長方形の背景色で示されています。このコントロールには、初期値は設定されていません。数字をクリックして日付を選択すると、コントロール値が設定されます。選択した日付の周囲に色付きの境界線が引かれ、現在の値が示されます。
上部にある 2 つのスピン コントロールを使用して、表示する年と月を切り替えることができます。

例: 単純な CalendarControl
{CalendarControl}
非ローカル オプションを設定して、CalendarControl で使用する色を変更できます。次のリストにこのようなオプションを示します。
これらのオプションを変更した結果を次の例に示します。

例: CalendarControl の色の設定
{CalendarControl
    calendar-control-header-background = "#cceecc",
    calendar-control-selected-day-color = "red",
    calendar-control-today-background = "#cceecc"
}
次のブール値オプションを使用して、表示する情報を追加したり削除したりできます。
これらのオプションの値を変更した結果を次の例に示します。

例: CalendarControl による表示情報の変更
{CalendarControl
    show-week-of-year? = true,
    show-adjacent-dates? = true,
    show-date-controls? = false
}

値とイベント

次の例では、CalendarControl.value の変更の結果、ValueChanged イベントと ValueFinished イベントが発生します。CalendarControl には初期値は設定されていません。日付をクリックするか、日付のスピン コントロールで新しい月または年を選択して値を選択すると、選択した値が calendar-control-selected-day-color の境界線で示されます。CalendarControl.unset-value を使用して値の設定を解除すると、選択日の境界線が表示されなくなりますが、ValueChanged イベントと ValueFinished イベントは発生しません。

例: CalendarControl と関連イベント
{let changed-value:TextField = {TextField}}
{let finished-value:TextField = {TextField}}
{let cc:CalendarControl = 
    {CalendarControl
        {on ValueChanged at s:CalendarControl do
            set changed-value.value = {String s.value.info.locale-date}
        },
        {on ValueFinished at s:CalendarControl do
            set finished-value.value = {String s.value.info.locale-date}
        }
    }
}
{VBox
    cc,
    {Table
        {row-prototype "Changed value: ", {value changed-value}},
        {row-prototype "Finished value: ", {value finished-value}}
    },
    {CommandButton
        label = "unset data field",
        {on Action do
            {cc.unset-value}
        }
    }
}

データ モデルの指定

DateDataModel を使用して CalendarControl.data-model の値を指定できます。データ モデルの min-valuemax-value、および value によって、CalendarControlmin-valuemax-value、および value が設定されます。カレンダー コントロールで選択した内容によって、基になるデータ モデルの value が変更されることもあります。
次の例では、data-model によって最大値と最小値、およびコントロールの初期値が設定されます。データ モデルを削除して例を実行すると、コントロールに初期値が設定されていないことがわかります。

例: データ モデルの使用
{let cc:CalendarControl = 
    {CalendarControl
        data-model = 
            {DateDataModel
                {DateTime "01/01/2006"},
                {DateTime "12/31/2006"},
                {DateTime "12/15/2006"}
            }
    }
}
{VBox
    cc,
    {if cc.has-value? then
        {spaced-hbox "Initial value is: ",
            cc.value.info.locale-date
        }
     else
        "No initial value."
    }
}
データ モデルを変更するには、CalendarControl.data-model プロパティを設定します。この例では、データ モデルを変更すると、コントロールの現在の値も変更されます。コントロールの表示にその変更が反映されます。

例: データモデルの変更
{let fall:DateDataModel = 
    {DateDataModel
        {DateTime "09/01/2006"},
        {DateTime "01/31/2007"},
        {DateTime "09/15/2006"}
    }
}
{let spring:DateDataModel = 
    {DateDataModel
        {DateTime "02/01/2007"},
        {DateTime "06/30/2007"},
        {DateTime "02/15/2007"}

                }
}
{let calendar-control:CalendarControl  = 
    {CalendarControl
        data-model = fall
    }
}
{Dialog
    {VBox
        {text Select a date:},
        {RadioFrame
            value = 0,
            {HBox
                {text Term:},
                {Fill},
                {RadioButton
                    label = "Fall 2006",
                    radio-value = 0
                },
                {RadioButton
                    label = "Spring 2007",
                    radio-value = 1
                }
            },
            {on ValueChanged at rf:RadioFrame do
                {if rf.value == 0 then
                    set calendar-control.data-model = fall
                    
                 else
                    set calendar-control.data-model = spring
                }
            }
        },
        calendar-control
    }
}
DateDataModel.set-range メソッドを使用して、データ モデルで指定された使用可能な日付の範囲を変更することもできます。

例: データ モデルの範囲の設定
{let calendar-control:CalendarControl  = 
    {CalendarControl
        data-model = 
            {DateDataModel
                {DateTime "01/01/2006"},
                {DateTime "12/31/2006"},
                {DateTime "12/01/2006"}
            }
    }
}
{VBox
    calendar-control,
    {CommandButton
        width = {add-stretch},
        label = "Change data model range",
        {on Action do
            {calendar-control.data-model.set-range
                {DateTime "12/01/2006"},
                {DateTime "12/31/2006"}
            }
        }
    }
}

休日の指定

休日は時や場所によって大きく異なるため、CalendarControl は既定では休日が指定されていません。CalendarControl で休日を示すためには、休日に DateTime 値の配列を指定する必要があります。CalendarControl は、calendar-control-holiday-backgroundcalendar-control-holiday-color を使用して休日をグラフィカルに表示します。CalendarControl でテキスト色と背景色以外の方法で休日を表現するには、CalendarControl.day-proc を使用する必要があります。
次の例では、5 日間の休日を初期セットとして指定し、カレンダー コントロールではこれらの休日を既定の色で表示します。コマンド ボタンをクリックして 5 日間の休日を追加し、休日の背景色とテキスト色を変更してください。コマンド ボタンのイベント ハンドラは、CalendarControl.holidays 配列の内容を変更するため、CalendarControl.note-holidays-changed メソッドも呼び出す必要があります。次の行を削除して、この例を実行してください。
{calendar-control.note-holidays-changed}
コマンド ボタンをクリックしても、追加の休日が表示されないことに注意してください。

例: 休日のリストの指定
{let holiday-list:{Array-of DateTime} = 
    {new {Array-of DateTime},
        {DateTime "01/02/2006"},
        {DateTime "01/03/2006"},
        {DateTime "01/04/2006"},
        {DateTime "01/05/2006"},
        {DateTime "01/06/2006"}
    }
}
{let calendar-control:CalendarControl  = 
    {CalendarControl
        data-model = 
            {DateDataModel
                {DateTime "01/01/2006"},
                {DateTime "12/31/2006"},
                {DateTime "01/31/2006"}
            },
        holidays = holiday-list
    }
}
{VBox
    calendar-control,
    {CommandButton
        label = "Add more holidays",
        width = {add-stretch},
        {on Action do
            {for d:DateTime in holiday-list do
                set d = d + 7d
                {holiday-list.append d}
            }
            {calendar-control.note-holidays-changed}
            set calendar-control.calendar-control-holiday-background = "beige"
            set calendar-control.calendar-control-holiday-color = "brown"
        }
    }
}

月と曜日の名前

CalendarControl には、曜日と月の名前を含む次の配列が用意されています。これらのリスト内の名前は、CalendarControl.locale によって決まります。
既定では、CalendarControl は週の見出しで short-weekday-names の名前を使用し、月の選択に使用するスピン コントロールで month-names の名前を使用します。

表示プロシージャの使用

CalendarControl には、コントロールの外観を変更できるプロシージャが用意されています。次のリストにこのようなプロシージャを示します。
次の例では、CalendarControl.weekday-names 配列により完全な曜日名を使用する weekday-proc プロシージャを指定しています。

例: weekday-proc の指定
{CalendarControl
    data-model = 
        {DateDataModel
            {DateTime "01/01/2006"},
            {DateTime "12/31/2006"},
            {DateTime "12/01/2006"}
        },
    weekday-proc =
        {proc {cc:CalendarControl, weekday:int}:(#Graphic)
            {return
                cc.weekday-names[weekday - 1]
            }
        }
}

日付フィールド

DateField は、日付入力専用のデータ入力フィールドです。次の例を使用して、単純な DateField のプロパティを確認してください。

例: 単純な DateField
{DateField}
次の例では、prompt 付きの DateField を表示し、日付フィールドの値の設定を解除するコマンド ボタンを表示しています。DateField.unset-value は、DateField.valuenull に、DateField.has-value?false に設定します。コントロールに値が指定されない場合は、DateField によってプロンプトが表示されます。
また、この例では、DateField.show-spin-buttons?true に設定しています。これらのボタンをクリックすることと、上方向キーや下方向キーを使用することは同じです。

例: プロンプトとスピン ボタン付きの DateField
{let df:DateField = 
    {DateField
        prompt = "",
        show-spin-buttons? = true
    }
}
{HBox
    df,
    {CommandButton
        label = "unset data field",
        {on Action do
            {df.unset-value}
        }
    }
}
日付フィールドに関連付けられているカレンダー コントロールのデータ モデルに新しい値を割り当てる場合は、DateField.note-calendar-control-property-changed メソッドを使用してコントロールに変更を通知する必要があります。

例: データ モデルの変更
{let cal-control:CalendarControl = 
    {CalendarControl
        takes-focus? = false,
        data-model = 
            {DateDataModel
                {DateTime "01/01/2006"},
                {DateTime "12/31/2006"},
                {DateTime "12/25/2006"}
            }
    }
}
{let cc-date-field:DateField = 
    {DateField
        prompt = "",
        calendar-control = cal-control
    }
}       
{spaced-vbox
    {HBox
        cc-date-field,
        {CommandButton
            label = "change data model",
            {on Action do
                set cal-control.data-model = 
                    {DateDataModel
                        {DateTime "01/01/2005"},
                        {DateTime "12/31/2006"},
                        {DateTime "12/25/2005"}
                    }
                {cc-date-field.note-calendar-control-property-changed}
            }
        }
    }
}

プログレスバー

ProgressBar は、アプリケーションで時間のかかるタスクを実行し、エンド ユーザーにそのタスクの進捗状況を知らせる必要がある場合に使用します。単純にアプリケーションがビジー状態であることを示す待機カーソルよりも詳しい情報を表示します。ProgressBar では、処理の進行速度と処理がどの程度完了しているかがわかります。
次の例は、ProgressBar の単純な使用法を示しています。[Start] を押すと、sleep を使用してアプレットによる処理をシミュレーションする計算ループに入り、ProgressBar を更新します。アプレットがビジー状態でプログレスバーが実行されている間に待機カーソルを表示するのもよい方法です。
この例の ProgressBar は簡単に実装でき、多くの場合、この例のコードのみで必要な処理が行われます。
アプレットが長時間ビジー状態の場合は、UI を無効にするのは良い方法です。 UI を無効にしない場合は、アプレットがビジー状態の間にキューに格納されたすべてのイベントは、長い計算が完了すると処理されます。プログレスバーの実行中に Show Popup ボタンを数回クリックしてください。計算ループが終了するまでポップアップ メッセージが表示されないことと、終了時にすべてのメッセージが一度に表示されることに注意してください。
コードのコメント化された行のコメントを解除して例を実行すると、アプレットがビジー状態の間 UI を無効にする効果が得られます。UI を再度有効にするコードを {after 0s do} ブロック内に入力することで、UI が無効な状態のままでアプレットが処理中でビジー状態の間に、イベントを蓄積することができます。
また、アプレットが逼迫した計算ループに入ると、画面更新イベントなどのイベントが処理されないことにも注意してください。アプレットで画面が確実に更新されるようにするには、ここで示すように ProgressBar.update-view を呼び出します。

例: スタート ボタンを持つ単純なプログレスバー
{let max:double = 700}
{let simple-bar:ProgressBar = 
    {ProgressBar
        width = 5cm,
        min-value = 0,
        max-value = max,
        value = 0,
        caption = "Press Start to Begin",
        {on e:ValueChanged at pb:ProgressBar do
            set pb.caption = pb.percent-complete & "%"
            {pb.update-view}
        }
    }
}
{HBox
    "Progress:",
    simple-bar,
    {CommandButton
        label = "Start",
        {on Action at cb:CommandButton do
||--                以下のコメントをはずすと UI が無効になります。
||--                let view = {cb.get-view}
||--                {if-non-null view then
||--                    set view.enabled? = false
||--                }
            {with-busy-cursor
                {for i = 1.0 to max do
                    {simple-bar.set-value-with-events i}
                    || 待機時間を作ります。
                    {sleep .01s}
                }
            }
||--                以下のコメントをはずすと再び UI が有効になります。
||--                {after 0s do
||--                    {if-non-null view then
||--                        set view.enabled? = true
||--                    }
||--                }
        }
    },
    {CommandButton
        label = "Show Popup",
        {on Action do
            {popup-message "Message"}
        }
    }
}

キャンセル ボタンの追加

次の例は Cancel ボタンを追加します。計算ループ内で dispatch-events を呼び出して、アプレットが Cancel ボタンに反応して計算ループを終了できるようにしています。
実際のアプリケーションで Cancel ボタンを持つ ProgressBar を使用するには、プログレスバーと UI を更新するコードを、時間を消費する処理を行うルーチンに追加します。ユーザー インターフェイスは dispatch-events が呼び出される場合にのみアクティブになります。この例では、このプロシージャはループを回るごとに 1 回呼び出されます。実際のアプリケーションでは、1 回の繰り返しが遅くなると、ユーザーはアプレットが反応していないことに気付く場合があります。
この例では、時間を消費する処理が実行されている間、不必要な再入を避けるために残りのユーザー インターフェイスを無効にします。dispatch-events を呼び出すたびに、すべての着信イベントが処理されます。UI が有効の場合、アプレットは現在の操作が完了する前に他の操作を行おうとする場合があります。Cancel ボタンに明示的に enabled? = true を設定して、収容している View が無効な場合にこのボタンが無効にならないようにする必要があります。
[Start working] というラベルのボタンをクリックして、例が含まれているダイアログを表示してください。この例は、別個のダイアログによって実装されるため、UI を無効にするコードによって、このドキュメントを表示するアプレット全体の UI が無効になることはありません。

例: スタート ボタンとキャンセル ボタンを持つ単純なプログレスバー
{let max:double = 1000}
{let computing?:bool = false}
{let pb:ProgressBar = 
    {ProgressBar
        width = 5cm, 
        enabled? = true,
        min-value = 0,
        max-value = max,
        value = 0,
        caption = "Press Start to Begin",
        {on e:ValueChanged at pb:ProgressBar do
            set pb.caption = pb.percent-complete & "%"
        }
    }
}
{let d:Dialog = 
    {Dialog
        {HBox
            "Progress:",
            pb,
            {CommandButton
                label = "Start",
                {on Action at cb:CommandButton do
                    set computing? = true
                    {if-non-null view = {cb.get-view} then
                        set view.enabled? = false
                    }
                    {for i = 1.0 to max do
                        {pb.set-value-with-events i}
                        || これは待機時間を作ります。
                        {sleep .01s}
                        {dispatch-events false}
                        {if not computing? then
                            set pb.caption = "Canceled. Press Start to begin."
                            {break}
                        }
                    }
                    {if-non-null view = {cb.get-view} then
                        set view.enabled? = true
                    }
                }
            },
            {CommandButton
                label = "Cancel",
                enabled? = true, || [Cancel] ボタンは有効でなければいけません。
                {on Action do
                    set computing? = false
                    {pb.set-value-with-events 0.0}
                }
            },
            {CommandButton
                label = "Show Popup",
                {on Action do
                    {popup-message "Message"}
                }
            }
        }
    }
}
{CommandButton 
    label = "Start working.",
    {on Action do
        {d.show}
    }
}

一時停止ボタンの追加

次の例は以前の例とは異なるアプレット構造を使います。 呼び出す毎に少しずつ処理を進めることのできるルーチンにより、時間を消費する処理は実行されます。 この構造により[pause]ボタンを追加することができるため、エンドユーザーは処理を中断し、 他の操作を行い、後にこの処理を再開することができます。
この例では、compute と呼ばれるプロシージャが、 長い全処理の一部を実行するルーチンをシミュレートします。 ループは compute を繰り返し呼び出します。 この例では、UI を計算コードから分離するという適切な処理を行っていますが、 繰り返し呼び出して処理を実行できるプロシージャまたはメソッドが必要となります。
また、次の例では、LinearGradientFillPattern を使用して progress-color をカスタマイズしています。

例: スタート ボタンと一時停止ボタンを持つプログレスバー
{let max:double = 50}
{let computing?:bool = false}
{let so-far:double = 0}
{define-proc {compute}:void
    {for j = 1 to 1000000 do
        {let x:double = {sqrt 3}}
    }
}
{let prog-bar:ProgressBar = 
    {ProgressBar
        width = 5cm,
        enabled? = true,
        min-value = 0,
        max-value = max,
        value = 0,
        caption = "Press Start to Begin",
        progress-color =
            {LinearGradientFillPattern
                {Fraction2d 0, 1}, 
                {Fraction2d 0, 0}, 
                {Spectrum.from-envelope
                {FillPattern.get-silver}, 0,
                {FillPattern.get-white}, 0.5,
                {FillPattern.get-silver}, 1.0
                } 
            },
        {on e:ValueChanged at pb:ProgressBar do
            set pb.caption = pb.percent-complete & "%"
        }
    }
}
{let d:Dialog = 
    {Dialog
        {HBox
            "Progress:",
            prog-bar,
            {CommandButton
                label = "Start",
                {on Action at cb:CommandButton do
                    set computing? = true
                    {if-non-null view = {cb.get-view} then
                        set view.enabled? = false
                    }
                    {for i = so-far to max do
                        {prog-bar.set-value-with-events i}
                        {compute}
                        set so-far = i
                        {dispatch-events false}
                        {if not computing? then
                            set prog-bar.caption = 
                                "Paused. Press Start to resume."
                            {break}
                        }
                    }
                    {if-non-null view = {cb.get-view} then
                        set view.enabled? = true
                    }
                }
            },
            {CommandButton
                label = "Pause",
                enabled? = true,
                {on Action do
                    set computing? = false
                }
            }
        }
    }
}
{CommandButton 
    label = "Start working.",
    {on Action do
        {d.show}
    }
}

不確定プログレスバー

次の例は、不確定プログレスバーを示しています。
ProgressBar.indeterminate?ture の場合、ProgressBar の値は無意味なものと成り、ProgressBar はアニメーションを連続的に表示します。ただし、UI オブジェクトが混乱しないように、有効な値を指定する必要があります。アニメーションをオンにしたりオフにしたりするには、不確定モードを切り替えます。
コードの構造は前の例と同じで、compute ルーチンは繰り返し呼び出されます。

例: スタート ボタンと終了ボタンを持つ不確定プログレスバー
{define-proc {compute}:void
    {for j = 1 to 1000000 do
        {let x:double = {sqrt 3}}
    }
}
{let computing?:bool = false}
{let pb:ProgressBar = 
    {ProgressBar
        width = 5cm,
        caption = "Press Start to Begin",
        progress-color = "#5BDD5D"
    }
}

{let rr:RolledRandom = {RolledRandom 50, 150}}
{let d:Dialog = 
    {Dialog
        {HBox
            "Progress:",
            pb,
            {CommandButton
                label = "Start",
                {on Action at cb:CommandButton do
                    set computing? = true
                    set pb.indeterminate? = true
                    {if-non-null view = {cb.get-view} then
                        set view.enabled? = false
                    }
                    {for i = 1.0 to {rr.next-roll} do
                        {if computing? then
                            set pb.caption = ""
                            {compute}
                            {dispatch-events false}
                         else
                            set pb.caption = 
                                "Stopped. Press Start to begin."
                            {break}
                        }
                    }
                    set pb.indeterminate? = false
                    {if-non-null view = {cb.get-view} then
                        set view.enabled? = true
                    }
                }
            },
            {CommandButton
                label = "Stop",
                enabled? = true,
                {on Action do
                    set computing? = false
                }
            }
        }
    }
}
{CommandButton 
    label = "Start working.",
    {on Action do
        {d.show}
    }
}

スライダ

Slider では、定義済みの値コレクションから値または値の範囲を選択できます。値をソート順に表示するという点で SpinControl に似ています。スライダのつまみを移動して値を選択すると、コントロールの値が変わります。
Slider は、Domain オブジェクトを使用して一連の値を定義します。StandardIntDomain などの多くの標準ドメインでは、既定値を指定し、最小値と最大値が存在しない場合は null を使用します。スライダ コントロールで使用可能な値を無限にすることはできません。そのため、SliderDomain を使用する場合は、最小値と最大値を指定する必要があります。使用可能な値は、最大値と最小値の間にある、指定したデータ型の値です。
次の例は、ドメインとプロパティの既定値を使用した Slider と、変更値と終了値の表示を有効にするイベント ハンドラを示しています。Slider で使用する既定のドメインは、0 から 100 までの整数で、既定値は 0 です。Slider の動作について、次の点に注意してください。

例: 既定の Slider
{let changed-value:TextField = {TextField}}
{let finished-value:TextField = {TextField}}
{HBox
    valign = "top",
    {Slider
        {on ValueChanged at s:Slider do
            set changed-value.value = {String s.value}
        },
        {on ValueFinished at s:Slider do
            set finished-value.value = {String s.value}
        }
    },
    {Table
        {row-prototype "Changed value: ", {value changed-value}},
        {row-prototype "Finished value: ", {value finished-value}}
    }
}

ドメインの指定

Slider を使用して、StandardIntDomainStandardFloatDomainStandardDoubleDomain などの数値ドメインで連続する値を表示できます。日付と時刻を含むドメインなど、他のドメインを使用するには、使用可能な値の配列を指定するだけです。列挙型から作成されたドメインを指定することもできます。
次の例では、スライダで float ドメインを使用しています。

例: Float ドメイン
{value
    let changed-value:TextField = {TextField}
    let finished-value:TextField = {TextField}
    let slide:Slider =
        {Slider
            width = 10cm,
            domain =
                {StandardFloatDomain
                    default-value = 0,
                    min-allowable = 0,
                    max-allowable = 50
                },
            {on ValueChanged at s:Slider do
                set changed-value.value = {String s.value}
            },
            {on ValueFinished at s:Slider do
                set finished-value.value = {String s.value}
            }
        }
    {VBox
        slide,
        {HBox 
            "changed value:", 
            changed-value, 
            "finished value:", 
            finished-value
        }
    }
}
次の例では、StandardTimeDomain を使用して allowable-values の配列を指定しています。

例: Time ドメイン
{let changed-value:TextField = {TextField}}
{let finished-value:TextField = {TextField}}

{let times:{Array-of Time} = 
    {new {Array-of Time}}
}
{for i:Time = 0s to 60s step 1s do
    {times.append i}
}
{let slide:Slider = 
    {Slider
        width = 12cm,
        domain = 
            {StandardTimeDomain
                allowable-values = times
            },
        major-tick-spacing = 15,
        minor-tick-spacing = 5,
        {on ValueChanged at s:Slider do
            set changed-value.value = {String s.value}
        },
        {on ValueFinished at s:Slider do
            set finished-value.value = {String s.value}
        }
    }
}
{VBox
    slide,
    {HBox 
        "changed value:", 
        changed-value, 
        "finished value:", 
        finished-value
    }
}
Slider は、SpinControl と同じように列挙型を処理します。値は、列挙体で指定された順にコントロールに表示されます。major-tick-spacingminor-tick-spacing の既定値は、列挙値を使用するスライダでは正常に機能しない場合があります。次の例では、これらの両方のプロパティを 1 に設定して、7 つの曜日すべてに主目盛りとラベルを表示しています。主目盛りの間隔を 2 に変更して、曜日のラベルが 1 つおきに表示されることを確認してください。

例: 列挙型のドメイン
{let changed-value:TextField = {TextField}}
{let finished-value:TextField = {TextField}}

{define-enum public Days
    Sunday = "Sunday", 
    Monday= "Monday", 
    Tuesday  = "Tuesday", 
    Wednesday  = "Wednesday", 
    Thursday  = "Thursday",
    Friday = "Friday",
    Saturday = "Saturday"
}
{let slider:Slider = 
    {Slider
        width = 12cm,
        domain =
            {Domain.from-type
                Days
            },
        major-tick-spacing = 1,
        minor-tick-spacing = 1,
        {on ValueChanged at s:Slider do
            set changed-value.value = {String s.value.value}
        },
        {on ValueFinished at s:Slider do
            set finished-value.value = {String s.value.value}
        }
        
    }
}
{VBox
    slider,
    {HBox 
        "Changed value:", 
        {Fill}, 
        {value changed-value}, 
        "Finished value:", 
        {Fill}, 
        {value finished-value}
    }
}

ラベルと目盛りの操作

Slider には、コントロールのラベルと目盛りマークを変更するためのプロパティが用意されています。次の例では、Slider.snap-to-ticks?true に設定して、ユーザーがスライダのつまみを目盛りマークの間に移動できないようにしています。このプロパティ設定は、ユーザーが使用できる値を制限する点で SpinControl.step の設定と同じ効果を持ちます。この例では、使用可能な値は 050 の間のすべての整数です。ただし、スライダのつまみを 5 単位でしか移動できないため、値も 5 単位でしか選択できません。

例: snap-to-ticks? の使用
{value
    let changed-value:TextField = {TextField}
    let finished-value:TextField = {TextField}
    let slider:Slider =
        {Slider
            width = 10cm,
            snap-to-ticks? = true,
            domain =
                {StandardIntDomain
                    default-value = 0,
                    min-allowable = 0,
                    max-allowable = 50
                },
            {on ValueChanged at s:Slider do
                set changed-value.value = {String s.value}
            },
            {on ValueFinished at s:Slider do
                set finished-value.value = {String s.value}
            }
        }
    {VBox
        slider,
        {HBox 
            "changed value:", 
            changed-value, 
            "finished value:", 
            finished-value
        }
    }
}

Slider.show-ticks? プロパティと Slider.show-labels? プロパティは、目盛りとラベルをスライダに表示するかどうかを制御します。次の例にいくつかのバリエーションを示します。

例: 目盛りとラベルのバリエーション
{spaced-hbox
    valign = "center",
    {Slider show-ticks? = false},
    {Slider show-labels? = false},
    {Slider
        show-ticks? = false,
        show-labels? = false
    }
}
Slider.labels プロパティでは、目盛りラベルとして機能するグラフィックを指定できます。値と対応するグラフィックのハッシュ テーブルを指定する必要があります。
次の例は、グラフィカルなラベルを有効に活用できる状況を示しています。ラベルは、スライダのつまみを右側に移動するにつれて高くなる色の濃度を表しています。

例: ラベルとしてのグラフィックの使用
{let r:double = .5, g:double = .5, b:double = .5}
{let dom:Domain = 
    {StandardDoubleDomain
        default-value = 0.5,
        min-allowable = 0.0,
        max-allowable = 1.0
    }
}
{let f:Fill = 
    {Fill
        width = 5cm,
        height = 5cm,
        background = {Color.from-rgb r, g, b}
    }
}
{define-proc public 
    {make-labels color:String, how-many:int}:{HashTable-of any, Graphic}
    {let r:double = 1, g:double = 1, b:double = 1}
    let ht:{HashTable-of any, Graphic} = {new {HashTable-of any, Graphic}}
    {for i:double = 0 to how-many step