## guiboss-widget-layout.pkg
#
# A support library for
#
#
src/lib/x-kit/widget/gui/guiboss-imp.pkg#
# Here we handle the problem of assigning widgets specific
# pixmap sites on which to draw themselves. Our inputs
# are the Guipane tree describing the abstract layout
# together with the Widget_Layout_Hint information encoding
# preferences for the size and stretchiness of individual
# widgets.
#
# 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 ap = client_to_atom; # client_to_atom is from
src/lib/x-kit/xclient/src/iccc/client-to-atom.pkg# package au = authentication; # authentication is from
src/lib/x-kit/xclient/src/stuff/authentication.pkg# package cpm = cs_pixmap; # cs_pixmap is from
src/lib/x-kit/xclient/src/window/cs-pixmap.pkg# package cpt = cs_pixmat; # cs_pixmat is from
src/lib/x-kit/xclient/src/window/cs-pixmat.pkg# package dy = display; # display is from
src/lib/x-kit/xclient/src/wire/display.pkg# package fil = file__premicrothread; # file__premicrothread is from
src/lib/std/src/posix/file--premicrothread.pkg# package fti = font_index; # font_index is from
src/lib/x-kit/xclient/src/window/font-index.pkg# package r2k = xevent_router_to_keymap; # xevent_router_to_keymap is from
src/lib/x-kit/xclient/src/window/xevent-router-to-keymap.pkg# package mtx = rw_matrix; # rw_matrix is from
src/lib/std/src/rw-matrix.pkg# package rop = ro_pixmap; # ro_pixmap is from
src/lib/x-kit/xclient/src/window/ro-pixmap.pkg# package rw = root_window; # root_window is from
src/lib/x-kit/widget/lib/root-window.pkg# package rwv = rw_vector; # rw_vector is from
src/lib/std/src/rw-vector.pkg# package sep = client_to_selection; # client_to_selection is from
src/lib/x-kit/xclient/src/window/client-to-selection.pkg# package shp = shade; # shade is from
src/lib/x-kit/widget/lib/shade.pkg# package sj = socket_junk; # socket_junk is from
src/lib/internet/socket-junk.pkg# package x2s = xclient_to_sequencer; # xclient_to_sequencer is from
src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg# package tr = logger; # logger is from
src/lib/src/lib/thread-kit/src/lib/logger.pkg# package tsr = thread_scheduler_is_running; # thread_scheduler_is_running is from
src/lib/src/lib/thread-kit/src/core-thread-kit/thread-scheduler-is-running.pkg# package u1 = one_byte_unt; # one_byte_unt is from
src/lib/std/one-byte-unt.pkg# package v1u = vector_of_one_byte_unts; # vector_of_one_byte_unts is from
src/lib/std/src/vector-of-one-byte-unts.pkg# package v2w = value_to_wire; # value_to_wire is from
src/lib/x-kit/xclient/src/wire/value-to-wire.pkg# package wg = widget; # widget is from
src/lib/x-kit/widget/old/basic/widget.pkg# package wi = window; # window is from
src/lib/x-kit/xclient/src/window/window.pkg# package wme = window_map_event_sink; # window_map_event_sink is from
src/lib/x-kit/xclient/src/window/window-map-event-sink.pkg# package wpp = client_to_window_watcher; # client_to_window_watcher is from
src/lib/x-kit/xclient/src/window/client-to-window-watcher.pkg# package wy = widget_style; # widget_style is from
src/lib/x-kit/widget/lib/widget-style.pkg# package xc = xclient; # xclient is from
src/lib/x-kit/xclient/xclient.pkg# package xj = xsession_junk; # xsession_junk is from
src/lib/x-kit/xclient/src/window/xsession-junk.pkg# package xtr = xlogger; # xlogger is from
src/lib/x-kit/xclient/src/stuff/xlogger.pkg #
#
package evt = gui_event_types; # gui_event_types is from
src/lib/x-kit/widget/gui/gui-event-types.pkg package gts = gui_event_to_string; # gui_event_to_string is from
src/lib/x-kit/widget/gui/gui-event-to-string.pkg package gt = guiboss_types; # guiboss_types is from
src/lib/x-kit/widget/gui/guiboss-types.pkg package gtj = guiboss_types_junk; # guiboss_types_junk is from
src/lib/x-kit/widget/gui/guiboss-types-junk.pkg package a2r = windowsystem_to_xevent_router; # windowsystem_to_xevent_router is from
src/lib/x-kit/xclient/src/window/windowsystem-to-xevent-router.pkg package gd = gui_displaylist; # gui_displaylist is from
src/lib/x-kit/widget/theme/gui-displaylist.pkg package pp = standard_prettyprinter; # standard_prettyprinter is from
src/lib/prettyprint/big/src/standard-prettyprinter.pkg package err = compiler::error_message; # compiler is from
src/lib/core/compiler/compiler.pkg # error_message is from
src/lib/compiler/front/basics/errormsg/error-message.pkg package bt = gui_to_sprite_theme; # gui_to_sprite_theme is from
src/lib/x-kit/widget/theme/sprite/gui-to-sprite-theme.pkg package ct = gui_to_object_theme; # gui_to_object_theme is from
src/lib/x-kit/widget/theme/object/gui-to-object-theme.pkg package wt = widget_theme; # widget_theme is from
src/lib/x-kit/widget/theme/widget/widget-theme.pkg package boi = spritespace_imp; # spritespace_imp is from
src/lib/x-kit/widget/space/sprite/spritespace-imp.pkg package cai = objectspace_imp; # objectspace_imp is from
src/lib/x-kit/widget/space/object/objectspace-imp.pkg package pai = widgetspace_imp; # widgetspace_imp is from
src/lib/x-kit/widget/space/widget/widgetspace-imp.pkg #
package gtg = guiboss_to_guishim; # guiboss_to_guishim is from
src/lib/x-kit/widget/theme/guiboss-to-guishim.pkg package b2s = spritespace_to_sprite; # spritespace_to_sprite is from
src/lib/x-kit/widget/space/sprite/spritespace-to-sprite.pkg package c2o = objectspace_to_object; # objectspace_to_object is from
src/lib/x-kit/widget/space/object/objectspace-to-object.pkg package s2s = sprite_to_spritespace; # sprite_to_spritespace is from
src/lib/x-kit/widget/space/sprite/sprite-to-spritespace.pkg package o2o = object_to_objectspace; # object_to_objectspace is from
src/lib/x-kit/widget/space/object/object-to-objectspace.pkg package g2p = gadget_to_pixmap; # gadget_to_pixmap is from
src/lib/x-kit/widget/theme/gadget-to-pixmap.pkg package pl = paired_lists; # paired_lists is from
src/lib/std/src/paired-lists.pkg# package frm = frame; # frame is from
src/lib/x-kit/widget/leaf/frame.pkg package idm = id_map; # id_map is from
src/lib/src/id-map.pkg package im = int_red_black_map; # int_red_black_map is from
src/lib/src/int-red-black-map.pkg# package is = int_red_black_set; # int_red_black_set is from
src/lib/src/int-red-black-set.pkg package r8 = rgb8; # rgb8 is from
src/lib/x-kit/xclient/src/color/rgb8.pkg package r64 = rgb; # rgb is from
src/lib/x-kit/xclient/src/color/rgb.pkg package g2d = geometry2d; # geometry2d is from
src/lib/std/2d/geometry2d.pkg package g2j = geometry2d_junk; # geometry2d_junk is from
src/lib/std/2d/geometry2d-junk.pkg package ebi = millboss_imp; # millboss_imp is from
src/lib/x-kit/widget/edit/millboss-imp.pkg package e2g = millboss_to_guiboss; # millboss_to_guiboss is from
src/lib/x-kit/widget/edit/millboss-to-guiboss.pkg package gxi = translate_guipane_to_guipith; # translate_guipane_to_guipith is from
src/lib/x-kit/widget/gui/translate-guipane-to-guipith.pkg package gpj = guiboss_popup_junk; # guiboss_popup_junk is from
src/lib/x-kit/widget/gui/guiboss-popup-junk.pkg tracefile = "widget-unit-test.trace.log";
nb = log::note_on_stderr; # log is from
src/lib/std/src/log.pkgDummy1 = ebi::Millboss_Option; # XXX SUCKO DELETEME. This is a quick hack to make sure the package compiles during early development of it.
dummy2 = gxi::guipanes_to_guipiths; # XXX SUCKO DELETEME. This is a quick hack to make sure the package compiles during early development of it.
herein
package guiboss_widget_layout
: Guiboss_Widget_Layout # Guiboss_Widget_Layout is from
src/lib/x-kit/widget/gui/guiboss-widget-layout.api {
Dummy = Int;
Widget_Site_Info
=
{ id: Id,
subwindow_or_view: gt::Subwindow_Or_View, # A widget can be located either directly on a subwindow, or via a scrollport (which is ultimately visible on a subwindow, possibly via aother scrollports).
site: g2d::Box
};
fun gather_widget_layout_hints
{ me: gt::Guiboss_State,
guipane: gt::Guipane
}
: idm::Map( gt::Widget_Layout_Hint)
=
{ result = REF (idm::empty: idm::Map( gt::Widget_Layout_Hint));
#
gtj::guipane_apply (guipane, [ gtj::RG_WIDGET_FN rg_widget_fn ])
where
fun rg_widget_fn (rg_widget: gt::Rg_Widget)
=
{ widget_layout_hint = rg_widget.guiboss_to_widget.get_widget_layout_hint ();
#
result := idm::set( *result,
rg_widget.guiboss_to_widget.id,
widget_layout_hint
);
};
end;
*result;
};
fun lay_out_guipane # Assign to each widget in given widget-tree a pixel-rectangle on which to draw itself, in window coordinates.
{ # lay_out_widgets is called from restart_gui' in
src/lib/x-kit/widget/gui/guiboss-imp.pkg site: g2d::Box, # This is the available window rectangle to divide between our widgets.
rg_widget: gt::Rg_Widget_Type, # This is the tree of widgets -- possibly a single leaf widget.
subwindow_info: gt::Subwindow_Data,
widget_layout_hints: idm::Map( gt::Widget_Layout_Hint ),
me: gt::Guiboss_State
}
: idm::Map( Widget_Site_Info ) # Our result is a map from widget ids to assigned sites.
=
{ subwindow_info -> gt::SUBWINDOW_DATA subwindow_info;
subwindow_info = gt::SUBWINDOW_INFO subwindow_info;
compute_size_preferences_for_rg_widget_tree rg_widget;
assign_sites_to_all_widgets (site, subwindow_info, rg_widget);
*sites;
}
where
#
Sized_Widget
=
{ rg_widget: gt::Rg_Widget_Type,
size_prefs: gt::Widget_Layout_Hint,
col_number: Int,
row_number: Int
};
# Establish our result map:
#
sites = (REF idm::empty): Ref( idm::Map( Widget_Site_Info ) ); # Index is (id_to_int guiboss_to_gadget.id).
fun grid_dimensions (grid: List( List( Sized_Widget ) )): g2d::Size
=
{ high = list::length grid;
wide = int::list_max (map list::length grid);
#
{ high, wide };
};
fun nth_grid_row
(
grid: List( List( Sized_Widget ) ),
row: Int
)
: List( Sized_Widget )
=
list::nth (grid, row);
fun nth_grid_col
(
grid: List( List( Sized_Widget ) ),
col: Int
)
: List( Sized_Widget )
=
map get_colth grid
where
fun get_colth (row: List( Sized_Widget ))
=
list::nth (row, col);
end;
fun grid_cols (grid: List( List( Sized_Widget ) )) # Return all columns in grid.
=
{ (grid_dimensions grid) -> { high, wide };
#
grid_cols' (wide - 1, [])
where
fun grid_cols' (-1, result)
=>
result;
grid_cols' (i, result)
=>
grid_cols' (i - 1, (nth_grid_col (grid, i)) ! result);
end;
end;
};
fun get_widget_layout_hint (r: gt::Rg_Widget)
=
{ id = r.guiboss_to_widget.id;
# i = id_to_int id;
case (idm::get (widget_layout_hints, id))
#
THE hint => hint;
#
NULL => gt::default_widget_layout_hint; # This is not expected to happen; possibly we should log something here.
esac;
};
fun get__pixels_high_cut (widget: Sized_Widget) = widget.size_prefs.pixels_high_cut;
fun get__pixels_wide_cut (widget: Sized_Widget) = widget.size_prefs.pixels_wide_cut;
#
fun get__pixels_high_min (widget: Sized_Widget) = widget.size_prefs.pixels_high_min;
fun get__pixels_wide_min (widget: Sized_Widget) = widget.size_prefs.pixels_wide_min;
fun find_max_of_pixels_high_mins (widgets: List( Sized_Widget )) = int::list_max (map get__pixels_high_min widgets);
fun find_max_of_pixels_wide_mins (widgets: List( Sized_Widget )) = int::list_max (map get__pixels_wide_min widgets);
#
fun find_max_of_pixels_high_cuts (widgets: List( Sized_Widget )) = float::list_max (map get__pixels_high_cut widgets);
fun find_max_of_pixels_wide_cuts (widgets: List( Sized_Widget )) = float::list_max (map get__pixels_wide_cut widgets);
fun compute_size_preferences_for_grid_rows ([]: List( List( gt::Rg_Widget_Type )), row_number, result_rows): List(List(Sized_Widget)) # A little helper fn used in the gt::RG_GRID cases of both fun compute_size_preferences_for_rg_widget_tree and assign_sites_to_all_widgets.
=>
reverse result_rows;
compute_size_preferences_for_grid_rows (row ! rest, row_number, result)
=>
compute_size_preferences_for_grid_rows (rest, row_number + 1, compute_size_preferences_for_grid_row (row, 0, []) ! result)
where
fun compute_size_preferences_for_grid_row ([]: List(gt::Rg_Widget_Type), col_number, result_row)
=>
reverse result_row;
compute_size_preferences_for_grid_row (rg_widget ! rest, col_number, result_row)
=>
{
entry = { rg_widget,
size_prefs => compute_size_preferences_for_rg_widget_tree rg_widget,
row_number,
col_number
};
compute_size_preferences_for_grid_row (rest, col_number + 1, entry ! result_row);
};
end;
end;
end
also
fun compute_size_preferences_for_rg_widget_tree
#
(rg_widget: gt::Rg_Widget_Type) # This is the tree of widgets -- possibly a single leaf widget.
=
case rg_widget
#
gt::RG_ROW r
=>
{ do_row (r.widgets, 0, 0, 1.0, 1.0)
where
fun do_row ([], pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut)
=>
{ result = { pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
#
r.widget_layout_hint := result;
result;
};
do_row ((widget: gt::Rg_Widget_Type) ! rest, pixels_high_min', pixels_wide_min', pixels_high_cut', pixels_wide_cut')
=>
{ (compute_size_preferences_for_rg_widget_tree widget) # This widget may be (for example) a nested ROW, COL or GRID: If so, process it recursively.
->
{ pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
pixels_high_min = max (pixels_high_min, pixels_high_min');
pixels_high_cut = max (pixels_high_cut, pixels_high_cut');
#
pixels_wide_min = (pixels_wide_min + pixels_wide_min');
pixels_wide_cut = max (pixels_wide_cut, pixels_wide_cut');
do_row (rest, pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut);
};
end;
end;
};
gt::RG_COL r
=>
{ do_col (r.widgets, 0, 0, 1.0, 1.0)
where
fun do_col ([], pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut)
=>
{ result = { pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
#
r.widget_layout_hint := result;
result;
};
do_col ((widget: gt::Rg_Widget_Type) ! rest, pixels_high_min', pixels_wide_min', pixels_high_cut', pixels_wide_cut')
=>
{ (compute_size_preferences_for_rg_widget_tree widget) # This widget may be (for example) a nested ROW, COL or GRID: If so, process it recursively.
->
{ pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
pixels_high_min = (pixels_high_min + pixels_high_min');
pixels_high_cut = max (pixels_high_cut, pixels_high_cut');
#
pixels_wide_min = max (pixels_wide_min, pixels_wide_min');
pixels_wide_cut = max (pixels_wide_cut, pixels_wide_cut');
do_col (rest, pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut);
};
end;
end;
};
gt::RG_GRID r
=>
{ grid = compute_size_preferences_for_grid_rows (r.widgets, 0, []);
#
rows = grid;
cols = grid_cols grid; # Columns of grid, so we can compute column-by-column values conveniently.
row_high_mins = map find_max_of_pixels_high_mins rows; # The min height for each row is the max of the min-heights of the widgets in that row.
col_wide_mins = map find_max_of_pixels_wide_mins cols; # The min width for each col is the max of the min-widths of the widgets in that col.
pixels_high_min = int::sum row_high_mins; # The min height for the grid widget is the sum of the row min-heights.
pixels_wide_min = int::sum col_wide_mins; # The min width for the grid widget is the sum of the col min-widths.
all_widgets = list::cat grid;
pixels_high_cut = float::list_max (map get__pixels_high_cut all_widgets); # The grid high-cut is max height-cut over all widgets in the grid.
pixels_wide_cut = float::list_max (map get__pixels_wide_cut all_widgets); # The grid high-cut is max height-cut over all widgets in the grid.
{ pixels_high_min,
pixels_wide_min,
#
pixels_high_cut,
pixels_wide_cut
};
};
gt::RG_MARK r
=>
{ (compute_size_preferences_for_rg_widget_tree r.widget) # This widget may be (for example) a nested ROW, COL or GRID: If so, process it recursively.
->
{ pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
h = { pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
r.widget_layout_hint := h;
h;
};
gt::RG_FRAME r
=>
{ (compute_size_preferences_for_rg_widget_tree r.widget) # This widget may be (for example) a nested ROW, COL or GRID: If so, process it recursively.
->
{ pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
frame_indent_hint
=
case r.frame_widget
#
gt::RG_WIDGET { guiboss_to_widget, ... }
=>
guiboss_to_widget.get_frame_indent_hint ();
_ => gt::default_frame_indent_hint;
esac;
pixels_high_min = pixels_high_min
+ frame_indent_hint.pixels_for_top_of_frame
+ frame_indent_hint.pixels_for_bottom_of_frame
;
pixels_wide_min = pixels_wide_min
+ frame_indent_hint.pixels_for_left_of_frame
+ frame_indent_hint.pixels_for_right_of_frame
;
h = { pixels_high_min, pixels_wide_min, pixels_high_cut, pixels_wide_cut };
r.widget_layout_hint := h;
h;
};
gt::RG_SCROLLPORT r
=>
{ # Supply default layout parameters.
{ pixels_high_min => 0,
pixels_wide_min => 0,
#
pixels_high_cut => 1.0,
pixels_wide_cut => 1.0
};
};
gt::RG_TABPORT r
=>
{ # Supply default layout parameters.
{ pixels_high_min => 0,
pixels_wide_min => 0,
#
pixels_high_cut => 1.0,
pixels_wide_cut => 1.0
};
};
gt::RG_WIDGET r
=>
get_widget_layout_hint r;
gt::RG_OBJECTSPACE r
=>
{
msg = sprintf "do_re_site_widget_tree/pass1/OBJECTSPACE unimplemented";
nb {. msg; };
raise exception DIE msg;
};
gt::RG_SPRITESPACE r
=>
{
msg = sprintf "do_re_site_widget_tree/pass1/SPRITESPACE unimplemented";
nb {. msg; };
raise exception DIE msg;
};
gt::RG_NULL_WIDGET /* r */
=>
{ pixels_wide_min => 0, pixels_high_min => 0, pixels_wide_cut => 0.0, pixels_high_cut => 0.0 };
esac;
fun wide_min_for_widget ( widget as gt::RG_WIDGET r : gt::Rg_Widget_Type) => (get_widget_layout_hint r).pixels_wide_min;
wide_min_for_widget ( widget as gt::RG_ROW { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_min;
wide_min_for_widget ( widget as gt::RG_COL { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_min;
wide_min_for_widget ( widget as gt::RG_GRID { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_min;
wide_min_for_widget ( widget as gt::RG_MARK { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_min;
wide_min_for_widget ( widget as gt::RG_FRAME { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_min;
wide_min_for_widget _ => 0;
end;
fun high_min_for_widget ( widget as gt::RG_WIDGET r : gt::Rg_Widget_Type) => (get_widget_layout_hint r).pixels_high_min;
high_min_for_widget ( widget as gt::RG_ROW { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_min;
high_min_for_widget ( widget as gt::RG_COL { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_min;
high_min_for_widget ( widget as gt::RG_GRID { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_min;
high_min_for_widget ( widget as gt::RG_MARK { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_min;
high_min_for_widget ( widget as gt::RG_FRAME { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_min;
high_min_for_widget _ => 0;
end;
fun wide_cut_for_widget ( widget as gt::RG_WIDGET r : gt::Rg_Widget_Type) => (get_widget_layout_hint r).pixels_wide_cut;
wide_cut_for_widget ( widget as gt::RG_ROW { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_cut;
wide_cut_for_widget ( widget as gt::RG_COL { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_cut;
wide_cut_for_widget ( widget as gt::RG_GRID { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_cut;
wide_cut_for_widget ( widget as gt::RG_MARK { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_cut;
wide_cut_for_widget ( widget as gt::RG_FRAME { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_wide_cut;
wide_cut_for_widget _ => 1.0;
end;
fun high_cut_for_widget ( widget as gt::RG_WIDGET r : gt::Rg_Widget_Type) => (get_widget_layout_hint r).pixels_high_cut;
high_cut_for_widget ( widget as gt::RG_ROW { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_cut;
high_cut_for_widget ( widget as gt::RG_COL { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_cut;
high_cut_for_widget ( widget as gt::RG_GRID { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_cut;
high_cut_for_widget ( widget as gt::RG_MARK { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_cut;
high_cut_for_widget ( widget as gt::RG_FRAME { widget_layout_hint => h, ... }: gt::Rg_Widget_Type) => (*h).pixels_high_cut;
high_cut_for_widget _ => 1.0;
end;
fun assign_sites_to_all_widgets
( site: g2d::Box, # This is the available window rectangle to divide between our widgets.
subwindow_or_view: gt::Subwindow_Or_View, # This is the pixmap on which our site is located.
rg_widget: gt::Rg_Widget_Type # This is the tree of widgets -- possibly a single leaf widget.
)
=
case rg_widget
#
gt::RG_MARK r
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
#
min_widths = wide_min_for_widget r.widget; # Compute total pixels needed by fixed-width widgets. Fixed-width widgets get first call on available space; variable-width widgets divide up what is left.
total_cut = wide_cut_for_widget r.widget; # Sum cut values of all widgets. Each variable-width widget will get widget.share/total_cut of sharable_pixels. Which might be zero.
total_cut = if (total_cut > 0.0) total_cut;
else 1.0; # Prevent divide-by-zero in subsequent logic.
fi;
site -> { row, col, high, wide };
sharable_pixels # Compute pixels remaining after all fixed-width widgets have been given their cut.
=
if (wide > min_widths) float::from_int (wide - min_widths); #
else 0.0; # No pixels left after giving fixed-width widgets their allotments.
fi;
wide_min = wide_min_for_widget r.widget;
wide_cut = wide_cut_for_widget r.widget;
pixels_for_this_widget
#
= wide_min
+ float::floor ((wide_cut / total_cut) * sharable_pixels);
site = { row, col, high, wide => pixels_for_this_widget };
assign_sites_to_all_widgets # This widget may be a nested ROW, COL, GRID, SCROALLBLE_VIEW (...) so assign sites recursively within it.
(
site,
subwindow_or_view,
r.widget
);
};
gt::RG_ROW r
=>
{ r.site := site; # Remember this widget's assigned site on its home pixmap.
#
widgets = r.widgets;
fun assign_sites_to_widgets (widgets, site, dry_run, first_widget, extra_pixels_left, total_pixels_allocated)
=
{
my (site, widgets, widget1_pixels)
=
case (r.first_cut, widgets)
#
(THE fraction, first_widget ! remaining_widgets) # If RG_ROW.first_cut is set *and* we have at least two widgets in the ROW
=> # then 'fraction' overrides our usual pixel-allocation formula. We use this mechanism to support "C-x ^" in our emacs-ish editor.
{ site -> { row, col, high, wide };
#
pixels_for_first_widget
=
float::floor (fraction * (float::from_int wide));
if (not dry_run) # Allocate pixels to first_widget specially.
#
site1 = { row, col, high, wide => pixels_for_first_widget };
assign_sites_to_all_widgets # This widget may be a nested ROW, COL, GRID, SCROALLBLE_VIEW (...) so assign sites recursively within it.
(
site1,
subwindow_or_view,
first_widget
);
fi;
site = { row, col => col + pixels_for_first_widget, # Allocate pixels to the remaining widgets normally.
high, wide => wide - pixels_for_first_widget
};
(site, remaining_widgets, pixels_for_first_widget);
};
_ => (site, widgets, 0); # Normal case: All widgets get allocated space by same logic.
esac;
min_widths = int::sum (map wide_min_for_widget widgets); # Compute total pixels needed by fixed-width widgets. Fixed-width widgets get first call on available space; variable-width widgets divide up what is left.
total_cut = float::sum (map wide_cut_for_widget widgets); # Sum cut values of all widgets. Each variable-width widget will get widget.share/total_cut of sharable_pixels. Which might be zero.
total_cut = if (total_cut > 0.0) total_cut;
else 1.0; # Prevent divide-by-zero in subsequent logic.
fi;
site -> { row, col, high, wide };
sharable_pixels # Compute pixels remaining after all fixed-width widgets have been given their cut.
=
if (wide > min_widths) float::from_int (wide - min_widths); #
else 0.0; # No pixels left after giving fixed-width widgets their allotments.
fi;
fun assign_sites_to_widgets' ([], col, dry_run, first_widget, extra_pixels_left, total_pixels_allocated)
=>
total_pixels_allocated;
assign_sites_to_widgets'
(
(w as (widget: gt::Rg_Widget_Type)) ! rest,
col,
dry_run,
first_widget,
extra_pixels_left,
total_pixels_allocated
)
=>
{
pixels_for_this_widget
#
=
{ wide_min = wide_min_for_widget w;
wide_cut = wide_cut_for_widget w;
wide_min
+ float::floor ((wide_cut / total_cut) * sharable_pixels);
};
my (pixels_for_this_widget, extra_pixels_left)
=
if (extra_pixels_left == 0) (pixels_for_this_widget, extra_pixels_left );
else (pixels_for_this_widget + 1, extra_pixels_left - 1);
fi;
site = { row, col, high, wide => pixels_for_this_widget };
if (not dry_run)
#
assign_sites_to_all_widgets # This widget may be a nested ROW, COL, GRID, SCROALLBLE_VIEW (...) so assign sites recursively within it.
(
site,
subwindow_or_view,
widget
);
fi;
assign_sites_to_widgets' # Do remaining widgets in this ROW.
(
rest,
col + pixels_for_this_widget,
dry_run,
FALSE, # first_widget
extra_pixels_left,
pixels_for_this_widget + total_pixels_allocated
);
};
end;
total_pixels_allocated
=
assign_sites_to_widgets' (widgets, col, dry_run, first_widget, extra_pixels_left, total_pixels_allocated)
+
widget1_pixels;
(total_pixels_allocated, sharable_pixels, min_widths);
};
(assign_sites_to_widgets (widgets, site, TRUE, TRUE, 0, 0))
->
(total_pixels_allocated, sharable_pixels, min_widths);
extra_pixels_left
=
(min_widths + (float::floor sharable_pixels)) - total_pixels_allocated;
assign_sites_to_widgets (widgets, site, FALSE, TRUE, extra_pixels_left, 0);
();
};
gt::RG_COL r
=>
{ r.site := site; # Remember this widget's assigned site on its home pixmap.
#
widgets = r.widgets;
fun assign_sites_to_widgets (widgets, site, dry_run, first_widget, extra_pixels_left, total_pixels_allocated)
=
{
my (site, widgets, widget1_pixels)
=
case (r.first_cut, widgets)
#
(THE fraction, first_widget ! remaining_widgets) # If RG_ROW.first_cut is set *and* we have at least two widgets in the ROW
=> # then 'fraction' overrides our usual pixel-allocation formula. We use this mechanism to support "C-x ^" in our emacs-ish editor.
{ site -> { row, col, high, wide };
#
pixels_for_first_widget
=
float::floor (fraction * (float::from_int high));
if (not dry_run) # Allocate pixels to first_widget specially.
#
site1 = { row, col, wide, high => pixels_for_first_widget };
assign_sites_to_all_widgets # This widget may be a nested ROW, COL, GRID, SCROALLBLE_VIEW (...) so assign sites recursively within it.
(
site1,
subwindow_or_view,
first_widget
);
fi;
site = { col, row => row + pixels_for_first_widget, # Allocate pixels to the remaining widgets normally.
wide, high => high - pixels_for_first_widget
};
(site, remaining_widgets, pixels_for_first_widget);
};
_ => (site, widgets, 0); # Normal case: All widgets get allocated space by same logic.
esac;
min_heights = int::sum (map high_min_for_widget widgets); # Compute total pixels needed by fixed-height widgets. Fixed-height widgets get first call on available space; variable-height widgets divide up what is left.
total_cut = float::sum (map high_cut_for_widget widgets); # Sum cut values of all widgets. Each variable-height widget will get widget.share/total_cut of sharable_pixels. Which might be zero.
total_cut = if (total_cut > 0.0) total_cut;
else 1.0; # Prevent divide-by-zero in subsequent logic.
fi;
site -> { row, col, high, wide };
sharable_pixels # Compute pixels remaining after all fixed-width widgets have been given their cut.
=
if (high > min_heights) float::from_int (high - min_heights); #
else 0.0; # No pixels left after giving fixed-width widgets their allotments.
fi;
fun assign_sites_to_widgets' ([], row, dry_run, first_widget, extra_pixels_left, total_pixels_allocated)
=>
total_pixels_allocated;
assign_sites_to_widgets'
(
(w as (widget: gt::Rg_Widget_Type)) ! rest,
row,
dry_run,
first_widget,
extra_pixels_left,
total_pixels_allocated
)
=>
{
pixels_for_this_widget
#
=
{ high_min = high_min_for_widget w;
high_cut = high_cut_for_widget w;
high_min
+ float::floor ((high_cut / total_cut) * sharable_pixels);
};
my (pixels_for_this_widget, extra_pixels_left)
=
if (extra_pixels_left == 0) (pixels_for_this_widget, extra_pixels_left );
else (pixels_for_this_widget + 1, extra_pixels_left - 1);
fi;
site = { row, col, wide, high => pixels_for_this_widget };
if (not dry_run)
#
assign_sites_to_all_widgets # This widget may be a nested ROW, COL, GRID, SCROALLBLE_VIEW (...) so assign sites recursively within it.
(
site,
subwindow_or_view,
widget
);
fi;
assign_sites_to_widgets' # Do remaining widgets in this ROW.
(
rest,
row + pixels_for_this_widget,
dry_run,
FALSE, # first_widget
extra_pixels_left,
pixels_for_this_widget + total_pixels_allocated
);
};
end;
total_pixels_allocated
=
assign_sites_to_widgets' (widgets, row, dry_run, first_widget, extra_pixels_left, total_pixels_allocated)
+
widget1_pixels;
(total_pixels_allocated, sharable_pixels, min_heights);
};
(assign_sites_to_widgets (widgets, site, TRUE, TRUE, 0, 0))
->
(total_pixels_allocated, sharable_pixels, min_heights);
extra_pixels_left
=
(min_heights + (float::floor sharable_pixels)) - total_pixels_allocated;
assign_sites_to_widgets (widgets, site, FALSE, TRUE, extra_pixels_left, 0);
();
};
gt::RG_GRID r
=>
{ r.site := site; # Remember this widget's assigned site on its home pixmap.
#
grid = compute_size_preferences_for_grid_rows (r.widgets, 0, []);
rows = grid; # Rows of grid, so we can compute column-by-row values conveniently.
cols = grid_cols grid; # Columns of grid, so we can compute column-by-column values conveniently.
row_high_mins = map find_max_of_pixels_high_mins rows; # The min height for each row is the max of the min-heights of the widgets in that row.
col_wide_mins = map find_max_of_pixels_wide_mins cols; # The min width for each col is the max of the min-widths of the widgets in that col.
row_high_cuts = map find_max_of_pixels_high_cuts rows; #
col_wide_cuts = map find_max_of_pixels_wide_cuts cols; #
row_highs = pl::zip (row_high_mins, row_high_cuts);
col_wides = pl::zip (col_wide_mins, col_wide_cuts);
total_high_min = int::sum row_high_mins; # The min height for the grid widget is the sum of the row min-heights.
total_wide_min = int::sum col_wide_mins; # The min width for the grid widget is the sum of the col min-widths.
total_high_cut = float::sum row_high_cuts; # The grid high-cut is max high-cut over all rows in the grid.
total_wide_cut = float::sum col_wide_cuts; # The grid wide-cut is max wide-cut over all cols in the grid.
total_high_cut = if (total_high_cut > 0.0) total_high_cut;
else 1.0; # Prevent divide-by-zero in subsequent logic.
fi;
total_wide_cut = if (total_wide_cut > 0.0) total_wide_cut;
else 1.0; # Prevent divide-by-zero in subsequent logic.
fi;
site -> { row, col, high, wide };
sharable_vertical_pixels # Compute pixels remaining after all fixed-height widgets have been given their cut.
=
if (high > total_high_min) float::from_int (high - total_high_min); #
else 0.0; # No pixels left after giving fixed-height widgets their allotments.
fi;
sharable_horizontal_pixels # Compute pixels remaining after all fixed-height widgets have been given their cut.
=
if (high > total_wide_min) float::from_int (wide - total_wide_min); #
else 0.0; # No pixels left after giving fixed-height widgets their allotments.
fi;
col_wides # For each column, its width in pixels.
=
map assign_width_to_column col_wides
where
fun assign_width_to_column (col_wide_min, col_wide_cut)
=
{
pixels_for_this_col = col_wide_min
+ float::floor ((col_wide_cut / total_wide_cut) * sharable_horizontal_pixels);
pixels_for_this_col;
};
end; # NB: The 'floor' op results in fractional pixels being discarded.
# We should distribute them among the widgets instead -- see
# the RG_ROW and RG_COL logic for guidance. XXX SUCKO FIXME.
row_highs # For each row, its height in pixels.
=
map assign_width_to_row row_highs
where
fun assign_width_to_row (row_high_min, row_high_cut)
=
{
pixels_for_this_row = row_high_min
+ float::floor ((row_high_cut / total_high_cut) * sharable_vertical_pixels);
pixels_for_this_row;
};
end;
assign_sites_to_grid_widget_rows (grid, row, col)
where
fun assign_sites_to_grid_widget_rows
(
[]: List(List(Sized_Widget)), # We need to assign window sites to all these widgets
row: Int, # This tracks which vertical window pixel on which current widget should be located.
col: Int # This tracks which horizontal window pixel on which current widget should be located.
)
=>
();
assign_sites_to_grid_widget_rows (widget_row ! rest, row, col)
=>
{ high = assign_sites_to_grid_widget_row (widget_row, row, col);
#
assign_sites_to_grid_widget_rows (rest, row + high, col);
}
where
fun assign_sites_to_grid_widget_row
(
[]: List(Sized_Widget),
row: Int, # Which pixel row in window coordinates should current widget be located at?
col: Int # Which pixel col in window coordinates should current widget be located at?
)
=>
0;
assign_sites_to_grid_widget_row (widget ! rest, row, col)
=>
{ wide = list::nth (col_wides, widget.col_number);
high = list::nth (row_highs, widget.row_number);
site = { row, col, wide, high };
assign_sites_to_all_widgets # This widget may be a nested ROW, COL, GRID, SCROALLBLE_VIEW (...) so assign sites recursively within it.
(
site,
subwindow_or_view,
widget.rg_widget
);
assign_sites_to_grid_widget_row (rest, row, col + wide);
high;
};
end;
end;
end;
end;
};
gt::RG_SCROLLPORT r
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
#
# Now to do recursive layout of this view:
subwidget = *r.rg_widget;
pixmap_size = r.pixmap.size;
subsite = { row => 0, high => pixmap_size.high, # Initially we have the entire subwindow_or_view for the view available for assignment to widgets.
col => 0, wide => pixmap_size.wide
};
subpixmap = gt::SCROLLABLE_INFO r;
assign_sites_to_all_widgets (subsite, subpixmap, subwidget); # Recursively lay out the view's widget-tree on its pixmap.
};
gt::RG_TABPORT r
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
#
############
# This is untested, immature code. 'pixmap_size' is inherited from
# scrollport, where it makes sense, since the size of the scrollable
# area visible through the viewport bears no necessary relation to
# scrollport.
#
# But the tabs all have the same size at the tabport, so either we
# should resize our pixmaps dynamically once we know what our site
# size is, or else maybe we shouldn't have per-tab pixmaps at all,
# and should just render our widgets directly onto our parent.
#
# This needs thought. For now, the below code is at least a placeholder
# and a zero-th order approximation to what we need.
#
# The following magic constant is also buried in
# pg_widget__to__rg_widget/do_pg_widget/gt::RG_TABPORT in
src/lib/x-kit/widget/gui/guiboss-imp.pkg############
pixmap_size = { high => 400, wide => 800 }: g2d::Size;
apply do_tab r.tabs #
# #
where
fun do_tab (subpixmap: gt::Tabbable_Info)
=
{ subpixmap -> { rg_widget => subwidget, pixmap => view_pixmap, ... };
# Now to do recursive layout of this view:
#
subsite = { row => 0, high => pixmap_size.high, # Initially we have the entire subwindow_or_view for the view available for assignment to widgets.
col => 0, wide => pixmap_size.wide
};
subpixmap = gt::TABBABLE_INFO subpixmap;
assign_sites_to_all_widgets (subsite, subpixmap, subwidget); # Recursively lay out the view's widget-tree on its pixmap.
};
end;
};
gt::RG_FRAME r
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
#
assign_sites_to_all_widgets (site, subwindow_or_view, r.frame_widget); # Assign full site to frame_widget, which will almost always be
src/lib/x-kit/widget/leaf/frame.pkg # or
src/lib/x-kit/widget/leaf/popupframe.pkg frame_indent_hint
=
case r.frame_widget
#
gt::RG_WIDGET { guiboss_to_widget, ... }
=>
guiboss_to_widget.get_frame_indent_hint ();
_ => gt::default_frame_indent_hint;
esac;
indented_site = gtj::make_nested_box (site, frame_indent_hint);
assign_sites_to_all_widgets (indented_site, subwindow_or_view, r.widget); # Recursively lay out the framed widgets. We expect r.widget to typically be a ROW, COL or GRID compound widget.
};
gt::RG_WIDGET (r as { guiboss_to_widget, ... })
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
#
widget_site_info
=
{ id => guiboss_to_widget.id,
subwindow_or_view => subwindow_or_view,
site
};
sites := idm::set (*sites, guiboss_to_widget.id, widget_site_info);
};
gt::RG_OBJECTSPACE r
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
msg = sprintf "do_re_site_widget_tree/pass2/OBJECTSPACE unimplemented";
nb {. msg; };
raise exception DIE msg;
};
gt::RG_SPRITESPACE r
=>
{
r.site := site; # Remember this widget's assigned site on its home pixmap.
msg = sprintf "do_re_site_widget_tree/pass2/SPRITESPACE unimplemented";
nb {. msg; };
raise exception DIE msg;
};
gt::RG_NULL_WIDGET /* r */
=>
();
esac;
end;
# fun lay_out_subwindow
# (
# subwindow_info: gt::Subwindow_Data, #
# hostwindow_for_gui: gtg::Guiboss_To_Hostwindow, # This provides redraw_all_guipanes with the window on which to do the redraw.
# me: gt::Guiboss_State,
# to: Replyqueue
# )
# =
# {
# hostwindow_for_gui.pass_window_site to
# #
# (\\ ({ size => { high => hostwindow_high, wide => hostwindow_wide }, ... }: g2d::Window_Site) # hostwindow_high and hostwindow_wide are currently unused, but you'd think we'd need them eventually.
# =
# {
# subwindow_info
# =
# case subwindow_info
# #
# gt::SUBWINDOW_DATA r
# =>
# r;
# esac;
#
# # gwl::lay_out_widgets # Assign to each widget in given widget-tree a pixel-rectangle on which to draw itself, in window coordinates.
# # :
# # { site: g2d::Box, # This is the available window rectangle to divide between our widgets.
# # rg_widget: gt::Rg_Widget_Type, # This is the tree of widgets -- possibly a single leaf widget.
# # subwindow_info: gt::Subwindow_Data,
# # widget_layout_hints: im::Map( gt::Widget_Layout_Hint ),
# # me: gt::Guiboss_State
# # }
# # -> im::Map( Widget_Site_Info ); # Our result is a map from widget ids to assigned sites.
#
# subwindow_info
# ->
# { guipane: Ref( Null_Or( gt::Guipane ) ),
# pixmap: Ref( g2p::Gadget_To_Rw_Pixmap ), # Main backing store for this running gui.
# popups: Ref(List(gt::Subwindow_Data)), # These will all be SUBWINDOW_INFO, so 'Ref(List(gt::Subwindow_Info))' would be a better type here.
# parent: Null_Or( gt::Subwindow_Data ), # For popups this points to the parent; for the original non-popup window it is NULL.
# stacking_order: Int, # Assigned in increasing order starting at 1; these determine who overlies who visually on the screen in case of overlaps. (Popups must be entirely within parent, but sibling popups can overlap.)
# upperleft: Ref(g2d::Point) # If we have a parent, this gives our location on it. Note that pixmap.size gives our size.
# };
#
# case *guipane
# #
# NULL => ();
# THE guipane
# =>
# {
# hostwindow_info = idm::get_or_raise_exception_not_found (*me.hostwindows, hostwindow_for_gui.id)
# except
# NOT_FOUND = { printf "*me.hostwindows contains no entry for hostwindow %d?! -- restart_gui' in guiboss-imp.pkg\n" (id_to_int hostwindow_for_gui.id);
# log::fatal (sprintf "*me.hostwindows contains no entry for hostwindow %d?! -- restart_gui' in guiboss-imp.pkg" (id_to_int hostwindow_for_gui.id));
# raise exception NOT_FOUND; # Execution will never reach this point, but the compiler doesn't know that log::fatal doesn't return.
# };
# (*pixmap).size -> { high, wide };
#
# site = { col => 0, high, # Allocate all of window pixel area to widgets in guipane.rg_widget widget-tree.
# row => 0, wide
# }
# : g2d::Box;
#
# # guipane.guiboss_to_widgetspace.pass_re_siting_done_flag # Ask widgetspace-imp to re-do widget layout for this gui (either the base gui for this hostwindow or else a popup gui for this hostwindow).
# # ( site,
# # guipane.subwindow_info,
# # guipane.rg_widget,
# # *me.widget_layout_hints
# # )
# # to
# # {.
# # # The code hereabouts was adapted from restart_gui'
# # # which does the following, but if this is needed at
# # # all it seems like it should be done in redraw_all_guipanes
# # # (below) rather than here, so I've commented it out for now: -- 2015-01-17 CrT
# # #
# # # gtj::guipane_postorder_apply # If a view pixmap is too small to fill its scrollport there will be undefined pixels showing in the scrollport.
# # # ( # By setting the upperleft to its default 0,0 we trigger the logic to black out these undefined areas.
# # # guipane, # Does doing so result in a double-draw of views at GUI startup? If so, that might someday prove problematic: XXX QUERO FIXME
# # # [ gt::RG_SCROLLPORT_FN
# # # \\ (scrollable_view: gt::Scrollable_View)
# # # =
# # # (*scrollable_view.scroller).set_scrollport_upperleft
# # #
# # # *scrollable_view.upperleft
# # # ]
# # # );
# #
# # ();
# # };
#
# gwl::lay_out_widgets # Assign to each widget in given widget-tree a pixel-rectangle on which to draw itself, in window coordinates.
# {
# me,
# site, # This is the available window rectangle to divide between our widgets.
# rg_widget, # This is the tree of widgets -- possibly a single leaf widget.
# subwindow_info,
# widget_layout_hints,
# note_widget_site'
# };
#
# # # The code hereabouts was adapted from restart_gui'
# # # which does the following, but if this is needed at
# # # all it seems like it should be done in redraw_all_guipanes
# # # (below) rather than here, so I've commented it out for now: -- 2015-01-17 CrT
# # #
# # # gtj::guipane_postorder_apply # If a view pixmap is too small to fill its scrollport there will be undefined pixels showing in the scrollport.
# # # ( # By setting the origin to its default 0,0 we trigger the logic to black out these undefined areas.
# # # guipane, # Does doing so result in a double-draw of views at GUI startup? If so, that might someday prove problematic: XXX QUERO FIXME
# # # [ gt::RG_SCROLLPORT_FN
# # # \\ (scrollable_view: gt::Scrollable_View)
# # # =
# # # (*scrollable_view.scroller).set_scrollport_upperleft
# # #
# # # *scrollable_view.upperleft
# # # ]
# # # );
# #
# # ();
# # };
# };
# esac;
# }
# );
# };
#
# fun lay_out_all_guipanes # This fn is intended to re-layout all running guis for one hostwindow. Untested but should be at least approximately right. -- 2015-01-17 CrT
# ( #
# subwindow_info: gt::Subwindow_Data, # This provides redraw_all_guipanes an entrypoint into the remaining Subwindow_Or_View tree. Any Subwindow_Or_View in the tree would do.
# hostwindow_for_gui: gtg::Guiboss_To_Hostwindow, # This provides redraw_all_guipanes with the window on which to do the redraw.
# me: gt::Guiboss_State,
# to: Replyqueue
# )
# =
# {
# subwindow_datas_to_lay_out
# =
# gtj::find_all_subwindow_datas_above_given_stacking_order
# #
# (subwindow_info, 0); # 'stacking_order' fields are always positive, so searching for all Subwindow_Or_View instances with stacking_order > 0 gets us everything.
#
# apply lay_out_one_subwindow subwindow_datas_to_lay_out;
# where
# fun lay_out_one_subwindow (subwindow: gt::Subwindow_Data)
# =
# lay_out_subwindow (subwindow, hostwindow_for_gui, me, to);
# end;
# };
#
# This is actually operational, currently called (only) from kill_gui' in
src/lib/x-kit/widget/gui/guiboss-imp.pkg fun redraw_all_guipanes # Intended to be called after changing the popup structure -- killing a popup, moving a popup, whatever. (Not needed after just creating a new popup.)
( # For our purposes here the base window is just one more popup, which happens to never go away. I.e., for us, "popup" == "gt::SUBWINDOW_INFO".
subwindow_info: gt::Subwindow_Data, # This provides redraw_all_guipanes an entrypoint into the remaining Subwindow_Or_View tree. Any Subwindow_Or_View in the tree would do.
hostwindow_for_gui: gtg::Guiboss_To_Hostwindow # This provides redraw_all_guipanes with the window on which to do the redraw.
)
=
{
subwindow_infos_to_redraw
=
gtj::find_all_subwindow_datas_above_given_stacking_order
#
(subwindow_info, 0); # 'stacking_order' fields are always positive, so searching for all Subwindow_Or_View instances with stacking_order > 0 gets us everything.
apply redraw_subwindow_info subwindow_infos_to_redraw
where
fun redraw_subwindow_info
(
subwindow_info: gt::Subwindow_Data
)
=
{
subwindow_info -> gt::SUBWINDOW_DATA subwindow_info;
subwindow_or_view = gt::SUBWINDOW_INFO subwindow_info;
size = (*subwindow_info.pixmap).size; # Redraw all of running gui.
upperleft = g2d::point::zero; # We need from_box to be in subwindow_or_view coordinate system, so upperleft will always be zero.
#
from_box = g2d::box::make (upperleft, size); # Our from_box covers the entire popup pixmap, so as to redraw all of it.
gpj::update_offscreen_parent_pixmaps_and_then_hostwindow # Redraw all visible parts of popup. (In our situation there is no offscreen updating to be done.)
#
(subwindow_or_view, from_box, hostwindow_for_gui);
};
end;
};
};
end;