diff --git a/core/enumgen.go b/core/enumgen.go index 516c40c901..32710ad503 100644 --- a/core/enumgen.go +++ b/core/enumgen.go @@ -183,11 +183,11 @@ var _sceneFlagsValues = []sceneFlags{0, 1, 2, 3, 4, 5} // sceneFlagsN is the highest valid value for type sceneFlags, plus one. const sceneFlagsN sceneFlags = 6 -var _sceneFlagsValueMap = map[string]sceneFlags{`HasShown`: 0, `Updating`: 1, `NeedsRender`: 2, `NeedsLayout`: 3, `ImageUpdated`: 4, `PrefSizing`: 5} +var _sceneFlagsValueMap = map[string]sceneFlags{`HasShown`: 0, `Updating`: 1, `NeedsRender`: 2, `NeedsLayout`: 3, `ImageUpdated`: 4, `ContentSizing`: 5} -var _sceneFlagsDescMap = map[sceneFlags]string{0: `sceneHasShown is whether this scene has been shown. This is used to ensure that [events.Show] is only sent once.`, 1: `sceneUpdating means the Scene is in the process of sceneUpdating. It is set for any kind of tree-level update. Skip any further update passes until it goes off.`, 2: `sceneNeedsRender is whether anything in the Scene needs to be re-rendered (but not necessarily the whole scene itself).`, 3: `sceneNeedsLayout is whether the Scene needs a new layout pass.`, 4: `sceneImageUpdated indicates that the Scene's image has been updated e.g., due to a render or a resize. This is reset by the global [RenderWindow] rendering pass, so it knows whether it needs to copy the image up to the GPU or not.`, 5: `scenePrefSizing means that this scene is currently doing a PrefSize computation to compute the size of the scene (for sizing window for example); affects layout size computation only for Over`} +var _sceneFlagsDescMap = map[sceneFlags]string{0: `sceneHasShown is whether this scene has been shown. This is used to ensure that [events.Show] is only sent once.`, 1: `sceneUpdating means the Scene is in the process of sceneUpdating. It is set for any kind of tree-level update. Skip any further update passes until it goes off.`, 2: `sceneNeedsRender is whether anything in the Scene needs to be re-rendered (but not necessarily the whole scene itself).`, 3: `sceneNeedsLayout is whether the Scene needs a new layout pass.`, 4: `sceneImageUpdated indicates that the Scene's image has been updated e.g., due to a render or a resize. This is reset by the global [RenderWindow] rendering pass, so it knows whether it needs to copy the image up to the GPU or not.`, 5: `sceneContentSizing means that this scene is currently doing a contentSize computation to compute the size of the scene (for sizing window for example). Affects layout size computation.`} -var _sceneFlagsMap = map[sceneFlags]string{0: `HasShown`, 1: `Updating`, 2: `NeedsRender`, 3: `NeedsLayout`, 4: `ImageUpdated`, 5: `PrefSizing`} +var _sceneFlagsMap = map[sceneFlags]string{0: `HasShown`, 1: `Updating`, 2: `NeedsRender`, 3: `NeedsLayout`, 4: `ImageUpdated`, 5: `ContentSizing`} // String returns the string representation of this sceneFlags value. func (i sceneFlags) String() string { return enums.BitFlagString(i, _sceneFlagsValues) } diff --git a/core/layout.go b/core/layout.go index ed33e5b636..a479fddc22 100644 --- a/core/layout.go +++ b/core/layout.go @@ -642,7 +642,7 @@ func (fr *Frame) laySetContentFitOverflow(nsz math32.Vector2, pass LayoutPasses) if nosz { continue } - if !(fr.Scene != nil && fr.Scene.hasFlag(scenePrefSizing)) && oflow.Dim(d) >= styles.OverflowAuto && fr.Parent != nil { + if !(fr.Scene != nil && fr.Scene.hasFlag(sceneContentSizing)) && oflow.Dim(d) >= styles.OverflowAuto && fr.Parent != nil { if mx.Dim(d) > 0 { asz.SetDim(d, styles.ClampMax(styles.ClampMin(asz.Dim(d), nsz.Dim(d)), mx.Dim(d))) } @@ -1519,7 +1519,7 @@ func (wb *WidgetBase) SizeFinal() { // any factor > 1 produces a full fill along that dimension. // Returns true if this resulted in a change in our Total size. func (wb *WidgetBase) growToAlloc() bool { - if (wb.Scene != nil && wb.Scene.hasFlag(scenePrefSizing)) || wb.Styles.GrowWrap { + if (wb.Scene != nil && wb.Scene.hasFlag(sceneContentSizing)) || wb.Styles.GrowWrap { return false } sz := &wb.Geom.Size diff --git a/core/mainstage.go b/core/mainstage.go index dcba6abbf3..4a6c5ac3bf 100644 --- a/core/mainstage.go +++ b/core/mainstage.go @@ -213,7 +213,7 @@ func (st *Stage) runWindow() *Stage { if TheApp.Platform() == system.Offscreen || (!TheApp.Platform().IsMobile() && (st.NewWindow || !st.FullWindow || currentRenderWindow == nil)) { - sz = sc.prefSize(sz) + sz = sc.contentSize(sz) // on offscreen, we don't want any extra space, as we want the smallest // possible representation of the content // also, on offscreen, if the new size is bigger than the current size, @@ -236,7 +236,7 @@ func (st *Stage) runWindow() *Stage { } else { // on other platforms, we want extra space and a minimum window size sz = sz.Add(image.Pt(20, 20)) - if st.NewWindow { + if st.NewWindow && st.UseMinSize { // we require windows to be at least 60% and no more than 80% of the // screen size by default scsz := system.TheApp.Screen(0).PixSize // TODO(kai): is there a better screen to get here? @@ -317,11 +317,13 @@ func (st *Stage) runDialog() *Stage { sz := ms.renderContext.geom.Size if !st.FullWindow || st.NewWindow { - sz = sc.prefSize(sz) + sz = sc.contentSize(sz) sz = sz.Add(image.Pt(50, 50)) - // dialogs must be at least 400dp wide by default - minx := int(ctx.Scene.Styles.UnitContext.Dp(400)) - sz.X = max(sz.X, minx) + if st.UseMinSize { + // dialogs must be at least 400dp wide by default + minx := int(ctx.Scene.Styles.UnitContext.Dp(400)) + sz.X = max(sz.X, minx) + } sc.Events.startFocusFirst = true // popup dialogs always need focus } if DebugSettings.WinRenderTrace { diff --git a/core/popupstage.go b/core/popupstage.go index 6b9c6ac981..524c25a2dd 100644 --- a/core/popupstage.go +++ b/core/popupstage.go @@ -87,7 +87,11 @@ func (st *Stage) runPopup() *Stage { sc.SceneGeom.Size = maxGeom.Size sc.SceneGeom.Pos = st.Pos - sz := sc.prefSize(maxGeom.Size) + sz := sc.contentSize(maxGeom.Size) + bigPopup := false + if usingWinGeom && 4*sz.X*sz.Y > 3*msc.SceneGeom.Size.X*msc.SceneGeom.Size.Y { // reasonable fraction + bigPopup = true + } scrollWd := int(sc.Styles.ScrollbarWidth.Dots) fontHt := 16 if sc.Styles.Font.Face != nil { @@ -124,8 +128,9 @@ func (st *Stage) runPopup() *Stage { } sc.SceneGeom.Size = sz - sc.fitInWindow(maxGeom) // does resize - if usingWinGeom { // reposition to be as close to top-right of main scene as possible + if bigPopup { // we have a big popup -- make it not cover the original window; + sc.fitInWindow(maxGeom) // does resize + // reposition to be as close to top-right of main scene as possible tpos := msc.SceneGeom.Pos tpos.X += msc.SceneGeom.Size.X if tpos.X+sc.SceneGeom.Size.X > maxGeom.Size.X { // favor left side instead @@ -145,6 +150,8 @@ func (st *Stage) runPopup() *Stage { tpos.Y = 0 } sc.SceneGeom.Pos = tpos + } else { + sc.fitInWindow(msc.SceneGeom) } sc.showIter = 0 diff --git a/core/render.go b/core/render.go index 65c228994d..f1fd677f08 100644 --- a/core/render.go +++ b/core/render.go @@ -214,7 +214,7 @@ func (sc *Scene) doUpdate() bool { if sc.showIter == sceneShowIters { // end of first pass sc.showIter++ - if !sc.hasFlag(scenePrefSizing) { + if !sc.hasFlag(sceneContentSizing) { sc.Events.activateStartFocus() } } @@ -255,20 +255,20 @@ func (sc *Scene) doRebuild() { sc.layoutRenderScene() } -// prefSize computes the preferred size of the scene based on current contents. -// initSz is the initial size -- e.g., size of screen. -// Used for auto-sizing windows. -func (sc *Scene) prefSize(initSz image.Point) image.Point { +// contentSize computes the size of the scene based on current content. +// initSz is the initial size, e.g., size of screen. +// Used for auto-sizing windows when created, and in [Scene.ResizeToContent]. +func (sc *Scene) contentSize(initSz image.Point) image.Point { sc.setFlag(true, sceneUpdating) // prevent rendering defer func() { sc.setFlag(false, sceneUpdating) }() - sc.setFlag(true, scenePrefSizing) + sc.setFlag(true, sceneContentSizing) sc.updateScene() sc.applyStyleScene() sc.layoutScene() sz := &sc.Geom.Size psz := sz.Actual.Total - sc.setFlag(false, scenePrefSizing) + sc.setFlag(false, sceneContentSizing) sc.showIter = 0 return psz.ToPointFloor() } diff --git a/core/scene.go b/core/scene.go index 3d78edb3ad..24ccb3469c 100644 --- a/core/scene.go +++ b/core/scene.go @@ -124,11 +124,10 @@ const ( // copy the image up to the GPU or not. sceneImageUpdated - // scenePrefSizing means that this scene is currently doing a - // PrefSize computation to compute the size of the scene - // (for sizing window for example); affects layout size computation - // only for Over - scenePrefSizing + // sceneContentSizing means that this scene is currently doing a + // contentSize computation to compute the size of the scene + // (for sizing window for example). Affects layout size computation. + sceneContentSizing ) // hasFlag returns whether the given flag is set. @@ -293,6 +292,29 @@ func (sc *Scene) resize(geom math32.Geom2DInt) { sc.NeedsLayout() } +// ResizeToContent resizes the scene so it fits the current content. +// Only applicable to Desktop systems where windows can be resized. +// Optional extra size is added to the amount computed to hold the contents, +// which is needed in cases with wrapped text elements, which don't +// always size accurately. +func (sc *Scene) ResizeToContent(extra ...image.Point) { + if TheApp.Platform().IsMobile() { // not resizable + return + } + win := sc.RenderWindow() + if win == nil { + return + } + go func() { + scsz := system.TheApp.Screen(0).PixSize + sz := sc.contentSize(scsz) + if len(extra) == 1 { + sz = sz.Add(extra[0]) + } + win.SystemWindow.SetSize(sz) + }() +} + // Close closes the [Stage] associated with this [Scene]. // This only works for main stages (windows and dialogs). // It returns whether the [Stage] was successfully closed. diff --git a/core/sizeclasses.go b/core/sizeclasses.go index 3d6389da45..90ad448888 100644 --- a/core/sizeclasses.go +++ b/core/sizeclasses.go @@ -27,7 +27,7 @@ const ( // in terms of dp (density-independent pixels). func (wb *WidgetBase) SceneSize() math32.Vector2 { dots := math32.FromPoint(wb.Scene.SceneGeom.Size) - if wb.Scene.hasFlag(scenePrefSizing) { + if wb.Scene.hasFlag(sceneContentSizing) { if currentRenderWindow != nil { rg := currentRenderWindow.SystemWindow.RenderGeom() dots = math32.FromPoint(rg.Size) diff --git a/core/stage.go b/core/stage.go index d6fdf285e5..85d346918f 100644 --- a/core/stage.go +++ b/core/stage.go @@ -120,6 +120,11 @@ type Stage struct { //types:add -setters // [WindowStage]s take up the entire window they are created in. FullWindow bool + // UseMinSize uses a minimum size as a function of the total available size + // for sizing new windows and dialogs. Otherwise, only the content size is used. + // The saved window position and size takes precedence on multi-window platforms. + UseMinSize bool + // Timeout, if greater than 0, results in a popup stages disappearing // after this timeout duration. Timeout time.Duration @@ -225,6 +230,7 @@ func (st *Stage) setPopups(mainSt *Stage) *Stage { // setType sets the type and also sets default parameters based on that type func (st *Stage) setType(typ StageTypes) *Stage { st.Type = typ + st.UseMinSize = true switch st.Type { case WindowStage: if !TheApp.Platform().IsMobile() { diff --git a/core/typegen.go b/core/typegen.go index e0cbc9d4eb..b396d57f43 100644 --- a/core/typegen.go +++ b/core/typegen.go @@ -779,7 +779,7 @@ func (t *Splits) SetTileSplits(v ...float32) *Splits { t.TileSplits = v; return // elements, there are 2 subsets of sub-splits, with 4 total subsplits. func (t *Splits) SetSubSplits(v ...[]float32) *Splits { t.SubSplits = v; return t } -var _ = types.AddType(&types.Type{Name: "cogentcore.org/core/core.Stage", IDName: "stage", Doc: "Stage is a container and manager for displaying a [Scene]\nin different functional ways, defined by [StageTypes].", Directives: []types.Directive{{Tool: "types", Directive: "add", Args: []string{"-setters"}}}, Fields: []types.Field{{Name: "Type", Doc: "Type is the type of [Stage], which determines behavior and styling."}, {Name: "Scene", Doc: "Scene contents of this [Stage] (what it displays)."}, {Name: "Context", Doc: "Context is a widget in another scene that requested this stage to be created\nand provides context."}, {Name: "Name", Doc: "Name is the name of the Stage, which is generally auto-set\nbased on the [Scene.Name]."}, {Name: "Title", Doc: "Title is the title of the Stage, which is generally auto-set\nbased on the [Body.Title]. It used for the title of [WindowStage]\nand [DialogStage] types, and for a [Text] title widget if\n[Stage.DisplayTitle] is true."}, {Name: "Modal", Doc: "Modal, if true, blocks input to all other stages."}, {Name: "Scrim", Doc: "Scrim, if true, places a darkening scrim over other stages."}, {Name: "ClickOff", Doc: "ClickOff, if true, dismisses the [Stage] if the user clicks anywhere\noff of the [Stage]."}, {Name: "ignoreEvents", Doc: "ignoreEvents is whether to send no events to the stage and\njust pass them down to lower stages."}, {Name: "NewWindow", Doc: "NewWindow, if true, opens a [WindowStage] or [DialogStage] in its own\nseparate operating system window ([renderWindow]). This is true by\ndefault for [WindowStage] on non-mobile platforms, otherwise false."}, {Name: "FullWindow", Doc: "FullWindow, if [Stage.NewWindow] is false, makes [DialogStage]s and\n[WindowStage]s take up the entire window they are created in."}, {Name: "Timeout", Doc: "Timeout, if greater than 0, results in a popup stages disappearing\nafter this timeout duration."}, {Name: "BackButton", Doc: "BackButton is whether to add a back button to the top bar that calls\n[Scene.Close] when clicked. If it is unset, is will be treated as true\non non-[system.Offscreen] platforms for [Stage.FullWindow] but not\n[Stage.NewWindow] [Stage]s that are not the first in the stack."}, {Name: "DisplayTitle", Doc: "DisplayTitle is whether to display the [Stage.Title] using a\n[Text] widget in the top bar. It is on by default for [DialogStage]s\nand off for all other stages."}, {Name: "Pos", Doc: "Pos is the target position for the [Stage] to be placed within\nthe surrounding window."}, {Name: "Main", Doc: "If a popup stage, this is the main stage that owns it (via its [Stage.popups]).\nIf a main stage, it points to itself."}, {Name: "popups", Doc: "For main stages, this is the stack of the popups within it\n(created specifically for the main stage).\nFor popups, this is the pointer to the popups within the\nmain stage managing it."}, {Name: "Mains", Doc: "For all stages, this is the main [Stages] that lives in a [renderWindow]\nand manages the main stages."}, {Name: "renderContext", Doc: "rendering context which has info about the RenderWindow onto which we render.\nThis should be used instead of the RenderWindow itself for all relevant\nrendering information. This is only available once a Stage is Run,\nand must always be checked for nil."}, {Name: "Sprites", Doc: "Sprites are named images that are rendered last overlaying everything else."}}}) +var _ = types.AddType(&types.Type{Name: "cogentcore.org/core/core.Stage", IDName: "stage", Doc: "Stage is a container and manager for displaying a [Scene]\nin different functional ways, defined by [StageTypes].", Directives: []types.Directive{{Tool: "types", Directive: "add", Args: []string{"-setters"}}}, Fields: []types.Field{{Name: "Type", Doc: "Type is the type of [Stage], which determines behavior and styling."}, {Name: "Scene", Doc: "Scene contents of this [Stage] (what it displays)."}, {Name: "Context", Doc: "Context is a widget in another scene that requested this stage to be created\nand provides context."}, {Name: "Name", Doc: "Name is the name of the Stage, which is generally auto-set\nbased on the [Scene.Name]."}, {Name: "Title", Doc: "Title is the title of the Stage, which is generally auto-set\nbased on the [Body.Title]. It used for the title of [WindowStage]\nand [DialogStage] types, and for a [Text] title widget if\n[Stage.DisplayTitle] is true."}, {Name: "Modal", Doc: "Modal, if true, blocks input to all other stages."}, {Name: "Scrim", Doc: "Scrim, if true, places a darkening scrim over other stages."}, {Name: "ClickOff", Doc: "ClickOff, if true, dismisses the [Stage] if the user clicks anywhere\noff of the [Stage]."}, {Name: "ignoreEvents", Doc: "ignoreEvents is whether to send no events to the stage and\njust pass them down to lower stages."}, {Name: "NewWindow", Doc: "NewWindow, if true, opens a [WindowStage] or [DialogStage] in its own\nseparate operating system window ([renderWindow]). This is true by\ndefault for [WindowStage] on non-mobile platforms, otherwise false."}, {Name: "FullWindow", Doc: "FullWindow, if [Stage.NewWindow] is false, makes [DialogStage]s and\n[WindowStage]s take up the entire window they are created in."}, {Name: "UseMinSize", Doc: "UseMinSize uses a minimum size as a function of the total available size\nfor sizing new windows and dialogs. Otherwise, only the content size is used.\nThe saved window position and size takes precedence on multi-window platforms."}, {Name: "Timeout", Doc: "Timeout, if greater than 0, results in a popup stages disappearing\nafter this timeout duration."}, {Name: "BackButton", Doc: "BackButton is whether to add a back button to the top bar that calls\n[Scene.Close] when clicked. If it is unset, is will be treated as true\non non-[system.Offscreen] platforms for [Stage.FullWindow] but not\n[Stage.NewWindow] [Stage]s that are not the first in the stack."}, {Name: "DisplayTitle", Doc: "DisplayTitle is whether to display the [Stage.Title] using a\n[Text] widget in the top bar. It is on by default for [DialogStage]s\nand off for all other stages."}, {Name: "Pos", Doc: "Pos is the target position for the [Stage] to be placed within\nthe surrounding window."}, {Name: "Main", Doc: "If a popup stage, this is the main stage that owns it (via its [Stage.popups]).\nIf a main stage, it points to itself."}, {Name: "popups", Doc: "For main stages, this is the stack of the popups within it\n(created specifically for the main stage).\nFor popups, this is the pointer to the popups within the\nmain stage managing it."}, {Name: "Mains", Doc: "For all stages, this is the main [Stages] that lives in a [renderWindow]\nand manages the main stages."}, {Name: "renderContext", Doc: "rendering context which has info about the RenderWindow onto which we render.\nThis should be used instead of the RenderWindow itself for all relevant\nrendering information. This is only available once a Stage is Run,\nand must always be checked for nil."}, {Name: "Sprites", Doc: "Sprites are named images that are rendered last overlaying everything else."}}}) // SetContext sets the [Stage.Context]: // Context is a widget in another scene that requested this stage to be created @@ -818,6 +818,12 @@ func (t *Stage) SetNewWindow(v bool) *Stage { t.NewWindow = v; return t } // [WindowStage]s take up the entire window they are created in. func (t *Stage) SetFullWindow(v bool) *Stage { t.FullWindow = v; return t } +// SetUseMinSize sets the [Stage.UseMinSize]: +// UseMinSize uses a minimum size as a function of the total available size +// for sizing new windows and dialogs. Otherwise, only the content size is used. +// The saved window position and size takes precedence on multi-window platforms. +func (t *Stage) SetUseMinSize(v bool) *Stage { t.UseMinSize = v; return t } + // SetTimeout sets the [Stage.Timeout]: // Timeout, if greater than 0, results in a popup stages disappearing // after this timeout duration. @@ -978,7 +984,7 @@ func (t *Text) SetText(v string) *Text { t.Text = v; return t } // It defaults to [TextBodyLarge]. func (t *Text) SetType(v TextTypes) *Text { t.Type = v; return t } -var _ = types.AddType(&types.Type{Name: "cogentcore.org/core/core.TextField", IDName: "text-field", Doc: "TextField is a widget for editing a line of text.\n\nWith the default [styles.WhiteSpaceNormal] setting,\ntext will wrap onto multiple lines as needed. You can\ncall [styles.Style.SetTextWrap](false) to force everything\nto be rendered on a single line. With multi-line wrapped text,\nthe text is still treated as a single contiguous line of wrapped text.", Directives: []types.Directive{{Tool: "core", Directive: "embedder"}}, Methods: []types.Method{{Name: "cut", Doc: "cut cuts any selected text and adds it to the clipboard.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "copy", Doc: "copy copies any selected text to the clipboard.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "paste", Doc: "paste inserts text from the clipboard at current cursor position; if\ncursor is within a current selection, that selection is replaced.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}}, Embeds: []types.Field{{Name: "Frame"}}, Fields: []types.Field{{Name: "Type", Doc: "Type is the styling type of the text field."}, {Name: "Placeholder", Doc: "Placeholder is the text that is displayed\nwhen the text field is empty."}, {Name: "Validator", Doc: "Validator is a function used to validate the input\nof the text field. If it returns a non-nil error,\nthen an error color, icon, and tooltip will be displayed."}, {Name: "LeadingIcon", Doc: "LeadingIcon, if specified, indicates to add a button\nat the start of the text field with this icon.\nSee [TextField.SetLeadingIcon]."}, {Name: "LeadingIconOnClick", Doc: "LeadingIconOnClick, if specified, is the function to call when\nthe LeadingIcon is clicked. If this is nil, the leading icon\nwill not be interactive. See [TextField.SetLeadingIcon]."}, {Name: "TrailingIcon", Doc: "TrailingIcon, if specified, indicates to add a button\nat the end of the text field with this icon.\nSee [TextField.SetTrailingIcon]."}, {Name: "TrailingIconOnClick", Doc: "TrailingIconOnClick, if specified, is the function to call when\nthe TrailingIcon is clicked. If this is nil, the trailing icon\nwill not be interactive. See [TextField.SetTrailingIcon]."}, {Name: "NoEcho", Doc: "NoEcho is whether replace displayed characters with bullets\nto conceal text (for example, for a password input). Also\nsee [TextField.SetTypePassword]."}, {Name: "CursorWidth", Doc: "CursorWidth is the width of the text field cursor.\nIt should be set in a Styler like all other style properties.\nBy default, it is 1dp."}, {Name: "CursorColor", Doc: "CursorColor is the color used for the text field cursor (caret).\nIt should be set in a Styler like all other style properties.\nBy default, it is [colors.Scheme.Primary.Base]."}, {Name: "PlaceholderColor", Doc: "PlaceholderColor is the color used for the [TextField.Placeholder] text.\nIt should be set in a Styler like all other style properties.\nBy default, it is [colors.Scheme.OnSurfaceVariant]."}, {Name: "SelectColor", Doc: "SelectColor is the color used for the text selection background color.\nIt should be set in a Styler like all other style properties.\nBy default, it is [colors.Scheme.Select.Container]."}, {Name: "complete", Doc: "complete contains functions and data for text field completion.\nIt must be set using [TextField.SetCompleter]."}, {Name: "text", Doc: "text is the last saved value of the text string being edited."}, {Name: "edited", Doc: "edited is whether the text has been edited relative to the original."}, {Name: "editText", Doc: "editText is the live text string being edited, with the latest modifications."}, {Name: "error", Doc: "error is the current validation error of the text field."}, {Name: "effPos", Doc: "effPos is the effective position with any leading icon space added."}, {Name: "effSize", Doc: "effSize is the effective size, subtracting any leading and trailing icon space."}, {Name: "startPos", Doc: "startPos is the starting display position in the string."}, {Name: "endPos", Doc: "endPos is the ending display position in the string."}, {Name: "cursorPos", Doc: "cursorPos is the current cursor position."}, {Name: "cursorLine", Doc: "cursorLine is the current cursor line position."}, {Name: "charWidth", Doc: "charWidth is the approximate number of chars that can be\ndisplayed at any time, which is computed from the font size."}, {Name: "selectStart", Doc: "selectStart is the starting position of selection in the string."}, {Name: "selectEnd", Doc: "selectEnd is the ending position of selection in the string."}, {Name: "selectInit", Doc: "selectInit is the initial selection position (where it started)."}, {Name: "selectMode", Doc: "selectMode is whether to select text as the cursor moves."}, {Name: "renderAll", Doc: "renderAll is the render version of entire text, for sizing."}, {Name: "renderVisible", Doc: "renderVisible is the render version of just the visible text."}, {Name: "numLines", Doc: "number of lines from last render update, for word-wrap version"}, {Name: "fontHeight", Doc: "fontHeight is the font height cached during styling."}, {Name: "blinkOn", Doc: "blinkOn oscillates between on and off for blinking."}, {Name: "cursorMu", Doc: "cursorMu is the mutex for updating the cursor between blinker and field."}, {Name: "undos", Doc: "undos is the undo manager for the text field."}, {Name: "leadingIconButton"}, {Name: "trailingIconButton"}}}) +var _ = types.AddType(&types.Type{Name: "cogentcore.org/core/core.TextField", IDName: "text-field", Doc: "TextField is a widget for editing a line of text.\n\nWith the default [styles.WhiteSpaceNormal] setting,\ntext will wrap onto multiple lines as needed. You can\ncall [styles.Style.SetTextWrap](false) to force everything\nto be rendered on a single line. With multi-line wrapped text,\nthe text is still treated as a single contiguous line of wrapped text.", Directives: []types.Directive{{Tool: "core", Directive: "embedder"}}, Methods: []types.Method{{Name: "cut", Doc: "cut cuts any selected text and adds it to the clipboard.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "copy", Doc: "copy copies any selected text to the clipboard.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "paste", Doc: "paste inserts text from the clipboard at current cursor position; if\ncursor is within a current selection, that selection is replaced.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}}, Embeds: []types.Field{{Name: "Frame"}}, Fields: []types.Field{{Name: "Type", Doc: "Type is the styling type of the text field."}, {Name: "Placeholder", Doc: "Placeholder is the text that is displayed\nwhen the text field is empty."}, {Name: "Validator", Doc: "Validator is a function used to validate the input\nof the text field. If it returns a non-nil error,\nthen an error color, icon, and tooltip will be displayed."}, {Name: "LeadingIcon", Doc: "LeadingIcon, if specified, indicates to add a button\nat the start of the text field with this icon.\nSee [TextField.SetLeadingIcon]."}, {Name: "LeadingIconOnClick", Doc: "LeadingIconOnClick, if specified, is the function to call when\nthe LeadingIcon is clicked. If this is nil, the leading icon\nwill not be interactive. See [TextField.SetLeadingIcon]."}, {Name: "TrailingIcon", Doc: "TrailingIcon, if specified, indicates to add a button\nat the end of the text field with this icon.\nSee [TextField.SetTrailingIcon]."}, {Name: "TrailingIconOnClick", Doc: "TrailingIconOnClick, if specified, is the function to call when\nthe TrailingIcon is clicked. If this is nil, the trailing icon\nwill not be interactive. See [TextField.SetTrailingIcon]."}, {Name: "NoEcho", Doc: "NoEcho is whether replace displayed characters with bullets\nto conceal text (for example, for a password input). Also\nsee [TextField.SetTypePassword]."}, {Name: "CursorWidth", Doc: "CursorWidth is the width of the text field cursor.\nIt should be set in a Styler like all other style properties.\nBy default, it is 1dp."}, {Name: "CursorColor", Doc: "CursorColor is the color used for the text field cursor (caret).\nIt should be set in a Styler like all other style properties.\nBy default, it is [colors.Scheme.Primary.Base]."}, {Name: "PlaceholderColor", Doc: "PlaceholderColor is the color used for the [TextField.Placeholder] text.\nIt should be set in a Styler like all other style properties.\nBy default, it is [colors.Scheme.OnSurfaceVariant]."}, {Name: "SelectColor", Doc: "SelectColor is the color used for the text selection background color.\nIt should be set in a Styler like all other style properties.\nBy default, it is [colors.Scheme.Select.Container]."}, {Name: "complete", Doc: "complete contains functions and data for text field completion.\nIt must be set using [TextField.SetCompleter]."}, {Name: "text", Doc: "text is the last saved value of the text string being edited."}, {Name: "edited", Doc: "edited is whether the text has been edited relative to the original."}, {Name: "editText", Doc: "editText is the live text string being edited, with the latest modifications."}, {Name: "error", Doc: "error is the current validation error of the text field."}, {Name: "effPos", Doc: "effPos is the effective position with any leading icon space added."}, {Name: "effSize", Doc: "effSize is the effective size, subtracting any leading and trailing icon space."}, {Name: "startPos", Doc: "startPos is the starting display position in the string."}, {Name: "endPos", Doc: "endPos is the ending display position in the string."}, {Name: "cursorPos", Doc: "cursorPos is the current cursor position."}, {Name: "cursorLine", Doc: "cursorLine is the current cursor line position."}, {Name: "charWidth", Doc: "charWidth is the approximate number of chars that can be\ndisplayed at any time, which is computed from the font size."}, {Name: "selectStart", Doc: "selectStart is the starting position of selection in the string."}, {Name: "selectEnd", Doc: "selectEnd is the ending position of selection in the string."}, {Name: "selectInit", Doc: "selectInit is the initial selection position (where it started)."}, {Name: "selectMode", Doc: "selectMode is whether to select text as the cursor moves."}, {Name: "selectModeShift", Doc: "selectModeShift is whether selectmode was turned on because of the shift key."}, {Name: "renderAll", Doc: "renderAll is the render version of entire text, for sizing."}, {Name: "renderVisible", Doc: "renderVisible is the render version of just the visible text."}, {Name: "numLines", Doc: "number of lines from last render update, for word-wrap version"}, {Name: "fontHeight", Doc: "fontHeight is the font height cached during styling."}, {Name: "blinkOn", Doc: "blinkOn oscillates between on and off for blinking."}, {Name: "cursorMu", Doc: "cursorMu is the mutex for updating the cursor between blinker and field."}, {Name: "undos", Doc: "undos is the undo manager for the text field."}, {Name: "leadingIconButton"}, {Name: "trailingIconButton"}}}) // NewTextField returns a new [TextField] with the given optional parent: // TextField is a widget for editing a line of text. diff --git a/examples/demo/demo.go b/examples/demo/demo.go index 373a5ff8a3..dd77f87506 100644 --- a/examples/demo/demo.go +++ b/examples/demo/demo.go @@ -9,6 +9,7 @@ package main import ( "embed" "fmt" + "image" "strconv" "strings" "time" @@ -635,6 +636,12 @@ func dialogs(ts *core.Tabs) { core.NewText(d).SetType(core.TextSupporting).SetText("A standalone window that opens in the same system window") d.NewWindow().SetNewWindow(false).SetDisplayTitle(true).Run() }) + + rw := core.NewButton(wrow).SetText("Resize to content") + rw.SetTooltip("Resizes this window to fit the current content") + rw.OnClick(func(e events.Event) { + wrow.Scene.ResizeToContent(image.Pt(0, 40)) // note: size is not correct due to wrapping? #1307 + }) } func makeStyles(ts *core.Tabs) { diff --git a/system/driver/desktop/window.go b/system/driver/desktop/window.go index a2bf29adfd..417c2ef391 100644 --- a/system/driver/desktop/window.go +++ b/system/driver/desktop/window.go @@ -223,6 +223,12 @@ func (w *Window) SetWinSize(sz image.Point) { }) } +func (w *Window) SetSize(sz image.Point) { + sc := w.Screen() + sz = sc.WinSizeFromPix(sz) + w.SetWinSize(sz) +} + func (w *Window) SetPos(pos image.Point) { if w.IsClosed() || w.Is(system.Fullscreen) { return