cogentcore / core

A free and open source framework for building powerful, fast, elegant 2D and 3D apps that run on macOS, Windows, Linux, iOS, Android, and the web with a single Go codebase, allowing you to Code Once, Run Everywhere.
http://cogentcore.org/core
BSD 3-Clause "New" or "Revised" License
1.75k stars 83 forks source link

Support a growing window #1298

Open bqback opened 4 days ago

bqback commented 4 days ago

Describe the bug

Providing GrowWrap = true to a new body makes it unable to display any content. Settings are taken directly from https://www.cogentcore.org/core/advanced/styling/.

Expected behavior: the windows contains a text widget with "Picker" and a picker with options "foo" and "bar". The window wraps tightly around contents of the child frame.

Result: a black window appears with a 0,0 box result

How to reproduce

core run the code in the issue

Example code

package main

import (
    "fmt"

    "cogentcore.org/core/core"
    "cogentcore.org/core/events"
    "cogentcore.org/core/styles"
    "cogentcore.org/core/styles/units"
)

func main() {
    b := core.NewBody()
    b.Styler(func(s *styles.Style) {
        s.GrowWrap = true
        s.Gap.X = units.Value{Value: 0.5, Unit: units.UnitEm}
        s.Gap.Y = units.Value{Value: 0.5, Unit: units.UnitEm}
    })

    fr := core.NewFrame(b)
    fr.Styler(func(s *styles.Style) {
        s.Align = styles.AlignSet{Items: styles.Center}
    })

    t := core.NewText(fr).SetText("Picker")
    t.Styler(func(s *styles.Style) {
        s.SetTextWrap(false)
    })
    ch := core.NewChooser(fr).SetStrings("foo", "bar")
    ch.OnChange(func(e events.Event) {
        core.MessageSnackbar(fr, fmt.Sprintf("Selected %v", ch.CurrentItem.Value))
    })

    b.RunMainWindow()
}

Relevant output

No response

Platform

Windows

bqback commented 4 days ago

I've also tried

with no success.

kkoreilly commented 3 days ago

Thank you for reporting this. GrowWrap is only intended for text elements, so it does not work to set that on the Body. You can try setting Wrap and/or Grow separately depending on what you are trying to accomplish; I can help you with the appropriate styling options once you tell me your use case. Thank you!

rcoreilly commented 3 days ago

we should probably make sure GrowWrap is properly ignored where it is irrelevant.

bqback commented 2 days ago

I can help you with the appropriate styling options once you tell me your use case.

The full usecase would be a sort of "growing" app window with the scenario being:

I'd like for the window to be automatically resized to wrap the visible frames up to step 3. I can vaguely predict the size and resize manually when frames are enabled/disabled, but that seems quite ineffective.

rcoreilly commented 1 day ago

See #1303 for discussion of why we cannot easily prevent the mistaken use of GrowWrap.

It should be relatively straightforward to add a ResizeToContents function that you can call whenever you want. Currently we have a "PrefSize" function (which we will rename) that is used when a new window is opened, but there is no reason why it cannot be called to get a size that should hold a given Scene's contents.

kkoreilly commented 1 day ago

Yes, so once we add the ResizeToContents function soon, you will be able to call it at each of those stages you mentioned to get the window to grow. Until then, you can just have the window at a constant size without any GrowWrap. We will let you know once we implement the ResizeToContents function.

bqback commented 1 day ago

Thank you! One more thing to look forward to in 0.4 (another is localized time picker formatting).

Last question that's only tangentially related (because I mentioned text fields somewhere in the thread) -- how exactly is text field's validate() supposed to work? I understand how the validator is assigned, and when and how it's ran, but it seems to me like no values are passed to Validator(). It's just a function that returns an error, but what is it supposed work with?

I think the only option that would make sense to me is explicitly defining my validator as a bound method func (tf *TextField) Validator() error to have access to tf.text inside the method, but that doesn't feel right.

bqback commented 1 day ago

Could it possibly have been intended as a func(string) error function, with tf.validate() calling it as tf.Validator(tf.text)?

bqback commented 1 day ago

Could it possibly have been intended as a func(string) error function, with tf.validate() calling it as tf.Validator(tf.text)?

Nvm, I've taken a look at tests for the validator, and it has to be defined in-place as an anonymous function. Don't really have any other questions, then.

kkoreilly commented 13 hours ago

Yes, it is intended to be defined as an anonymous closure that calls tf.Text() or any other functions to get relevant attributes. That structure allows it to be more generically extensible. If you want, you could always define another function that just takes the text field or text string directly and call that function in the anonymous closure.