Enter name while application waits
The example below defines a prompter for a name. As this prompter may often be called from many different locations in the program, the prompter itself is not destroyed after the name has been entered, but just removed from the screen.
% ask_name(+Prompt, +Label, -Name)
% Put a prompter on the screen and wait till the user has
% entered a name. Pressing cancel makes this predicate fail.
% Prompt is a long string, giving explanation; Label is a short
% label displayed for the text entry field.
:- pce_global(@name_prompter, make_name_prompter).
make_name_prompter(P) :-
new(P, dialog),
send(P, kind, transient),
send(P, append, label(prompt)),
send(P, append,
new(TI, text_item(name, '',
message(P?ok_member, execute)))),
send(P, append, button(ok, message(P, return, TI?selection))),
send(P, append, button(cancel, message(P, return, @nil))).
ask_name(Prompt, Label, Name) :-
send(@name_prompter?prompt_member, selection, Prompt),
send(@name_prompter?name_member, label, Label),
send(@name_prompter?name_member, clear),
get(@name_prompter, confirm_centered, RawName),
send(@name_prompter, show, @off),
RawName \== @nil,
Name = RawName.
ask_name :-
ask_name('Street', name, Street),
writeln(Street).
% Start the demo
:- ask_name.
Counting long strings in a chain
The following example assumes a chain of string objects. It will count all strings that are longer than 80 characters.
number_of_long_strings(Chain, N) :-
new(Number, number(0)),
send(Chain, for_all,
if(@arg1?size > 80,
message(Number, plus, 1))),
get(Number, value, N),
send(Number, done).
Basic examples on how to create objects
The examples below show how simple objects can be created.
% Objects with named references
?- new(@my_window, picture('My Window')). % A graphics window
?- new(@origin, point(0,0)). % A point at (0,0)
% Objects with anonymous (integer) references
?- new(B, box(30,40)). % A box of 30 x 40
?- new(L, line(0,0,100,200, second)). % A line with arrow
Finding objects in a collection
The following example shows how one can find an object in a chain that has a certain name.
This example uses the equality code class _==_ to perform the test.
The method chain<-find
executes the code argument successively on the elements of the chain,
forwarding each element via @arg1.
As soon as the code returns success, the value is returned.
find_named_in_chain(Chain, Name, Obj) :-
get(Chain, find, @arg1?name == Name, Obj).
Using spatials
The example below opens a picture object with two box objects that are related though a spatial object. Both boxes may be resized and moved. The spatial object ensures B1 is twice as large as B2, centered in X-direction and 10 pixels above B2.
spatial_demo :-
new(G, handler_group(resize_gesture(left),
move_gesture(left))),
new(P, picture('Spatial Demo')),
send(P, display, new(B1, box(100,100))),
send(P, display, new(B2, box(50,50))),
send_list([B1, B2], recogniser, G),
new(_, constraint(B1, B2,
spatial(xref = x+w/2, yref = y+h,
xref = x+w/2, yref = y-10,
w = w2*2, h = h2*2))),
send(P, open).
% Start the demo
:- spatial_demo.
Generate a tree from facts
The example below will create a graphical representation from facts
of the form is_a(Parent, Son), which are supposed to span a
single-inheritance tree.
Modern (2002 :-) Explorer-like hierarchies can be created using this
class too. A high-level library is in library(pce_toc)
(Table Of Contents). This library is also used by most hierarchies in
the XPCE and Prolog development tools.
% display_hierarchy(+Root)
% Show a window with the hierarchy described by is_a(Parent, Son)
% starting from the given root.
display_hierarchy(Root) :-
new(P, picture(string('Hierarchy from %s', Root))),
send(P, open),
send(P, display, tree(new(Node, node(text(Root))))),
expand_hierarchy(Root, Node).
expand_hierarchy(Root, Node) :-
forall(is_a(Root, Sub),
(send(Node, son, new(Son, node(text(Sub)))),
expand_hierarchy(Sub, Son))).
% Example database
is_a(species, plant).
is_a(species, animal).
is_a(animal, mamal).
is_a(mamal, monkey).
is_a(mamal, horse).
% Start the demo
:- display_hierarchy(species).
Linking two boxes with a connection
This example demonstrates how two boxes can be linked using connections. Note that the link is reusable, and is therefore declared as a global object.
:- pce_global(@in_out_link, make_in_out_link).
make_in_out_link(L) :-
new(L, link(in, out, line(0,0,0,0,second))).
linked_box_demo :-
new(P, picture('Linked Box demo')),
send(P, open),
send(P, display, new(B1, box(50,50)), point(20,20)),
send(P, display, new(B2, box(25,25)), point(100,100)),
send(B1, handle, handle(w, h/2, in)),
send(B2, handle, handle(w/2, 0, out)),
send(B1, connect, B2, @in_out_link),
% Add resize/move gestures so you can
% play
send_list([B1,B2], recogniser,
handler_group(resize_gesture(left),
move_gesture(left))).
% Start the demo
:- linked_box_demo.
Using a menu_bar in a frame
The example below illustrates how a menu_bar
object is used to present commands for a toolframe to the user. The demo
below uses a plain menu_bar
object. The library(toolbar) defines more high-level
infrastructure to deal with menu- and button bars.
:- set_prolog_flag(xpce_threaded, false).
:- use_module(library(pce)).
:- pce_autoload(finder, library(find_file)).
:- pce_global(@finder, new(finder)).
menu_bar_demo :-
new(F, frame('Menu Bar demo')),
send(F, append, new(D, dialog)),
send(new(V, view), below, D),
send(D, append, new(MB, menu_bar)),
send(MB, append, new(File, popup(file))),
send(MB, append, new(Help, popup(help))),
send_list(File, append,
[ menu_item(load,
message(V, load, @finder?file)),
menu_item(save,
message(V, save_buffer),
condition := V?modified == @on,
end_group := @on),
menu_item(quit,
message(F, destroy))
]),
send_list(Help, append,
[ menu_item(about,
message(@display, inform,
'By Jan Wielemaker'))
]),
send(F, open).
% Start the demo
:- initialization(menu_bar_demo).
Marked menu to select a fill
The code of this example creates a dialog window that allows the user
to select a fill. The selected value of the fill pattern is written in
the
Prolog.window,
create_fill_pattern_dialog :-
new(Dialog, dialog('Fill Patterns')),
send(Dialog, append,
new(M, menu(fill_pattern, marked,
message(@prolog, write_ln, @arg1)))),
send_list(M, append,
[ menu_item(white, @default, @white_image)
, menu_item(grey12, @default, @grey12_image)
, menu_item(grey25, @default, @grey25_image)
, menu_item(grey50, @default, @grey50_image)
, menu_item(grey75, @default, @grey75_image)
, menu_item(black, @default, @black_image)
]),
send(Dialog, open).
% Start the demo
:- create_fill_pattern_dialog.
Using multiple fonts in an editor
The following example show multiple fonts can be used in an editor. Suppose want to have an editor that displays descriptions in the proportional helvetica font family and code fragments in a screen font.
The editor will be given an additional key-binding for \C-c\C-c
to convert the selection into a code fragment.
% Create a view that that allows mixing two fonts: one
% for normal text and one for program-text.
make_code_view :-
new(V, view('Code Editor Demo')),
send(V, font, normal),
send(V, style, code, style(font := fixed)),
send(V, key_binding, '\\C-c', prefix),
send(V, key_binding, '\\C-c\\C-c',
message(@prolog, make_fragment, V, code)),
send(V, open).
% make_fragment(+View, +Kind)
% Create a fragment for the region (caret, mark) of
% kind `Kind'.
% Note that mark and caret may be in any order.
make_fragment(V, Kind) :-
get(V, selection_start, Start),
get(V, selection_end, End),
Length is End - Start,
new(_, fragment(V?text_buffer, Start, Length, Kind)).
% Start the demo
:- make_code_view.
Primitive usage of obtainers
These examples presents some elementary ways to use obtainers. The same mechanism applies to other function objects.
% Prolog has no functions, PCE does:
make_same_width(Gr1, Gr2) :-
send(Gr1, width, Gr2?width).
% Pick up arguments from a dialog window:
create_person_dialog :-
new(D, dialog('Enter new person')),
send(D, append, new(label)), % for reports
send(D, append, new(Name, text_item(name))),
send(D, append, new(Age, text_item(age))),
send(D, append, new(Sex, menu(sex, marked))),
send(Sex, append, female),
send(Sex, append, male),
send(Age, type, int),
send(D, append,
button(create, message(@prolog, create_person,
Name?selection,
Age?selection,
Sex?selection))),
send(D, default_button, create),
send(D, open).
create_person(Name, Age, Sex) :-
format('Creating ~w person ~w of ~d years old~n',
[Sex, Name, Age]).
% Start the demo
:- create_person_dialog.
Select Graphicals
This example defines a gesture that allows the user to set the selection of a graphical device to a specified graphical by left-clicking the graphical. With a SHIFT-left-click, the graphical is either removed from or added to the selection.
:- pce_global(@selection_gesture,
new(handler_group(
click_gesture(left, '', single,
message(@event?receiver?device,
selection, @event?receiver)),
click_gesture(left, s, single,
message(@event?receiver,
toggle_selected))))).
select_demo :-
new(P, picture('Select Demo')),
send(P, display,
new(Box, box(100, 100))),
send(P, display,
new(Bitmap, bitmap('pce.bm')), point(150,50)),
send_list([Box, Bitmap], recogniser,
@selection_gesture),
send(P, open).
% Start the demo
:- select_demo.
Creating a compound graphical
An effective way to find the references of elements of a
window/device/dialog is using the graphical<->name
attribute and the
device<-member
or device<-catch_all
methods, which provide a mechanism to decent the consists_of hierarchy
of graphicals by name.
The example below defines a box with text and a method to set the string-value of the text. It uses user-defined classes.
:- pce_begin_class(text_box(string, width, height), device).
initialise(T, S:string, Width:[int], Height:[int]) :->
"Initialise from string, width and heigth"::
default(Width, 100, W),
default(Height, 50, H),
send(T, send_super, initialise),
send(T, display, box(W,H)),
send(T, display, text(S, center)),
send(T, recenter).
recenter(T) :->
"Put text in center of box"::
get(T, member, text, Txt),
get(T, member, box, B),
send(Txt, center, B?center).
string(T, S:string) :->
"Set the string of the text"::
get(T, member, text, Txt),
send(Txt, string, S).
string(T, S) :<-
"Get the string of the text"::
get(T, member, text, Txt),
get(Txt, string, S).
:- pce_end_class.
% Start the demo
:- send(new(P, picture('Text box demo')), open),
send(P, display,
text_box('Box with centered text', 200, 50),
point(50,50)).
Tests inside a dialog window
This example demonstrates the use of conditional messages to relate dialog-items to each other.
Using this technique is to be preferred over tests in the application code to relate and query values of dialog windows.
The example shows a text entry field (text_item) and a create
button. Pressing the create button should create a keyword in the
application.
First the code is shown doing almost everything in Prolog, next an alternative using some PCE primitives is given.
% bad_make_keywords_dialog(D)
%
% Creates a dialog to generate keywords, using Prolog
% to link the dialog together.
bad_make_keywords_dialog :-
new(D, dialog('Create keyword')),
send(D, append, new(label)),
send(D, append,
text_item(keyword, '',
message(@prolog, create, D))),
send(D, append,
button(create, message(@prolog, create, D))),
send(D, append,
button(cancel, message(@prolog, cancel, D))),
send(D, open).
create(D) :-
get(?(D, member, keyword), selection, Kwd),
( Kwd == ''
-> send(D, report, warning,
'Please first enter a keyword')
; create_keyword(Kwd)
).
cancel(D) :-
send(D, destroy).
% good_make_keywords_dialog(?Dialog)
%
% Creates the same dialog, but using PCE to glue the
% things together. This way the distinction between
% UI and application become more clear.
good_make_keywords_dialog :-
new(D, dialog('Create keyword')),
send(D, append, new(label)),
send(D, append, new(T, text_item(keyword, ''))),
send(D, append,
button(create,
if(T?selection == '',
message(D, report, warning,
'Please enter a keyword'),
message(@prolog, create_keyword,
T?selection)))),
send(D, append,
button(cancel, message(D, destroy))),
send(D, default_button, create),
send(D, open).
create_keyword(Name) :-
format('Create keyword ~w~n', Name).
% Only start the good one
:- good_make_keywords_dialog.
Defining a window layout
This is an example on how to define the relative position/sizes of windows in a frame.
The task is to create a frame that has a browser and a picture on top and a dialog window at the bottom.
% frame_dialog/0
% Create a frame with a browser and picture at the top and a dialog
% at the bottom.
frame_dialog :-
new(P, picture('Frame layout demo')),
send(new(B, browser), right, P),
send(new(D, dialog), below, B),
send(D, append, button(quit, message(D, destroy))),
send(P, open).
% Start the demo
:- frame_dialog.