-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Fixes and new features #239
Conversation
Hey @flagarde wouldn't it make more sense to use a |
@MCWertGaming Nifty gives you:
This is not possibe to my knowledge with a raw share_ptr. An other drawback would be for the user . std::cout << "This should be printed in terminal if stdout is not redirected" << std::endl;
Term::terminal << "Now type a number and then a string !" << std::endl; with shared_ptr the syntax would be different. Maybe we could add some typedef but i'm not sure it is usefull. The magic trick is hidden to the user. An other feature PR which will follow is that in many places you can see HANDLE hConOut{CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)};
HANDLE hConIn{CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)}; to explicitly talk to the terminal and not the stdin stdout etc that could be redirected by the user. This can be avoid if we use a second nifty counter to open this handle and close them at the end.. This is important because the OS has some limit on the number of open files.. If they are not well closed it could create very hard to debug error. Assuring the opening once at start we are sure the functions will work and that they will be closed at the end of the program One of the advantage of this is the simplicity for the user to have ANSII escape code etc on windows. |
Yeah, pretty nice, thank you! |
Hi, I tested it on windows but maybe not enought.. could you share your code ? The way the library is functioning has changed drastically recently. Could you tell me what terminal you are using too? It seems the code think your terminal doesn't support escape codes... Does the examples provided with cpp-terminal are working fine ? colors for example |
Hi Color works indeed, also menu window example, but if I keep the key pressed the menu box goes up and down one row. I guess it's because of terminal scrolling not exact adjustment. Also if I maximize the terminal window, it draws 2 menus. I was not being able to make my program to work at the moment, I'll try again later. By the way, why are the method parameters missing in .h files? When using autocompletion you only get things like Thanks. |
@iganinja Are you using this kind of code to be aware of the change of terminal size. For now I think you need to clear and redraw by hand the screen : render(term_size.rows(), term_size.columns(), h, w, pos);
Term::Event event = Term::read_event();
switch(event.type())
{
case Term::Event::Type::Key:
switch(Term::Key(event))
{
case Term::Key::ARROW_LEFT:
if(w > 10) w--;
break;
case Term::Key::ARROW_RIGHT:
if(w < (term_size.columns() - 5)) w++;
break;
case Term::Key::ARROW_UP:
if(pos > 1) pos--;
break;
case Term::Key::ARROW_DOWN:
if(pos < h) pos++;
break;
case Term::Key::HOME: pos = 1; break;
case Term::Key::END: pos = h; break;
case Term::Key::q:
case Term::Key::ESC:
case Term::Key::CTRL_C: on = false; break;
default: break;
}
break;
case Term::Event::Type::Screen:
term_size = Term::Screen(event);
std::cout << Term::clear_screen() << std::flush;
render(term_size.rows(), term_size.columns(), h, w, pos);
break;
default: break;
}
}
case Term::Event::Type::Screen:
term_size = Term::Screen(event);
std::cout << Term::clear_screen() << std::flush;
render(term_size.rows(), term_size.columns(), h, w, pos);
break; is where the magic happens. If you can/ are allowed to send your code I could have a look. If it is not so big and if you agree it would be nice to have it as example or test has it seems to be able to detect a lot of regression on the library :) the method parameters are missing on .h just by my faulty preference (mainly because im very bad on giving names so I avoid them in .h as this is accessible to the end user). But I have proposed a PR to have a doxygen documentation #245 so this defect should be solved gradualy |
Hi again @flagarde I just created a new public repository on my github account, so you can get the code easily: https://github.com/iganinja/tmse I would be really glad to help cpp-terminal to mature with TMSE. There is some code already but I think you could follow with no problem. Take a look at the Respect to doxygen documentation, I would be happy just having the same parameter names we have in the .cpp files. With that it would be enough, as autocomplete would work properly. Thanks! |
@iganinja I did basic checks on Linux and one thing very strange is that the termina is not restored in CookedMode when the program exit :( If I understand correctly you have problem with resizing ? I did this : #include "mainwindow.h"
#include "tasks/mainwindowtasks.h"
#include "tasks/mainmenutasks.h"
#include <cpp-terminal/terminal.hpp>
#include "cpp-terminal/screen.hpp"
#include <thread>
#include <iostream>
using namespace Olagarro::Tasks;
using namespace TMSETasks;
using namespace std::chrono_literals;
MainWindow::MainWindow() :
testingSelectionWindow{"About", "Text Mode Sane Editor v0.1", {"OK"}, 40, 1, 1}
{
Term::terminal.setOptions({Term::Option::Raw});
Term::terminal_title("Text Mode Sane Editor");
checkTerminalResizing();
std::string text
{
"Esto es una prueba de un texto que tiene que partirse pero al nivel de las palabras, no a nivel de carácteres."
};
testingTextBox.setTitle("Warning");
testingTextBox.setText(std::move(text));
toggleMainMenuVisibility();
}
void MainWindow::run()
{
createTasks();
draw();
auto shouldRedraw{true};
while(mShouldKeepRunning)
{
mCurrentKey = Term::Key::NO_KEY;
Term::Event event = Term::read_event();
switch(event.type())
{
case Term::Event::Type::Key:
mCurrentKey = Term::Key{event};
shouldRedraw = true;
break;
case Term::Event::Type::Screen:
{
shouldRedraw = true;
Term::Screen screen(event);
mTerminalWindow = std::make_unique<Term::Window>(screen.columns(), screen.rows());
onResize(screen.columns(), screen.rows());
break;
}
default: break;
}
if(shouldRedraw)
{
draw();
}
mTaskExecutor.update();
// std::this_thread::sleep_for(1ms);
//checkTerminalResizing();
}
}
Term::Key MainWindow::currentKey() const
{
return mCurrentKey;
}
void MainWindow::consumeCurrentKey()
{
mCurrentKey = Term::Key::NO_KEY;
}
void MainWindow::exit()
{
mShouldKeepRunning = false;
}
Widgets::MainMenu& MainWindow::mainMenu()
{
return mMainMenu;
}
void MainWindow::toggleMainMenuVisibility()
{
mMainMenu.setVisible(!mMainMenu.isVisible());
onResize(mLastTerminalSize.x, mLastTerminalSize.y);
draw();
}
void MainWindow::toggleWelcomeBackgroundVisibility()
{
onResize(mLastTerminalSize.x, mLastTerminalSize.y);
draw();
}
void MainWindow::draw()
{
mTerminalWindow->clear();
// WARNING Temporal
if(mSize.x <= 40 || mSize.y < 10)
{
return;
}
mFilesTabs.draw(*mTerminalWindow);
mMainMenu.draw(*mTerminalWindow);
testingSelectionWindow.draw(*mTerminalWindow);
testingTextBox.draw(*mTerminalWindow);
std::cout << mTerminalWindow->render(1, 1, true) << std::flush;
}
void MainWindow::onResize(size_t newWidth, size_t newHeight)
{
mSize = Utils::Size{newWidth, newHeight};
if(mMainMenu.isVisible())
{
mMainMenu.setPosition(0, 0);
mMainMenu.onResize(newWidth, 1);
mFilesTabs.setPosition(0, 1);
mFilesTabs.onResize(newWidth, newHeight - 1);
testingSelectionWindow.setPosition(1, 2);
}
else
{
mFilesTabs.setPosition(0, 0);
mFilesTabs.onResize(newWidth, newHeight);
testingSelectionWindow.setPosition(1, 1);
}
testingTextBox.setPosition(10, 20);
testingTextBox.onResize(newWidth * 0.8, 6);
}
void MainWindow::checkTerminalResizing()
{
auto currentTerminalSize{Term::screen_size()};
const Utils::Size currentSize{currentTerminalSize.columns(), currentTerminalSize.rows()};
if(mLastTerminalSize != currentSize)
{
mTerminalWindow = std::make_unique<Term::Window>(currentSize.x, currentSize.y);
onResize(currentSize.x, currentSize.y);
mLastTerminalSize = currentSize;
}
}
void MainWindow::createTasks()
{
mTaskExecutor.addTask(TMSETasks::mainWindowTasks(*this));
mTaskExecutor.addTask(TMSETasks::mainMenu(*this));
} It works but sometimes is crashing and when I press F4 the size is not propagated somewhere. Now cpp-terminal has its own way to detect terminal size change. This requires more changes in your code but it should be more efficient as it uses platform specifics way to detect this changes. (signal on Linux and WINDOW_BUFFER_SIZE_EVENT and Windows). For me the most strange thing is that the terminal is not turned back in Cooked Mode when I quit the program. I need to investigate this too |
Hi @flagarde . Sorry for the late answer, I've been out of my home this week and I had not time to test your proposal. I copied and pasted your What I did also is to download and build TMSE's code from github, in Linux, and it seems to work. At least it draws everything and it reacts correctly to input. I don't understand how menu demo works but my code doesn't, as far as I know there are doing the same, drawing things in |
@iganinja Hi, Yes I agree it's very strnage situation. Cousl you give more information about your system? Windows version etc to try to dig into this very strange problem. I will try on my virtualbox. From the picture it seems to have 2 problems : Console is not turned into ASCII escape mode and some garbage texts. I imagine you use the terminal provided by QTcreator right ? Have you try to use the windows standard one ? Maybe QT creator add a layer on top of the terminal and this is maybe not detected my the library. I suggest to create a dedicated issue for this :). It would be easier for people to see this issue and maybe someone have the solution. I'm not expert on Windows "logics" |
I launched a Microsoft Windows [Versión 10.0.19044.2846] Then, I run the executable and got the same garbage I get running it in Qt Creator's terminal, so unfortunately it's not something related to this IDE. Previous versions of |
@iganinja I agree, Let me do some basic tests. Which compiler are you using ? which version? It's very strange color is working on main.cpp try
{
if(!Term::is_stdin_a_tty())
{
std::cout << "The terminal is not attached to a TTY and therefore can't catch user input. Exiting...\n";
return 1;
}
std::cout<<Term::color_fg(Term::Color::Name::Blue)<<"Test"<<Term::color_fg(Term::Color::Name::Default)<<std::endl;
loadSettings();
MainWindow mainWindow;
mainWindow.run();
} Test is printed in I don't know if it's a cpp-terminal library problem ? |
Hi again @flagarde I tested your last code and it works, I get the blue foreground and black background: I'm using MinGW 11.2.0 64 bits. Anyway, I'm using VC++ compiler in my gitlab repository in a job, and it creates an executable with the same behavior (I mean, with my full app, not the test you just posted). Is it possible that something got messed up in |
I have found the problem. Now the library create a terminal automatically. You don't need to create any on the classes you use.. Please suppress Term::Terminal mTerminal; on mainwindows.h. I have this : Maybe I should add a crashing if people create other instance of the class or be more precise on the new way cpp-terminal is working Please see : https://github.com/jupyter-xeus/cpp-terminal/blob/master/cpp-terminal/io.hpp |
What a coincidence, I just found right now that this also breaks the test:
This is without creating a |
@iganinja Which kind of problem ? #include <cpp-terminal/exception.hpp>
#include <cpp-terminal/input.hpp>
#include <cpp-terminal/terminal.hpp>
#include <cpp-terminal/tty.hpp>
#include <cpp-terminal/window.hpp>
#include <cpp-terminal/options.hpp>
#include "settings.h"
#include "mainwindow.h"
#include "utils/exception.h"
#include "cpp-terminal/color.hpp"
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono_literals;
int main()
{
try
{
if(!Term::is_stdin_a_tty())
{
std::cout << "The terminal is not attached to a TTY and therefore can't catch user input. Exiting...\n";
return 1;
}
std::cout<<Term::color_fg(Term::Color::Name::Blue)<<"Test"<<Term::color_fg(Term::Color::Name::Default)<<std::endl;
loadSettings();
int i;
std::cin>>i;
//MainWindow mainWindow;
//mainWindow.run();
}
catch(Utils::Exception& exception)
{
std::cout << "Error: " << exception.message() << "\n";
return 1;
}
catch(const Term::Exception& exception)
{
std::cout << "cpp-terminal error: " << exception.what() << "\n";
return 2;
}
catch(const std::exception& exception)
{
std::cout << "Unknown error: " << exception.what() << "\n";
return 3;
}
return 0;
} Is still working for me |
I tried without |
@iganinja You are welcome :) . To be sure the terminal is created automatically, you can add io.hpp. If you need to change options just add terminal.hpp call setoptions and that's it (terminal.hpp called io.hpp). The API is still not fixed maybe the class terminal should be hidden to users to avoid creating one. The changes have been made very recently and it's a big change on the way cpp-terminal is working but IMHO it makes its use easier especially on windows. Terminal is created the same way std::cout is created so think of Term:terminal as std::cout. You can even do Term::terminal<<"Test" if you want to be sure to print to the terminal and not cout. Very useful if cout has been redirected :) |
@flagarde thank you for the PR and all the Windows fixes! Thanks @MCWertGaming for reviewing and merging. @iganinja I want to create menus like you have. Do we have this functionality in cpp-terminal, or not yet? If not, then let's create it, it would be useful to many. |
@certik Thx for your nice words. We don't have this yet but I'm still focused on polishing the basic features and cleaning the API. I hope the library is becoming easier to use and have much features. UTF8 is still a bit hard to make correct on this library and it would be a nice addition I think. Mouse event would be nice to have too :). In windows it's easy but on linux it depends on the terminal and need escape code parsing. After the feature you requires could be built on top. Of course this menu could be coded with the actual library and updated while the basics are changing. |
@flagarde awesome! Great plan. What doesn't work with UTF-8? I thought I got the basics working. |
About UTF8, would it be possible to use https://github.com/nemtrif/utfcpp? I'm using it on my program, it seems mature and reliable. @certik As @flagarde said, there is no menu or widget concept in cpp-terminal. I think it would be nice to have an optional layer on top of cpp-terminal in order to build more sophisticated apps it if the user wants to. Do you want only menus, or a full set of useful widgets? Related to that, the other day I found this: https://github.com/gansm/finalcut In short, it's a terminal mode Qt library, in the sense that it works like Qt in regards to widgets but in text mode. Unfortunately it only works in Linux and other Posix systems, but not natively in Windows. |
@certik You are right the basics are here :). But I discovered some corner cases for example, chinese user could type in pinyin and had a program to print 中 on the terminal, this hanzi in the code was splited in byte and read one byte by byte now it is read as full utf8 but no detecting on if it's utf8 character or a bounch of byte copy pasted is done yet. For this kind of language it could be nice to have this feature, input not detected as key but as codepoint and act accordingly. Windows utf8 detectin etc seems to be checked size of character (printed) is necessary too (chinese or CJK characters for example are mainly 2 times bigger than (2 spaces) ASCII characters or european ones) ... |
@certik @iganinja There is indeed good alternatives.. I searched for long time the best alternative (for me...), clean easy to understand not too much dependency, cross-platform and found this library.. Looking into the code I liked the style and I saw it could be easily extended. Most alternatives are not cross-platform, C style coding or depend on big libraries... cpp-terminal offer some nice features others seem not to provide. It could be extended of course (I have still some ideas in mind if the maintainers accept the PR of course). Unfortunately I'm adding such idea a bit randomly or by step as some feature will/ necesitate/d big changes on the API and codes. I agree with you @iganinja , adding layers is a good choice (onion based library :) ). I really enjoy C++ for its "Pay only for what you use" philosophy and IMHO lirary should try to follow this rule. |
I am glad you like it @flagarde (yes, let's fix the utf-8 issues that you found). I did a thorough research of many libraries out there some time ago, but it was removed in #147. @MCWertGaming why not having a document where we list these alternatives? They are all good libraries to be inspired from, that's why I put them into the README. Maybe we can put them into a separate file. Let's add https://github.com/gansm/finalcut as @iganinja suggested above and then let's also add https://github.com/Textualize/textual, which has impressive widgets. Also just found https://github.com/charmbracelet/bubbletea and https://github.com/vadimdemedes/ink, both have links in the readme for projects that use them.
I would do full widgets, either Turbo Vision style, or probably using the modern "react" like approach as in textual. There is also imgui style. I am not sure which one is the best approach, but we can start with one, and we can iterate. I believe Textual allows you to run the same code in a terminal (nice TUI) as well as in the browser. We should try to do the same with this optional "widget" layer, then we can optionally compile the C++ code to WASM and it would run in a browser with a nice GUI interface. |
I like Turbo Vision visual style, imitating a graphical GUI. From the programming perspective, having a I also like the WASM idea, it would great to be able to create text mode like web apps. |
@iganinja @certik When I created the CI I added the dockcross ones which comes with wasm but my knowledge is limited on this but the code seems to compile (https://github.com/jupyter-xeus/cpp-terminal/actions/runs/4780025414/jobs/8497486942) but I don't know how this works and if the code is running fine. Maybe some of you could have test or if you have a link to know what wasm is about :). I think it is possible to activate in the repo |
@iganinja @certik utf8 is really something the library should deal with (in Windows it's quite messy but they improve from time to time). We can try to stay away of external library as much as possible as we don't need all the feature provided by most of the utf8 library. but maybe we will have to make the jump but I think it worth trying to try without any dependency first. |
Re utf-8: yes, let's try without a dependency and see. If it's too much for us to handle and maintain, we can pull in a dependency. I started a discussion here: |
Sorry for the big number of commits but I have to move from Linux to Windows and these changes are quite profound.
Mainly :
NOT more.
including the line mentioned 'io.hpp' (I know the name is ugly) and linking to cpp-terminal allows to have a minimal terminal setup. It turn ON ANSII escape code in windows etc without adding nothing in the code (see examples/minimal.cpp) and restore the old state when quiting. This lightweight version could interest many people I think.
Allow to use Term::terminal<< or Term::terminal>> adding "terminal.hpp" (see examples/cin.cpp) This operator always read, write to console (even if stdin stdout as been redirected)
By default the terminal is no Cooked you need to explicitly turn it into RawMode (see examples/cin.cpp etc...). It was necessary to allow 1)
Cleaning, warning fixes etc...
An other PR will follow with futher cleaning (2nd Nifty_Counter to avoid open/close file handlers and simplify code).