Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 9 из 25 ~18 мин +50 XP

Текст и ввод данных

Entry из третьего урока казался простым: поле, строка, OnChanged. Сегодня заглядываем глубже: запрос «fyne entry» тянет за собой целый пласт — многострочный ввод с переносами и прокруткой, разницу OnChanged и OnSubmitted, парольное поле, а по соседству живут RichText со стилями сегментов и TextGrid для моноширинных логов. Ввод текста fyne — это про то, как сделать поле ввода удобным, и про то, как показывать текст, который редактировать нельзя, но нужно красиво отформатировать. Плюс гигиена: правило сеттеров, дросселирование тяжёлых обработчиков и честные подсказки пользователю.

Содержание урока · 9 разделов

Третий урок дал тебе widget.NewEntry: поле Text, PlaceHolder для подсказки, OnChanged на каждое нажатие. Теперь — то, что отличает уверенную работу от случайной. Во-первых, правило сеттеров: текст меняй через SetText — он обновит поле и сам вызовет Refresh; присвоение entry.Text = «строка» меняет данные, но экран узнает об этом только при случайной перерисовке, например после растягивания окна. Прямое присвоение имеет смысл лишь при массовой инициализации, когда Refresh всё равно зовёшь сам. Во-вторых, Disable и Enable: заблокированное поле серо и не принимает фокус — это честнее скрытого поля, пользователь видит, что данные есть, но менять их нельзя. В-третьих, пароль: widget.NewPasswordEntry возвращает тот же Entry с точками вместо символов — больше никаких отличий, все обработчики работают одинаково. И в-четвёртых: Entry — виджет с фокусом из шестого урока: он сам ловит TypedRune, двигает курсор, держит выделение и буфер обмена. Твоя работа — не вмешаться в механику, а правильно повесить колбэки: запрос «fyne entry» в поиске почти всегда про одну из этих мелочей.

Чем OnSubmitted отличается от OnChanged?

Многострочный Entry: перенос, высота, Enter#

Многострочный режим включается вызовом widget.NewMultiLineEntry — или полем MultiLine = true у обычного Entry. Дальше три настройки, которые превращают поле в удобную область редактирования. Wrapping задаёт перенос строк структурой fyne.TextWrap: флаги Word и Break означают «переноси по словам, а при нехватке места и посимвольно»; без wrapping длинная строка уедет за край поля. SetMinRowsVisible(n) резервирует высоту под n строк: поле из одного бледного ряда становится полноценным редактором, и окно не дёргается по мере ввода. Прокрутка включается сама, когда текст перестаёт помещаться — для совсем длинных текстов поле дополнительно оборачивают в container.NewScroll. Поведение Enter заслуживает отдельной строки: в однострочном поле Enter вызывает OnSubmitted; в многострочном Enter вставляет перевод строки, а «отправить» — Shift+Enter. Это частый источник неожиданностей: пользователь чата жмёт Enter, ожидая отправку, и получает новую строку. Подсказывай поведение плейсхолдером или подписью под полем — интерфейс не должен молчать о таких вещах.

Редактор заметки со счётчиком

GO
package main

import (
        "fmt"

        "fyne.io/fyne/v2"
        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/widget"
)

func main() {
        myApp := app.New()
        window := myApp.NewWindow("Заметка")

        counter := widget.NewLabel("Символов: 0")

        editor := widget.NewMultiLineEntry()
        editor.PlaceHolder = "Пиши заметку... Shift+Enter — сохранить"
        editor.Wrapping = fyne.TextWrap{Word: true, Break: true}
        editor.SetMinRowsVisible(8)

        editor.OnChanged = func(s string) {
                counter.SetText(fmt.Sprintf("Символов: %d", len([]rune(s))))
        }
        editor.OnSubmitted = func(s string) {
                counter.SetText("Заметка сохранена: " +
                        fmt.Sprintf("%d символов", len([]rune(s))))
        }

        window.SetContent(container.NewVBox(editor, counter))
        window.ShowAndRun()
}

Многострочное поле с переносом по словам и резервом на восемь строк: SetMinRowsVisible держит высоту стабильной, fyne.TextWrap из флагов Word и Break переносит длинные строки. OnChanged обновляет счётчик на каждое нажатие — строки здесь короткие, это допустимо; для тяжёлых вычислений дросселируй. OnSubmitted в многострочном поле ловит Shift+Enter как «сохранить». Счётчик меняется через SetText — сеттер сам просит перерисовку, Refresh не нужен.

Enter в многострочном поле

Прочитайте код и выберите, что он выведет:

entry := widget.NewMultiLineEntry()
entry.SetMinRowsVisible(4)
entry.OnSubmitted = func(s string) {
        status.SetText("Отправлено: " + s)
}
// пользователь ввёл "строка один" и нажал Enter

RichText: текст из сегментов#

widget.Label рисует одну строку одним стилем — текст, который хочется форматировать, просит widget.RichText. Модель сегментная: поле Segments хранит []widget.RichTextSegment, а каждый сегмент — кусок текста со своим стилем. Базовый тип — widget.TextSegment с полями Style и Text; стили — предопределённые переменные пакета: RichTextStyleStrong жирный, RichTextStyleEmphasis курсив, RichTextStyleCode моноширинный, RichTextStyleParagraph блочный абзац. Ключевое различие — флаг Inline в структуре RichTextStyle: инлайн-сегменты текут в одной строке, блочные начинают с новой. Стиль — это не только шрифт: ColorName берёт цвет из активной темы (theme.ColorNameError покрасит ошибки в красный палитры), SizeName задаёт размер, TextStyle собирает Bold, Italic и Monospace. Свой стиль — просто значение структуры: RichTextStyle{Inline: false, TextStyle: fyne.TextStyle{Bold: true}}. Ссылки делаются стилем RichTextStyleHyperlink с полем URL. Конструктор widget.NewRichText принимает сегменты списком; после любой правки Segments зови Refresh — RichText не редактируемый, это дисплей, а не поле ввода.

Отчёт из сегментов

GO
package main

import (
        "fyne.io/fyne/v2"
        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/theme"
        "fyne.io/fyne/v2/widget"
)

func main() {
        myApp := app.New()
        window := myApp.NewWindow("Отчёт")

        report := widget.NewRichText(
                &widget.TextSegment{
                        Style: widget.RichTextStyleParagraph,
                        Text:  "Отчёт за неделю",
                },
                &widget.TextSegment{
                        Style: widget.RichTextStyleStrong,
                        Text:  "Собрано 42 отгрузки",
                },
                &widget.TextSegment{
                        Style: widget.RichTextStyleParagraph,
                        Text:  ", ошибок в документах нет.",
                },
                &widget.TextSegment{
                        Style: widget.RichTextStyle{
                                Inline:    true,
                                ColorName: theme.ColorNameError,
                                TextStyle: fyne.TextStyle{Bold: true},
                        },
                        Text: " Одна посылка просрочена",
                },
                &widget.TextSegment{
                        Style: widget.RichTextStyleCode,
                        Text:  " order-1024",
                },
        )

        window.SetContent(container.NewVBox(report))
        window.ShowAndRun()
}

Первый сегмент блочный — заголовок с новой строки; дальше инлайны текут в абзаце: жирный, обычный, цветной и моноширинный куски склеиваются в одну строку. Собственный стиль собран структурой RichTextStyle с ColorName из темы — при смене палитры приложения цвет обновится вместе со всем интерфейсом. В реальном приложении Segments собирают программно из данных и зовут report.Refresh() — например, перекрашивая статус задачи.

Ты дописал новый сегмент в rt.Segments. Что нужно, чтобы он появился на экране?

Инлайн против блочного

Прочитайте код и выберите, что он выведет:

rt := widget.NewRichText(
        &widget.TextSegment{
                Style: widget.RichTextStyleStrong,
                Text:  "Первый",
        },
        &widget.TextSegment{
                Style: widget.RichTextStyleParagraph,
                Text:  "Второй",
        },
)

TextGrid: моноширинная сетка#

RichText хорош для документов, но для логов, диффов и кода есть инструмент поточнее — widget.TextGrid. Это моноширинная сетка символов: текст хранится построчно, и каждая ячейка может нести собственный стиль. Создаётся widget.NewTextGrid(«строка\nстрока»), полностью перезаписывается SetText, к отдельным ячейкам добираешься SetStyle(строка, столбец, стиль), к диапазонам — SetStyleRange(строка, от, до, стиль); строки нумеруются с нуля. Готовые фабрики стилей: TextGridStyleForeground и TextGridStyleBackground красят в заданный цвет, TextGridStyleWhitespace подсвечивает пробелы и табы — удобно показывать «хвостовые» пробелы, как в редакторах кода. TextGrid не редактируемый и не умеет форматных абзацев — зато быстр: сетка рендерится как есть, без вёрстки сегментов, и тянет большие объёмы строк. Ниша ясна: просмотр логов с цветными уровнями, диффы файлов, ASCII-диаграммы, читалка кода в справке приложения. Редактора кода из коробки в тулките нет — Grid это только дисплей; ввод остаётся территорией Entry.

Лог сборки с цветными уровнями

GO
package main

import (
        "image/color"

        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/widget"
)

func main() {
        myApp := app.New()
        window := myApp.NewWindow("Лог сборки")

        log := widget.NewTextGrid(
                "INFO  сборка начата\n" +
                        "WARN  кеш устарел\n" +
                        "ERROR файл не найден",
        )
        log.SetStyle(1, 0, 3, widget.TextGridStyleForeground(
                &color.NRGBA{R: 214, G: 158, B: 46, A: 255}))
        log.SetStyle(2, 0, 4, widget.TextGridStyleForeground(
                &color.NRGBA{R: 192, G: 57, B: 43, A: 255}))

        window.SetContent(container.NewScroll(log))
        window.ShowAndRun()
}

Три строки лога одним TextGrid: уровни WARN и ERROR покрашены по ячейкам через SetStyle — строки нумеруются с нуля, диапазон от нулевого столбца покрывает слово целиком. TextGridStyleForeground принимает любой color.Color из седьмого урока. Длинный лог оберни в container.NewScroll, как здесь; если лог перечитывается целиком, проще собрать новый текст и вызвать SetText, а затем раскрасить заново.

Каталог: ввод и дисплейный текст#

ИнструментЧто умеетТипичная роль
widget.NewEntryоднострочный ввод: Text, PlaceHolder, OnChanged, OnSubmittedимя, поиск, короткие значения
widget.NewMultiLineEntryмногострочный ввод: Wrapping, SetMinRowsVisible, Shift+Enterзаметки, описания, чат
widget.NewPasswordEntryмаскировка символов точкамипароли, ключи, токены
entry.SetTextзадать текст: сеттер сам зовёт Refreshинициализация, вставка из кода
widget.RichTextсегменты TextSegment со стилями: инлайн и блочныеотчёты, сообщения, статусы
RichTextStyle и ColorNameцвет и стиль из темы: Strong, Emphasis, Code, свойакценты, ошибки, ссылки
widget.TextGridмоноширинная сетка: SetStyle, SetStyleRange, whitespaceлоги, диффы, код, ASCII
container.NewScrollпрокрутка длинного содержимогобольшие тексты и логи

Что из перечисленного пользователь может редактировать?

Собери поле редактора

Восстанови четыре пропуска: конструктор, подсказка, резерв высоты и обработчик отправки.

editor := widget.() editor. = "Пиши здесь..." editor.(6) editor. = func(s string) { status.SetText("Сохранено: " + s) }

Путь одного символа

Расставь по порядку, что происходит от нажатия клавиши до OnSubmitted.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Практика: поиск, чат, дросселирование#

Несколько сценариев, где эти детали экономят часы. Живой поиск: Entry с OnChanged фильтрует данные и обновляет список — но пересчитывать тяжёлый запрос на каждый символ расточительно; сохраняй последнее значение и срабатывай только на реальную перемену, а данные для отображения готовь заранее в срезе — приём целиком разберём в десятом уроке. Ввод больших текстов: многострочное поле с Wrapping по словам внутри container.NewScroll — стандартная схема заметок и комментариев. Форматированный чат: сообщения собираются в RichText из сегментов, стили ошибок и успеха берутся ColorName из темы — смена палитры не оставит их грязными, а время и номера можно дать моноширинным Code-сегментом. Подсветка синтаксиса в просмотрщике — это TextGrid плюс собственная раскраска по правилам языка. Если же нужен нестандартный контрол ввода с собственной отрисовкой — возвращайся к CreateRenderer из восьмого урока: Entry тоже однажды был собран именно так. Валидация ввода — отдельное ремесло, ей посвящён восемнадцатый урок; связывание полей с данными через binding — семнадцатый.

Что дальше: текст в большом приложении#

Сам Entry с обработчиками пришёл из третьего урока, фокус и клавиатура — из шестого, а форма с валидацией полей — из пятого. Собственный контрол ввода с нестандартной отрисовкой собирается по восьмому уроку, а кормить текстом длинные перечни научит десятый — там же пригодится живой поиск из этого урока. Полный маршрут из двадцати пяти уроков — на странице курса Fyne. Домашнее задание соберёт экран чата: RichText для истории, многострочный ввод для отправки и TextGrid для служебного лога — все три героя урока в одном окне.

Термины урока

Термин

SetText против entry.Text

нажмите, чтобы перевернуть
Определение

SetText — сеттер: обновляет поле и сам зовёт Refresh, экран меняется сразу. Прямое присвоение Text меняет данные без перерисовки — проявится только при случайной перерисовке.

1 / 6

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Виртуальный экран

HTML-модель виджет-дерева fyne
Ввод текстаfyne · мок
Журнал событий

Экран собран: «Ввод текста»

Коротко: что запомнить

  • Entry меняет текст через SetText — сеттер сам зовёт Refresh; прямое присвоение Text проявляет только при перерисовке.
  • OnChanged срабатывает на каждое нажатие, OnSubmitted — на «отправку»: Enter в однострочном поле, Shift+Enter в многострочном.
  • Многострочный ввод: NewMultiLineEntry, Wrapping с флагами Word и Break, SetMinRowsVisible для стабильной высоты.
  • RichText собирается из сегментов TextSegment: инлайновые стили текут в строке, блочный Paragraph начинает новую.
  • Цвета и размеры RichText берутся из темы по ColorName и SizeName; после правки Segments нужен Refresh.
  • TextGrid — моноширинная сетка для логов и кода: SetStyle и SetStyleRange красят ячейки, whitespace подсвечивает пробелы.
  • Редактируемое — только Entry; RichText и TextGrid — дисплейные виджеты.

Домашнее задание

Собери экран чата: сверху — RichText с историей из трёх последних сообщений, каждое собрано из сегментов (автор Strong, текст Paragraph, время Code), снизу — многострочный Entry с Wrapping и SetMinRowsVisible(4). Отправка по Shift+Enter: сообщение добавляется в Segments, RichText обновляется Refresh, поле очищается SetText. Добавь счётчик символов через OnChanged и предупреждение для сообщений длиннее двухсот символов — сегментом с ColorName темы Error. Затем сделай окно логов на TextGrid: генерируй строки со случайными уровнями INFO, WARN и ERROR, крась уровни TextGridStyleForeground в разные цвета, на отдельной строке-образце включи подсветку пробелов TextGridStyleWhitespace. В конце оберни чат в container.NewScroll и проверь, как ведёт себя прокрутка при длинных сообщениях, а также убедись, что Enter в поле ввода вставляет перевод строки, а не отправляет.

Личные заметкиПусто
Хранятся только в вашем браузере (Fyne · Текст и ввод данных)

Вопросы и ответы по уроку

Почему entry.Text = «строка» не обновил экран?

Присвоение меняет поле, но перерисовка происходит только по команде Refresh. Используй SetText — он сам просит кадр. Прямое присвоение оставь для инициализации до первого показа окна.

Как показать красный текст ошибки?

В RichText задай сегменту стиль с ColorName: theme.ColorNameError — цвет придёт из активной темы. У widget.Label цвет привязан к теме и напрямую не красится: либо RichText, либо canvas.Text из седьмого урока с ручным цветом.

TextGrid умеет редактирование?

Нет, это чистый дисплей: сетка символов со стилями ячеек. Ввод — территория Entry; редактор кода собирают как собственный контрол поверх TextGrid с TypedRune из шестого урока — задачка уровня кастомных виджетов восьмого урока.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Сравните с альтернативами

Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.

Следующая ступень учебного пути

Cobra25 уроков~10 чпрактика25 уроков уже доступны

Cobra: CLI-приложения на Go

Интерактивный курс Cobra — фреймворка для CLI на Go: команды, флаги, конфиги Viper, автодополнение. 25 уроков с примерами кода, тестами и итоговым приложением.

  • Создавать CLI-приложения с деревом команд на Cobra
  • Работать с аргументами, флагами и их привязкой к конфигу
  • Управлять конфигурацией через Viper: файлы, env, приоритеты
  • Писать хуки PreRun/PostRun и корректно обрабатывать ошибки