Tk Source Code

View Ticket
Login
Ticket UUID: 3049518
Title: Fire Event when a used font changes
Type: RFE Version: None
Submitter: oehhar Created on: 2010-08-20 17:20:21
Subsystem: 44. Generic Fonts Assigned To: aku
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2021-11-04 18:26:57
Resolution: Fixed Closed By: griffin
    Closed on: 2021-11-04 18:26:57
Description:
When a named font is used by a widget:
% font create Custom -size 8
% label .l -font Custom
and the font is reconfigured:
% font configure Custom -size 17
all core widgets automatically change their size.

If a megawidget uses a named font and does some position calculations on the font size,
there is no way to get notified if the font size change to trigger a size change.
In consequence, widgets look corrupted after a font size change.

Example megawidgets are: NoteBook from BWidgets, TableList

The demanded feature is to send an event if the size of the font changes.
Example:
bind .l <FontChange> reConfigure

This would be similar to the Configure event.
User Comments: griffin added on 2021-11-04 18:26:57:

Fixed with Tip-608, [ec2e648ee4ed21cae8274e51bb54ab984cad802dbedcbce5207aa6d9600fc215]


oehhar added on 2021-08-09 06:36:35:

This is followed by TIP 608.

https://core.tcl-lang.org/tips/doc/trunk/tip/608.md


oehhar added on 2021-08-06 09:08:20:

May I refer to the corresponding ticket at BWidget including test script and success story for the solution by Brian:

https://core.tcl-lang.org/bwidget/info/acbd67752a34aae1

-----

In addition, here is the comment by Csaba also supporting this solution:

Many thanks for implementing this improvement! I have performed a few tests to see how Tablelist can make use of the new virtual event.

I find it quite useful that the event is sent not only to those components of the tablelist that are directly affected by a change in the font in question, but also to the tablelist widget itself. For example, if MyFont is a named font then I can configure the header label of the tablelist's column #0 via

    $tbl columnconfigure 0 -labelfont MyFont

This is implemented by setting the corresponding label widget's -font option to MyFont. Now, a change like

    font configure MyFont -size 12

will send the <<TkWorldChanged>> event (with %d set to "FontChanged") not only to that label, but also to the tablelist widget $tbl itself.

Even more useful is the fact that the tablelist widget will receive the event also if the named font is used by a tag of the underlying text widget. For example,

    $tbl columnconfigure 0 -font MyFont

is implemented by setting the -font option of a certain text widget tag to MyFont. Again, changing one of MyFont's attributes will send the new virtual event not only to the tablelist's body component (which is a text widget), but also to the tablelist itself.

Implementing the handling of this event will take its time, due to the many levels and components of a tablelist widget that can be affected by changes in the font properties, but the results of my first tests are quite promising.


oehhar added on 2021-08-03 09:11:00:

First, I want to thank Brian for the upper proposal. That is great and would solve Canvas-Based solutions like BWidget NoteBook Widget.

Another current use-case is tablelist.

In my own code, I execute:

$t configure -font LabelFont -labelfont LabelFont

to update the displayed metrics of Tablelist after a font size change.

Here is a comment by Csaba by private E-Mail regarding this use-case:

Regarding Tablelist, it is as mentioned by Harald (except that the relevant tablelist options are -font and -labelfont). In addition, the -font option is available at column, row, and cell levels, too. Likewise, there is also a -labelfont column configuration option.

Tablelist handles the <<ThemeChanged>> event with the aid of a rather complex procedure that adapts the widget's look at the colors, fonts, reliefs, border widths, paddings, etc. specific to the new theme. Something similar would be needed to handle a <<FontChanged>> (or <<WorldChanged>>, or whatever) virtual event.

As mentioned by Harald, NoteBook and tablelist are just two examples of the many mega-widgets that would have to be adapted to handle notifications related to changes in font properties.


griffin added on 2021-08-02 23:19:01:
I have dug into this issue more deeply.  My conclusion thus far goes as follows:

   * All Tk widgets already handle font configuration changes correctly, and do the right thing.  

   * 3rd party (C-based) widgets that perform their own rendering need to follow protocol and register an appropriate WorldChangedProc to trigger any necessary re-rendering.  

   * The Canvas widget does appropriately (re)render text upon the WorldChanged callback.  However, unlike other widgets, it does not have any virtual events that an implementation can hang on to be notified of internal changes to the canvas.  What does mean?

   * Any mega-widget that uses the canvas widget to perform the task of a layout manager is blind to any internal changes the canvas makes based on external events such as font changes, basically because the canvas widget does not produce any user visible events, virtual or otherwise, at least not in this particular case where a font simply changes it's size.

Given this situation, it appears to me that the flaw here is smaller than assumed by the earlier discussion in this ticket.  I really simple fix is to have the canvas widget generate a <<WorldChanged>> virtual event based on it's own WorldChange proc.  Other widgets don't need to do this since the whole widget layout in other cases is fully managed by the widget.  For the canvas widget, there is no internal knowledge about the relationship of objects within the canvas to each other, so when a fonts size changes, there's no way for it to adjust other dependent objects in the canvas.

With the patch below, any mega-widget author should be able to solve any font change issues:

$ fossil diff generic/tkCanvas.c
Index: generic/tkCanvas.c
==================================================================
--- generic/tkCanvas.c
+++ generic/tkCanvas.c
@@ -2419,10 +2419,13 @@
     canvasPtr->flags |= REPICK_NEEDED;
     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
     canvasPtr->xOrigin, canvasPtr->yOrigin,
     canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
     canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
+
+    /* Broadcast font change virtually for mega-widget layout managers */
+    TkSendVirtualEvent(canvasPtr->tkwin, "TkWorldChanged", NULL);
 }

nemethi added on 2010-09-08 03:18:39:
Actually, I think that what we need is a <<NamedFontChanged>> virtual event, which could be implemented and handled just like <<ThemeChanged>>.

IIRC, there are also plans (or at least proposals) for named colors support.  That would require another virtual event, called, e.g., <<NamedColorChanged>>.

I'm not sure a generic <WorldChange>> virtual event would be the right solution.

oehhar added on 2010-09-07 14:27:06:
- tcl core list communications about this feature:
-tclguy-
Following up on this previous question, it appears that actually the 
WorldChange is a push sort of event.  In tkFont.c:TheWorldHasChanged, 
you will see the one place that calls it, but getting the 
worldChangedProc of the main window of the current application and 
calling it on every descendant widget.

If we are to generate a virtual <<WorldChange>>, when would be a good 
time to do it?

1. Places that cause WorldChange generate it, e.g. 
tkFont.c:TheWorldHasChanged.

2. Each consumer with a worldChangedProc generates itself, if it wants? 
  (this would require a change to each WorldChangedProc, but would allow 
specificity)

The other option is to turn this into a special wm protocol or main 
window only style event.  However, main window might not be quite right 
as you can technically have toplevels on different displays that might 
need to react differently.
-Brian Griffin-
This is an interesting problem.  The WorldChanged event is a broadcast event.  Every widget must receive it and it cannot have any event chain or parent/child dependancies in the way.  Currently Tcl/Tk does not have any broadcast mechanism.  The "all" class is the closest thing to a broadcast, but it is subject to [break] short-circuiting.  

This may call for a new event handling protocol.  If a widget binds <<WolrdChanged>>, it will receive the event regardless of [focus] or [bindtags] behavior.  It would be nice if there where a general purpose broadcast event that had this focus-less, break-less behavior.

oehhar added on 2010-08-23 13:13:44:
- Information provided by tclguy on clt:
-1-
This would mean exporting the WorldChanged proc to a virtual event.  WorldChanged is one of the class procs introduced in Tk 8.4 for (C-based) widgets to be made aware of just such changes.  I don't think it would hard to cause a virtual event visible in Tcl to be triggered in the same low-level machinery.
-2-
> So the point is that tkFont::TheWorldHasChanged would in addition generate an event ?
WorldChanged happens already at the C level, and fonts is just what 90% 
of people care about (e.g it's the only thing tktable really watches 
this for).  As a C level per-widget thing, it would be a per-widget 
<<WorldChange>> event to watch for on (mega)widgets that care (at least, 
that's my design in head without looking at code).