## message.pkg
#
# Text message widget.
# Compiled by:
#
src/lib/x-kit/widget/xkit-widget.sublibstipulate
include package threadkit; # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg #
package d3 = three_d; # three_d is from
src/lib/x-kit/widget/old/lib/three-d.pkg package wg = widget; # widget is from
src/lib/x-kit/widget/old/basic/widget.pkg package wa = widget_attribute_old; # widget_attribute_old is from
src/lib/x-kit/widget/old/lib/widget-attribute-old.pkg package wt = widget_types; # widget_types is from
src/lib/x-kit/widget/old/basic/widget-types.pkg #
package xc = xclient; # xclient is from
src/lib/x-kit/xclient/xclient.pkg #
package g2d= geometry2d; # geometry2d is from
src/lib/std/2d/geometry2d.pkgherein
package message
: (weak) Message # Message is from
src/lib/x-kit/widget/old/leaf/message.api {
Plea_Mail
= SET_TEXT String
| GET_TEXT Oneshot_Maildrop( String )
| GET_SIZE_CONSTRAINT Oneshot_Maildrop( wg::Widget_Size_Preference )
#
| DO_REALIZE { kidplug: xc::Kidplug,
window: xc::Window,
window_size: g2d::Size
}
;
Message
=
MESSAGE
{ widget: wg::Widget,
plea_slot: Mailslot( Plea_Mail )
};
Textinfo = { text: String,
text_wide: Int,
text_high: Int
};
fun get_line (font, text, starti, maxx)
=
loop (starti, 0, starti, 0)
where
char_info = xc::char_info_of font;
endi = size text;
fun loop (i, curx, end_word, end_word_x)
=
if (endi == i)
#
(i, curx);
else
c = string::get_byte_as_char (text, i);
#
if (c == '\n')
#
(i, curx);
else
(char_info (char::to_int c))
->
xc::CHAR_INFO { char_width, ... };
nextx = curx + char_width;
if (nextx > maxx)
#
if (end_word > starti)
#
(end_word, end_word_x);
else
if (i > starti) (i, curx);
else (i+1, nextx);
fi;
fi;
else
my (end_word, end_word_x)
=
if (char::is_space c)
#
(i+1, nextx);
else
(end_word, end_word_x);
fi;
loop (i+1, nextx, end_word, end_word_x);
fi;
fi;
fi;
end;
fun make_text_info (root, aspect, text, width, fontinfo, bw, padx, pady)
=
{ text, text_wide, text_high }
where
fontinfo -> (font, font_ascent, font_descent);
font_high = font_ascent + font_descent;
xdelta = 2*(bw + padx);
ydelta = 2*(bw + pady);
aspect_delta = int::max (5, aspect / 10);
lower_bound = aspect - aspect_delta;
upper_bound = aspect + aspect_delta;
(xc::size_of_screen (wg::screen_of root))
->
{ wide=>screen_width, ... };
wi = if (width > 0)
(width, 0);
else
width = screen_width / 2;
(width, width / 2);
fi;
endi = size text;
fun get_size (i, maxw, txtht, width)
=
if (i == endi)
#
(maxw, txtht);
elif (string::get_byte_as_char (text, i) == '\n' )
#
get_size (i+1, maxw, txtht+font_high, width);
else
(get_line (font, text, i, width))
->
(nexti, linex);
maxw = int::max (linex, maxw);
fun skip_ws i
=
{ c = string::get_byte_as_char (text, i);
#
if (c == '\n') i+1;
elif (char::is_space c) skip_ws (i+1);
else i;
fi;
};
get_size
( (skip_ws nexti) except _ = nexti,
maxw,
txtht + font_high,
width
);
fi;
fun do_layout (width, inc)
=
{ (get_size (0, 0, 0, width))
->
answer as (text_wide, text_high);
if (inc <= 2)
#
answer;
else
aspect = (100*(text_wide + xdelta)) / (text_high + ydelta);
if (aspect < lower_bound )
#
do_layout (width+inc, inc / 2);
elif (aspect > upper_bound )
#
do_layout (width-inc, inc / 2);
else
answer;
fi;
fi;
};
(do_layout wi)
->
(text_wide, text_high);
end;
Fontinfo = (xc::Font, Int, Int);
fun make_font_info font
=
{ (xc::font_high font)
->
{ ascent=>font_ascent, descent=>font_descent };
(font, font_ascent, font_descent);
};
attributes
=
[ (wa::aspect, wa::INT, wa::INT_VAL 150),
(wa::background, wa::COLOR, wa::STRING_VAL "white"),
(wa::border_thickness, wa::INT, wa::INT_VAL 2),
(wa::font, wa::FONT, wa::STRING_VAL "8x13"),
(wa::foreground, wa::COLOR, wa::STRING_VAL "black"),
(wa::gravity, wa::GRAVITY, wa::GRAVITY_VAL wt::CENTER),
(wa::halign, wa::HALIGN, wa::HALIGN_VAL wt::HLEFT),
(wa::padx, wa::INT, wa::NO_VAL),
(wa::pady, wa::INT, wa::NO_VAL),
(wa::relief, wa::RELIEF, wa::RELIEF_VAL wg::FLAT),
(wa::text, wa::STRING, wa::STRING_VAL " "),
(wa::width, wa::INT, wa::INT_VAL 0)
];
Result = { aspect: Int,
bg: xc::Rgb,
fg: xc::Rgb,
border_thickness: Int,
fontinfo: Fontinfo,
gravity: wt::Gravity,
justify: wt::Horizontal_Alignment,
padx: Int,
pady: Int,
relief: wg::Relief,
shades: wg::Shades,
textinfo: Ref( Textinfo ),
width: Int
};
fun get_resources (root, attributes) : Result
=
{ aspect = wa::get_int (attributes wa::aspect );
bg = wa::get_color (attributes wa::background);
font = wa::get_font (attributes wa::font );
my fontinfo as (_, font_ascent, _)
=
make_font_info font;
padx = case (wa::get_int_opt (attributes wa::padx))
#
THE i => i;
NULL => font_ascent / 2;
esac;
pady = case (wa::get_int_opt (attributes wa::pady))
#
THE i => i;
NULL => font_ascent / 4;
esac;
text = wa::get_string (attributes wa::text);
width = wa::get_int (attributes wa::width);
border_thickness = wa::get_int (attributes wa::border_thickness);
{ aspect,
bg,
border_thickness,
fontinfo,
fg => wa::get_color (attributes wa::foreground),
gravity => wa::get_gravity (attributes wa::gravity),
justify => wa::get_halign (attributes wa::halign),
padx,
pady,
relief => wa::get_relief (attributes wa::relief),
shades => wg::shades root bg,
textinfo => REF (make_text_info (root, aspect, text, width, fontinfo,
border_thickness, padx, pady)),
width
};
};
fun size_preference_thunk_of ( { textinfo, padx, pady, border_thickness, ... } : Result)
=
{ (*textinfo) -> { text_high, text_wide, ... };
x = text_wide + 2*(border_thickness + padx);
y = text_high + 2*(border_thickness + pady);
{ col_preference => wg::loose_preference x,
row_preference => wg::loose_preference y
};
};
fun drawf
( d,
size as { wide, high },
result: Result
)
=
{ result -> { border_thickness=>bw, pady, padx, ... };
#
result.fontinfo -> (font, font_ascent, font_descent);
(*result.textinfo)
->
{ text, text_high, text_wide };
y = case result.gravity
#
(wt::NORTH_WEST
| wt::NORTH | wt::NORTH_EAST) => bw + pady;
(wt::WEST
| wt::CENTER | wt::EAST) => (high - text_high) / 2;
_ => high - bw - pady - text_high;
esac
+
font_ascent;
r = g2d::box::make (g2d::point::zero, size);
font_high = font_ascent + font_descent;
txt_pen = xc::make_pen [ xc::p::FOREGROUND (xc::rgb8_from_rgb result.fg) ];
fun do_text (y, i)
=
if (string::get_byte_as_char (text, i) == '\n')
#
do_text (y+font_high, i+1);
else
(get_line (font, text, i, text_wide))
->
(nexti, linewid);
x = case result.gravity
#
(wt::NORTH_WEST
| wt::WEST | wt::SOUTH_WEST) => bw + result.padx;
(wt::NORTH
| wt::CENTER | wt::SOUTH) => (wide - text_wide) / 2;
_ => wide - bw - padx - text_wide;
esac;
x = case result.justify
#
wt::HCENTER => x + (text_wide - linewid) / 2;
wt::HRIGHT => x + (text_wide - linewid);
wt::HLEFT => x;
esac;
fun skip_ws i
=
{ c = string::get_byte_as_char (text, i);
#
if (c == '\n') i+1;
elif (char::is_space c) skip_ws (i+1);
else i;
fi;
};
xc::draw_transparent_string d txt_pen font
({ col=>x, row=>y }, substring (text, i, nexti-i));
do_text (y+font_high, skip_ws nexti);
fi;
\\ () = { do_text (y, 0) except _ = ();
#
case result.relief
#
wg::FLAT => ();
relief => d3::draw_box d { width=>bw, relief, box=>r } result.shades;
esac;
};
};
fun get_text ( { textinfo, ... } : Result)
=
(*textinfo).text;
fun realize (root, { kidplug, window, window_size }, result, plea_slot)
=
{ d = xc::drawable_of_window window;
#
plea' = take_from_mailslot' plea_slot;
(xc::ignore_mouse_and_keyboard kidplug)
->
xc::KIDPLUG { from_other', to_mom, ... };
fun do_mom (xc::ETC_REDRAW _, state as (draw, _))
=>
{ draw ();
state;
};
do_mom (xc::ETC_RESIZE ({ wide, high, ... }: g2d::Box), _)
=>
{ size = { wide, high };
#
xc::clear_drawable d;
(drawf (d, size, result), size);
};
do_mom (_, state)
=>
state;
end;
fun do_plea (SET_TEXT t, (draw, size))
=>
{ ti = make_text_info (root, result.aspect, t, result.width, result.fontinfo,
result.border_thickness, result.padx, result.pady);
result -> { textinfo, ... };
textinfo := ti;
xc::clear_drawable d;
block_until_mailop_fires (to_mom xc::REQ_RESIZE);
draw = drawf (d, size, result);
draw();
(draw, size);
};
do_plea (GET_TEXT reply_1shot, state)
=>
{ put_in_oneshot (reply_1shot, get_text result);
#
state;
};
do_plea (GET_SIZE_CONSTRAINT reply_1shot, state)
=>
{ put_in_oneshot (reply_1shot, size_preference_thunk_of result);
#
state;
};
do_plea (_, state)
=>
state;
end;
fun loop state
=
do_one_mailop [
plea' ==> (\\ plea = loop (do_plea (plea, state))),
from_other' ==> (\\ envelope = loop (do_mom (xc::get_contents_of_envelope envelope, state)))
];
loop (drawf (d, window_size, result), window_size);
}; # fun realize
fun init (root, result as { textinfo, ... } : Result, plea_slot)
=
loop ()
where
fun do_plea (SET_TEXT t)
=>
{ ti = make_text_info (root, result.aspect, t, result.width, result.fontinfo,
result.border_thickness, result.padx, result.pady);
textinfo := ti;
};
do_plea (GET_TEXT reply_1shot) => put_in_oneshot (reply_1shot, get_text result);
do_plea (GET_SIZE_CONSTRAINT reply_1shot) => put_in_oneshot (reply_1shot, size_preference_thunk_of result);
do_plea (DO_REALIZE arg ) => realize (root, arg, result, plea_slot);
end;
fun loop ()
=
for (;;) {
#
do_plea (take_from_mailslot plea_slot);
};
end;
fun message (root_window, view, args)
=
{ attributes = wg::find_attribute (wg::attributes (view, attributes, args));
#
result = get_resources (root_window, attributes);
plea_slot = make_mailslot ();
fun size_preference_thunk_of ()
=
{ reply_1shot = make_oneshot_maildrop ();
#
put_in_mailslot (plea_slot, GET_SIZE_CONSTRAINT reply_1shot);
get_from_oneshot reply_1shot;
};
make_thread "message" {.
#
init (root_window, result, plea_slot);
};
MESSAGE
{
plea_slot,
#
widget => wg::make_widget
{
root_window,
size_preference_thunk_of,
#
args => \\ () = { background => THE result.bg },
#
realize_widget => \\ arg = put_in_mailslot (plea_slot, DO_REALIZE arg)
}
};
};
fun as_widget (MESSAGE { widget, ... } )
=
widget;
fun set_text (MESSAGE { plea_slot, ... }, v)
=
put_in_mailslot (plea_slot, SET_TEXT v);
fun get_text (MESSAGE { plea_slot, ... } )
=
{ reply_1shot = make_oneshot_maildrop ();
#
put_in_mailslot (plea_slot, GET_TEXT reply_1shot);
get_from_oneshot reply_1shot;
};
}; # package message
end;