Fl_Input Justification

35 views
Skip to first unread message

Rob McDonald

unread,
Mar 6, 2023, 1:19:28 PM3/6/23
to fltkg...@googlegroups.com
I have an FL_Input that sometimes needs to display a text field that is wider than the widget.  Can I control which 'side' of the text is shown?

Let's say our Fl_Input is wide enough to show 5 characters -- and that our string is 123.456.

Currently, the Fl_Input will display "3.456" while I would prefer that it display "123.4".

I am aware of the things you can set via _Fl_Input::type() -- but there does not seem to be anything relevant there.

Any suggestions?

Rob

(BTW, I normally post through the Google Groups web interface.  I have been a group member for years, but not subscribed via email.  This is my fourth attempt to post this message.  If they're in some moderator's queue or if this otherwise shows up many times, I apologize for the duplication.)

Greg Ercolano

unread,
Mar 6, 2023, 1:29:56 PM3/6/23
to fltkg...@googlegroups.com


On 3/6/23 10:19, Rob McDonald wrote:
I have an FL_Input that sometimes needs to display a text field that is wider than the widget.  Can I control which 'side' of the text is shown?

Let's say our Fl_Input is wide enough to show 5 characters -- and that our string is 123.456.

Currently, the Fl_Input will display "3.456" while I would prefer that it display "123.4".

I am aware of the things you can set via _Fl_Input::type() -- but there does not seem to be anything relevant there.

Any suggestions?


    You probably just need to change the cursor position to 0 after pre-setting the field's value,
    and after the user finishes entering a value (when they hit ENTER or when they navigate
    off the field).

    The docs say Fl_Input_::position(int p) is how to set the cursor position, so try
    setting that to 0 after changing the value(), and ditto for the callback, assuming
    you set when() to a value appropriate for your widget so the callback is invoked
    at the right times.

    You can also preselect the text, so if someone navigates to the field and starts
    typing a new value, since it's preselected, typing the first character replaces the
    old value (instead of inserting). The user has the option to deselect first.

Rob McDonald

unread,
Mar 6, 2023, 2:12:26 PM3/6/23
to fltk.general
Thanks for that suggestion.  It does cause the display to change -- while moving the text entry point to the other end as well.

Is it possible to scroll the display without moving the text entry point?  Even if that means the text entry point is not visible when not editing?

Most of my Fl_Input fields are numeric.  However, they are treated as strings.  I have a 'smart edit' feature implemented where users can enter simple math expressions and they get computed on the value.

So, for example, lets say a field contains "7".  The cursor is after the number.  If the user then types "*4", the field will read "7*4" and when they press enter, I will parse the string, do the math, and set the value and field to read "28".

When one field is 'active', one can easily make various modifications very quickly.  "*2 enter +4 enter /6 enter" works well.  I would like to keep that capability and not make the user navigate to the end of their field at every step.

Thanks,

Rob

Bill Spitzak

unread,
Mar 6, 2023, 2:16:08 PM3/6/23
to fltkg...@googlegroups.com
If you want it to act like all the text is selected but scrolled right, try putting the mark at the end and the cursor at the start.

--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/0138d475-de14-44d8-be22-0ddaf269a0acn%40googlegroups.com.

Rob McDonald

unread,
Mar 6, 2023, 2:27:54 PM3/6/23
to fltkg...@googlegroups.com
That isn't quite right either.  That highlights the whole field -- so any entry (other than arrows) replaces the whole field instead of adds on to the end.

In addition, when you navigate (say by pressing the right arrow key), your initial position is that of position(), not mark(), so you're at the wrong end of the field.

Where/how does a Widget render text?  A Fl_Button with a label that is too-long justifies the 'other' way.  I am fine subclassing Fl_Input to make this work the way I'd like, but I'm not sure where to start.

Rob


You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/q3C4-P9v-7s/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/CAL-8oAhDMhi5RH6NsuUJ4%3Dc_pHE%3DT5%3D-HMPAg-b3Xt0Wq-5Keg%40mail.gmail.com.
Message has been deleted
Message has been deleted
Message has been deleted

Rob McDonald

unread,
Mar 13, 2023, 12:26:13 PM3/13/23
to fltk.general
On Monday, March 6, 2023 at 11:27:54 AM UTC-8 Rob McDonald wrote:
Where/how does a Widget render text?  A Fl_Button with a label that is too-long justifies the 'other' way.  I am fine subclassing Fl_Input to make this work the way I'd like, but I'm not sure where to start.

OK, I spent some time hacking around.  I made some general progress.

I had to modify Fl_Input_::drawtext() -- which is private, so not intended to be overridden by subclassing.  Duplicating everything it does (and what calls it) basically will amount to adding a new widget.  I also subclassed it to modify the behavior of handle().

The change to drawtext() is:

       if (newscroll < 0) newscroll = 0;
       if (newscroll != xscroll_) {
-        xscroll_ = newscroll;
+        if (Fl::focus()==this)
+          xscroll_ = newscroll;
         mu_p = 0; erase_cursor_only = 0;
       }

Which essentially ignores the 'newscroll' calculation when !focus().

I also had to implement Fl_Input_::xscroll(int) as a duplicate of what yscroll(int) does.

My over-ridden handle() is:

int MyInput::handle(int event)
{
    if ( event == FL_UNFOCUS )
        xscroll( 0 ); // also calls damage().
    return Fl_Input::handle( event );
}

Which forces xscroll to zero whenever focus is lost (and marks damage).

This largely behaves as I'd like.  That is:
1) The Input scrolls to the point of editing when working with an entry, keyboard navigation, selection, etc. all seem to work as normal.
2) When focus is lost, the Input scrolls to a point where the start of the field is always shown (and the end is clipped).
3) When clicking back on the Input, the editing position works as expected.

Unfortunately, there is one issue I've noticed so far:
A) When leaving focus, the cursor line is not erased.  Clicking back and forth between two Inputs leaves them a mess of dead cursors.

There could well be other issues -- Fl_Input_::drawtext() is a lot more complex than you might first expect.

I'm looking for go-forward advice.

Since changes to a private method of Fl_Input_ are required, this is not something I can do as a slight modification by overriding handle() or other public methods.  Would changes to make this an optional behavior change in mainline FLTK be accepted?

If not, is the recommended path to copy/paste all the Fl_Input related files (two source and two headers) into my project and use them to concoct a custom Widget?  Is there some other alternative?

Any suggestions on fixing the cursor erasure issue.

Thanks in advance,

Rob

Bill Spitzak

unread,
Mar 13, 2023, 1:10:42 PM3/13/23
to fltkg...@googlegroups.com
I would try moving the cursor to the start when you lose focus

--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.

Rob McDonald

unread,
Mar 13, 2023, 9:09:23 PM3/13/23
to fltk.general
On Monday, March 13, 2023 at 10:10:42 AM UTC-7 spitzak wrote:
I would try moving the cursor to the start when you lose focus

Thanks for that suggestion.  This gets me to basically what I had achieved -- but much more simply.

Unfortunately, the cursor is still not erased properly.

I will have to work to see if I can construct an example that others can try.

Thanks,

rob


 

Rob McDonald

unread,
Mar 14, 2023, 1:27:02 AM3/14/23
to fltkg...@googlegroups.com
I will have to work to see if I can construct an example that others can try.


OK, here (attached) is an example based on one of Erco's cheat sheet examples...

I've modified it to call position(0) at the places required to get the behavior I'm after -- but it results in the non-erasing cursor.

Screen Shot 2023-03-13 at 10.21.31 PM.png
Any help is appreciated.

This is with the latest tip of master on MacOS.

Rob

 
mwe.cpp

Greg Ercolano

unread,
Mar 14, 2023, 1:32:09 AM3/14/23
to fltkg...@googlegroups.com

On 3/13/23 22:26, Rob McDonald wrote:

I will have to work to see if I can construct an example that others can try.


OK, here (attached) is an example based on one of Erco's cheat sheet examples...
I've modified it to call position(0) at the places required to get the behavior I'm after -- but it results in the non-erasing cursor.


    I may have missed part of this thread; calling redraw() on the widget after calling
    position() should fix this, as it "should" mark the entire input widget damage()ed
    and force it to redraw everything in it completely.


Rob McDonald

unread,
Mar 14, 2023, 1:36:14 AM3/14/23
to fltkg...@googlegroups.com
I am calling position() in two places -- in the handle() and after setting the value() when the 'other' widget's callback is activated.

Should I call redraw() after both?

The cursors accumulate when I switch focus back and forth between the two Input boxes -- without changing values or moving the slider.  Just clicking one then the other.

From reading the Fl_Input drawtext() code, it has a minimal update code path that tries to not redraw everything, but just tries to erase the cursor.  I believe the position() is messing up that math.

Rob

 

Rob McDonald

unread,
Mar 14, 2023, 1:37:54 AM3/14/23
to fltkg...@googlegroups.com
On Mon, Mar 13, 2023 at 10:36 PM Rob McDonald <rob.a.m...@gmail.com> wrote:
I am calling position() in two places -- in the handle() and after setting the value() when the 'other' widget's callback is activated.

Should I call redraw() after both?

OK, I should have just tried it.

Calling redraw() after the position() in the handle(int) seems to do the trick.

Is this a 'clean' fix?  It seems a bit messy to me...

Rob

 

Rob McDonald

unread,
Mar 14, 2023, 1:45:21 AM3/14/23
to fltkg...@googlegroups.com
Unfortunately, this brings back some un-desired behavior in my application, but that does not show up in my MWE based on Erco's example.

In Erco's example, if you type a number into the Input field and press enter, the entire value box is highlighted -- such that if you immediately start typing another number, the prior value is cleared.

In my program, when you type in a number and press enter, the cursor stays at the end of the number and nothing is highlighted.  If you immediately start typing, you can either navigate the button, or type 'after' the value.  This is important for us.  This was the way my program worked until I added the redraw().

Now, when you type in a number and press enter, the cursor moves to position(0) -- even though focus has not been lost (I thought).  This means that if you start typing, you now are entering 'before' the value.

For the record, my 'real' program is implemented with Fl_Input (not Fl_Float_Input as in the posted example) and I keep track of my value in a separate field.

Rob




 

Greg Ercolano

unread,
Mar 14, 2023, 2:14:06 AM3/14/23
to fltkg...@googlegroups.com

    Calling input->redraw() seems like the right thing to do to me.
    The equivalent would be input->damage(FL_DAMAGE_ALL);

    Doing either one of those seems like the right thing to do in this case.

    If you wanted to be super efficient, you could check if position()
    is already at the value you're setting to avoid forcing a redraw
    unnecessarily, but that's about as far as I'd go to optimizing
    at the widget hierarchy level you're working at.

Patrick Madueke

unread,
Mar 14, 2023, 2:59:09 PM3/14/23
to fltkg...@googlegroups.com
1. Remove input->when(...); entirely! 💀
2. Create a very basic example, where you:
    a. Subclass Fl_Input.
    b. Override handle(...);
    c. if (event == FL_KEYDOWN) {
        if (Fl::event_key() == FL_Enter) {
            // Ave Maria. 👏
        }
    }
3. Have your handle return Fl_Input::handle(event);
4. I hope I understood the emails.

--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages