Предисловие
Некоторое время назад мне понадобилось написать некое консольное приложение для собственных нужд. Но так как это было бы слишком просто, было решено прикрутить к нему некое подобие GUI.
Блуждая на просторах интернета, мне удалось найти только две библиотеки для .NET, которые, возможно, удовлетворили бы мои потребности: MonoCurses и CursesSharp. Но эти библиотеки целиком и полностью заточены под ncurses — *nix-библиотеку для управления терминалом, и с Windows если и вообще совместимы, то очень плохо. В итоге возникло единственное решение написать свой велосипед.
Функционал
Кроссплатформенность
GoddamnConsole полностью совместим и с Windows (используется WinAPI), и с Linux (используется ncurses), и, возможно, даже с другими Unix-подобными ОС (Mac OS X, FreeBSD).
Весь функционал библиотеки одинаково работает на обеих операционных системах. Кроме того, поддержку ОС можно расширить путем написания консольного провайдера (если на этой ОС можно запустить .NET, конечно).
Визуальные компоненты
Компонент — основная единица UI библиотеки. Для написания своего компонента достаточно просто создать наследника класса Control и переопределить пару методов (отрисовка, события клавиатуры). Также доступно несколько встроенных компонентов:
— TabControl — компонент, использующийся для создания вкладок. Переключение вкладок происходит по нажатию на стрелку вправо и влево.
— TextView — компонент для отображения текста.
StackPanel — простейший контейнер, укладывает дочерние элементы в столбец или строку.
Кстати, это пример компонента, у которого даже не переопределен метод отрисовки — т.е. сам он ничего не рисует.
— Grid — контейнер, представляющий собой сетку элементов. Очень похож на одноименный компонент из WPF.
GridWindow дублирует функциональность этого контейнера, за исключением того, что GridWindow самостоятелен, а Grid — нет.
— TextBox — компонент для отображения и ввода текста. В отличие от TextView, захватывает фокус и поддерживает скроллинг.
— Button — кнопка. Имеет событие Clicked, вызывающееся при нажатии на Enter.
Независимость от размера консоли
Независимость от размера консоли — очень важный элемент консольного (да и вообще любого) UI, так как терминалы *nix поддерживают свободное изменение своего размера. А с недавних пор такая возможность была добавлена и в Windows 10.
Как это использовать?
Для использования библиотеки вам достаточно скачать последний релиз библиотеки из репозитория GitHub и добавить ссылку в проект на нее. Кроме того, вы можете скачать исходники и самостоятельно скомпилировать их. В случае Linux с компиляцией могут возникнуть проблемы из-за того, что xbuild не умеет собирать проекты .NET 4.5. Вам потребуется либо собрать библиотеку вручную (инструкция будет позже), либо воспользоваться компьютером с Windows.
Простой пример использования:
using GoddamnConsole.Controls;
using Console = GoddamnConsole.Console;
namespace GoddamnConsoleSample
{
internal class Program
{
private static void Main()
{
var window = new ContentWindow(); // создание окна
window.Title = "Sample Window"; // установка заголовка окна
var text = new TextView(); // создание текстового поля
text.Text = "Hello, World!";
window.Content = text; // установка содержимого окна
Console.Windows.Add(window); // добавление окна в консоль
Console.Start(); // запуск консоли
}
}
}
Результат:
Кроме того, в репозитории вы можете найти пример, который охватывает практически всю текущую функциональность.
Заключение
Библиотека еще не готова полностью, но она вполне пригодна для построения простых (и не только) приложений.
Некоторая функциональность в статье не описана (например, Data Binding) в следствие ее недостаточной завершенности. В дальнейшем планируется добавить больше компонентов, добавить новую функциональность и отладить существующую.
С удовольствием отвечу на все вопросы в комментариях, если возникнут.
Отдельное спасибо сайту govnokod.ru за помощь в костылировании под линукс.
Ссылки
Комментарии (52)
Vjatcheslav3345
06.07.2016 12:33-4Может пригодится в обучении программированию — сначала учат на консольных программах, потом на переходном этапе — приложениям с этой библиотекой, и, затем, переходят к неконсольным программам.
Melz
06.07.2016 13:52+1Ух ты!
Мне периодически такое хотелось сделать. Я смотрел на MonoCurses, но там последний коммит 8 лет назад.
А CursesSharp точно под Win не работает? Там написано что он через CursesSharp все делает.
mnakamura
06.07.2016 14:20О, кстати, а расскажите, можно ли у вас в Windows выводить на консоль такие символы, как U+2014 (—), U+2E3A (?) или U+2E3B (?), как это будет выглядеть и сколько знакомест занимать? Как вы вообще решали проблему с разной шириной моноширинных символов — wcwidth() по идее есть только в ncurses, какими-то словарями, EastAsianWidth.txt или чем-то таким?
zawodskoj
06.07.2016 14:35Странная проблема, не столкнулся с такой
mnakamura
06.07.2016 15:08Ну, вы же как-то считаете длины строчек — сколько знакомест будет занимать выведенная на экран строка?
zawodskoj
06.07.2016 15:21Один символ — одно знакоместо
mnakamura
07.07.2016 14:21Понятно. К сожалению, в реальности все не так просто: какой-нибудь ? занимает два знакоместа, а есть еще символы вроде приведенных выше, которые занимают по три. В ncurses есть для этого такая функция, как wcwidth(), а вот как это делать без ncurses мне всегда было интересно. Вот ниже подсказывают, что тоже хорошего решения не нашли.
iqiaqqivik
06.07.2016 16:29> проблему с разной шириной моноширинных символов
Моя консоль (gnome-terminal) просто начинает рисовать следующий символ в позиции «текущая + ширина m», прямо поверх всех этих дашей. Я не думаю, что это проблема библиотеки, я думаю, это вопрос алгоритма отрисовки моноширинного шрифта консолью.
xaizek
06.07.2016 20:05+1Когда мне это понадобилось на Windows, я просто взял ту же реализацию, что используется в ncurses (лежит здесь). Как это сделать через WinAPI, я не нашёл. Если просто выводить такие символы по соседним координатам, то каждый второй пропадёт. Может работать нормально при обычном выводе типа WriteFile. Но если рисовать через WriteConsoleOutput, чтобы можно было задавать атрибуты для каждого символа отдельно, то надо вставлять "пропуски" c нужными атрибутами, чтобы всё нормально отображалось.
DeXPeriX
16.07.2016 12:04Не подскажете, какие именно атрибуты нужны для «пропусков»? И где про это можно почитать? Японские иероглифы консоль сможет отобразить, если Windows русская?
xaizek
16.07.2016 12:20+1Не подскажете, какие именно атрибуты нужны для «пропусков»?
Те же, что и для самих символов. Я так патчил для pdcurses.
И где про это можно почитать?
На MSDN я не увидел такого требования (может не заметил), но сделал так и всё начало отображаться нормально.
Японские иероглифы консоль сможет отобразить, если Windows русская?
Должно, я с китайскими пробовал на английской Windows. Единственное, шрифт должен уметь их выводить. В cmd.exe оно просто так не работало (квадраты вместо символов, но при правильном шрифте должно отображаться), а ConEmu отображало все иероглифы правильно изначально.
bolk
06.07.2016 21:15-1Разной шириной моноширинных символов? Реально есть такая проблема? По мне так взаимоисключающие параграфы.
xaizek
06.07.2016 21:22+1Есть. Не столько для специальных символов, сколько для CJK-символов, которые обычно занимают две соседние символьные позиции, так как в одну они просто не помещаются.
bmforce
06.07.2016 14:55+2В своё время наткнулся на такую библиотеку:
https://github.com/elw00d/consoleframeworkzawodskoj
06.07.2016 15:11Интересная библиотека, но развивается довольно медленно
elw00d
06.07.2016 18:12Да, к сожалению, это так. Выяснилось, что это мало кому нужно. Хотел заняться ей плотнее после выхода .NET Core, но там отпилили режим build --native, а очень хотелось попробовать именно в этом режиме позапускать. Но в любом случае, буду пробовать портировать на .NET Core, а там может быть и нативный режим прикрутят.
Athari
12.07.2016 15:51У вашей библиотеки есть какие-то принципиальные преимущества над ConsoleFramework? Может, если библиотека развивается медленно, стоило начать с пулл-реквестов, а не с отдельной реализации конкурента с нуля? :)
zawodskoj
12.07.2016 15:59Когда я начинал разработку, про эту библиотеку я не знал
Принципиальных отличий нет, но реализовано все по-другому. Начиная от реализации кроссплатформенности (хотя есть свои косяки), заканчивая недавно добавленной поддержкой XAML (в библиотеке elw00d самописные парсеры, у меня System.Xaml)Athari
12.07.2016 16:48Посмотрел код. С лейаутом всё очень печально (в WPF и ConsoleFramework двухпроходной measure-arrange, у вас что-то непонятное), типографики нет (перенос текста по дефисам и всё такое), иерархия классов вызывает вопросы (зачем GridWindow, если есть Grid и Window?), код местами очень сомнительный (простыни пятивложенного кода на несколько страниц в Grid). В целом, всё несоизмеримо проще и топорнее, чем в ConsoleFramework. По-моему, лучше приложить усилия к развитию ConsoleFramework, чем изобретать что-то своё.
У System.Xaml есть серьёзный недостаток — он не очень-то переносимый и универсальный: его нет в .NET Core, а в Mono он кривой.
zawodskoj
12.07.2016 17:07Бросать разработку я не буду, но в ConsoleFramework пару пулл-реквестов может быть и сделаю
Shoomaher
06.07.2016 19:23Боюсь, что задаю глупый вопрос, но всё же, есть ли что-нибудь подобное для java? Хоть что-нибудь, что поможет реализовать псевдо-графический интерфейс?
korolv
06.07.2016 22:18+2Мы Lanterna используем для Java. Вполне удобная библиотека: github.com/mabe02/lanterna
sova
07.07.2016 02:46чем оно лучше curses?)
zawodskoj
07.07.2016 07:26curses — библиотека для управления терминалом. Не надо сравнивать мою библиотеку и curses — у них разное назначение
sova
07.07.2016 14:14-1но curses позволяет делать все тоже самое…
zawodskoj
07.07.2016 15:27+1Зачем тогда WPF? WinAPI ведь позволяет делать все то же самое
sova
07.07.2016 15:29не совсем корректно, curses позволяет делать тоже самое для консоли примерно в тех же терминах.
просто более известный проект.zawodskoj
07.07.2016 15:35Во-первых curses нет под Windows (если не считать порты вроде PDCurses, последний стабильный релиз которого был в 2008)
Во-вторых в curses нет и половины того функционала, который есть или будет в моей библиотеке (Data Binding, XAML), ибо предназначена она не для этого
В-третьих, curses не нативен для c# — нужно писать обертки для него (CursesSharp, например)
qwertEHOK
07.07.2016 11:02а под VCL (Delphi) нет такого?
У заказчиков WinXP и любое GUI приложение под Delphi XE10 требует дополнительного тестирования. Проще им сделать отдельный инфтерфейс в стиле DOS
DrugGarry
07.07.2016 13:03Был Visual Basic For DOS 1.0, дальше не пошло. Окошки в стиле Борландовского Турбо. Ээех, время бежит…
rbobot
Красота. Надо будет из powershell ее заиспользовать.