tBane Temat założony przez niniejszego użytkownika |
[SFML 2.X] Notepad v2 - aplikacja notatnik wersja druga » 2025-06-03 18:28:45 Witam. Postanowiłem od nowa napisać aplikację Notepad, ponieważ w poprzedniej wersji źle pozycjonowałem kursor. Aby to naprawić, musiałem zrezygnować ze zmiennej int index i zamiast niej użyć sf::Vector2i cursorPosition. Skoro usunąłem indeks, wygodniej jest przechowywać tekst w liniach – za pomocą sf::Text. Napisałem już większą część aplikcji. No więc mój problem polega na tym, że nie wiem jak powinien zachować się program w przypadku podania backspace n początku linii. Próbowałem wzorować się na Microsoft Notepad i raczej wrapowanie całego tekstu odpada, ponieważ czasami słowo z poprzedniej linii się cofa i łączy z tym przed którym zrobiliśmy backspace. Pomoże ktoś i wyjaśni jak powinien zachować się tekst po wciśnięciu backspace na początku linii? #include <SFML/Graphics.hpp> #include <iostream> #include <vector> #include <filesystem>
sf::RenderWindow * window;
sf::Font font; short characterSize; sf::Color textColor; sf::Color backgroundColor;
std::vector < sf::Text * > lines;
sf::Vector2i mousePosition; sf::Vector2f worldMousePosition;
sf::RectangleShape cursor; sf::Vector2i cursorPosition = sf::Vector2i( 0, 0 );
sf::Clock timeClock; sf::Time currentTime;
void wrap_text( std::wstring text ) { if( !lines.empty() ) { for( auto & line: lines ) delete line; lines.clear(); } std::wstring line = L""; std::wstring word = L""; std::cout << window->getSize().x << "\n"; for( auto & character: text ) { if( character == L'\n' ) { if( sf::Text( line + word, font, characterSize ).getGlobalBounds().width > window->getSize().x ) { lines.push_back( new sf::Text( line, font, characterSize ) ); line = word; word = L""; } else { lines.push_back( new sf::Text( line + word, font, characterSize ) ); line = L""; word = L""; } } else if( character == L' ' || character == L'\t' ) { if( sf::Text( line + word, font, characterSize ).getGlobalBounds().width > window->getSize().x ) { lines.push_back( new sf::Text( line, font, characterSize ) ); line = word + character; word = L""; } else { line = line + word + character; word = L""; } } else word = word + character; } if( line != L"" || word != L"" ) { lines.push_back( new sf::Text( line + word, font, characterSize ) ); } for( int i = 0; i < lines.size(); i++ ) { lines[ i ]->setPosition( sf::Vector2f( 0, i * font.getLineSpacing( characterSize ) ) ); lines[ i ]->setFillColor( textColor ); } }
void setCursorUp() { if( cursorPosition.y > 0 ) { float targetX = cursor.getGlobalBounds().left; cursorPosition.y -= 1; sf::Text * line = lines[ cursorPosition.y ]; size_t lineLength = line->getString().toWideString().size(); size_t closestIndex = 0; float closestDistance = std::abs( line->findCharacterPos( 0 ).x - targetX ); for( size_t i = 1; i <= lineLength; ++i ) { sf::Vector2f pos = line->findCharacterPos( i ); float distance = std::abs( pos.x - targetX ); if( distance < closestDistance ) { closestIndex = i; closestDistance = distance; } } cursorPosition.x = closestIndex; cursor.setPosition( line->findCharacterPos( closestIndex ) ); } } void setCursorDown() { if( cursorPosition.y < lines.size() - 1 ) { float targetX = cursor.getGlobalBounds().left; cursorPosition.y += 1; sf::Text * line = lines[ cursorPosition.y ]; size_t lineLength = line->getString().toWideString().size(); size_t closestIndex = 0; float closestDistance = std::abs( line->findCharacterPos( 0 ).x - targetX ); for( size_t i = 1; i <= lineLength; ++i ) { sf::Vector2f pos = line->findCharacterPos( i ); float distance = std::abs( pos.x - targetX ); if( distance < closestDistance ) { closestIndex = i; closestDistance = distance; } } cursorPosition.x = closestIndex; cursor.setPosition( line->findCharacterPos( closestIndex ) ); } }
void cursorPositioning() { if( lines.size() == 0 ) { cursor.setPosition( 0, 0 ); return; } float line_length = lines[ cursorPosition.y ]->getString().toWideString().size(); sf::Vector2f pos; pos.x = lines[ cursorPosition.y ]->findCharacterPos( cursorPosition.x ).x; pos.y = cursorPosition.y * font.getLineSpacing( characterSize ); cursor.setPosition( pos ); }
int main() { sf::View view( sf::FloatRect( 0, 0, 480, 640 ) ); window = new sf::RenderWindow( sf::VideoMode( view.getSize().x, view.getSize().y ), "Easy Notepad v2", sf::Style::Titlebar | sf::Style::Close ); font = sf::Font(); font.loadFromFile( "arial.ttf" ); characterSize = 17; textColor = sf::Color( 192, 192, 192 ); backgroundColor = sf::Color( 48, 48, 48 ); wrap_text( L"Gracz najpierw zagaduje handlarza gdyż ten jest najbliżej. Handlarz oferuje skórzane ubranie w zamian za dostarczenie kilku skór od myśliwego, " L"którego gracz mijał wcześniej. Zielarka da graczowi trochę złota w zamian za przyniesienie kilku roślin leczniczych. " L"U kowala gracz może zakupić oręż - zwyczajny prosty miecz gdyż jest to niewprawiony kowal w miecznictwie. " L"Zaś do wieży mędrca nie da się dostać. Gracz rusza spowrotem do myśliwego po skóry, lecz ten jest nieufny, " L"ale ostatecznie zgadza się i daje graczowi skóry.\n" L"Gracz wraca ze skórami do handlarza i odbiera nowe ubranie \"skórzane kurtka\" oraz \"skórzane spodnie\"." L"Handlarz jednak jeszcze jedno zadanie ma dla gracza. Dostawa towarów ze wschodu się opóźnia i trzeba sprawdzić " L"co się z nią stało i tak gracz rusza z kolejnym zadaniem \"spóźniona dostawa\"." ); std::cout << "Current directory: " << std::filesystem::current_path() << std::endl; cursor = sf::RectangleShape( sf::Vector2f( 2, characterSize ) ); cursor.setFillColor( sf::Color::Red ); sf::Clock clock; while( window->isOpen() ) { mousePosition = sf::Mouse::getPosition( * window ); worldMousePosition = window->mapPixelToCoords( mousePosition ); currentTime = timeClock.getElapsedTime(); sf::Event event; while( window->pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window->close(); if( event.type == sf::Event::Resized ) { sf::View view; view.setSize( static_cast < float >( event.size.width ), static_cast < float >( event.size.height ) ); view.setCenter( view.getSize() / 2.f ); window->setView( view ); } else if( event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left ) { } else if( event.type == sf::Event::TextEntered ) { if( event.text.unicode < 128 ) { std::wstring line = lines[ cursorPosition.y ]->getString().toWideString(); if( event.text.unicode == '\b' ) { if( line.size() > 0 && cursorPosition != sf::Vector2i( 0, 0 ) ) { if( cursorPosition.x > 0 ) { line.erase( cursorPosition.x - 1, 1 ); cursorPosition.x -= 1; } else { } } } else if( event.text.unicode == 32 ) { line.insert( cursorPosition.x, 1, ' ' ); cursorPosition.x += 1; } else if( event.text.unicode == 13 ) { line.insert( cursorPosition.x, 1, '\n' ); cursorPosition.x += 1; } else { line.insert( cursorPosition.x, 1, char( event.text.unicode ) ); lines[ cursorPosition.y ]->setString( line ); cursorPosition.x += 1; } lines[ cursorPosition.y ]->setString( line ); } cursorPositioning(); } else if( event.type == sf::Event::KeyPressed ) { if( event.key.code == sf::Keyboard::Left ) { if( cursorPosition.x > 0 ) cursorPosition.x -= 1; else if( cursorPosition.y > 0 ) { cursorPosition.y -= 1; cursorPosition.x = lines[ cursorPosition.y ]->getString().toWideString().size(); } } else if( event.key.code == sf::Keyboard::Right ) { if( cursorPosition.x < lines[ cursorPosition.y ]->getString().getSize() ) cursorPosition.x += 1; else if( cursorPosition.y < lines.size() - 1 ) { cursorPosition.x = 0; cursorPosition.y += 1; } } else if( event.key.code == sf::Keyboard::Up ) { setCursorUp(); } else if( event.key.code == sf::Keyboard::Down ) { setCursorDown(); } cursorPositioning(); } } window->clear( backgroundColor ); for( auto & line: lines ) window->draw( * line ); if( std::fmod( currentTime.asSeconds(), 0.6f ) < 0.3f ) window->draw( cursor ); window->display(); } }
|
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-06-03 19:51:24 Napisałem taki Backspace ale też nie działa. Źle pozycjonuje linie po enterze: if( event.text.unicode == '\b' ) { if( line.size() > 0 && cursorPosition != sf::Vector2i( 0, 0 ) ) { if( cursorPosition.x > 0 && cursorPosition.y > 0 ) { std::wstring prev_line = lines[ cursorPosition.y - 1 ]->getString().toWideString(); std::wstring current_line = lines[ cursorPosition.y ]->getString().toWideString(); std::vector < sf::Text * > new_lines = wrap_text( prev_line + current_line ); delete lines[ cursorPosition.y - 1 ]; delete lines[ cursorPosition.y ]; lines[ cursorPosition.y - 1 ] = new_lines[ 0 ]; lines[ cursorPosition.y ] = new_lines[ 1 ]; } } }
|
|
termistor |
» 2025-06-03 20:00:56 Funkcja wrap_text w przykładzie, który wkleiłeś, zwraca void i tylko ustawia obiekty lines na bazie podanej w całości treści. Kiedy w metodzie obsługi Backspace próbowałeś wywołać: std::vector<sf::Text*> new_lines = wrap_text(prev_line + current_line);
to ani nie zwracałeś niczego z wrap_text, ani nie aktualizowałeś globalnego documentText. Dlatego nic się nie zmieniało albo linie były źle przeliczane |
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-06-03 20:02:38 Mój błąd zapomniałem dodać, że zmieniłem funkcję wrap_text i teraz zwraca listę std::vector < st::Text* >. Przesuwa mi linię w dół ale pozostałych linii już nie. Nie wiem dlaczego. #include <SFML/Graphics.hpp> #include <iostream> #include <vector> #include <filesystem>
sf::RenderWindow * window;
sf::Font font; short characterSize; sf::Color textColor; sf::Color backgroundColor;
std::vector < sf::Text * > lines;
sf::Vector2i mousePosition; sf::Vector2f worldMousePosition;
sf::RectangleShape cursor; sf::Vector2i cursorPosition = sf::Vector2i( 0, 0 );
sf::Clock timeClock; sf::Time currentTime;
std::vector < sf::Text * > wrap_text( std::wstring text ) { std::vector < sf::Text * > lines; std::wstring line = L""; std::wstring word = L""; std::cout << window->getSize().x << "\n"; for( auto & character: text ) { if( character == L'\n' ) { if( sf::Text( line + word, font, characterSize ).getGlobalBounds().width > window->getSize().x ) { lines.push_back( new sf::Text( line, font, characterSize ) ); line = word; word = L""; } else { lines.push_back( new sf::Text( line + word, font, characterSize ) ); line = L""; word = L""; } } else if( character == L' ' || character == L'\t' ) { if( sf::Text( line + word, font, characterSize ).getGlobalBounds().width > window->getSize().x ) { lines.push_back( new sf::Text( line, font, characterSize ) ); line = word + character; word = L""; } else { line = line + word + character; word = L""; } } else word = word + character; } if( line != L"" || word != L"" ) { lines.push_back( new sf::Text( line + word, font, characterSize ) ); } for( int i = 0; i < lines.size(); i++ ) { lines[ i ]->setPosition( sf::Vector2f( 0, i * font.getLineSpacing( characterSize ) ) ); lines[ i ]->setFillColor( textColor ); } return lines; }
void setCursorUp() { if( cursorPosition.y > 0 ) { float targetX = cursor.getGlobalBounds().left; cursorPosition.y -= 1; sf::Text * line = lines[ cursorPosition.y ]; size_t lineLength = line->getString().toWideString().size(); size_t closestIndex = 0; float closestDistance = std::abs( line->findCharacterPos( 0 ).x - targetX ); for( size_t i = 1; i <= lineLength; ++i ) { sf::Vector2f pos = line->findCharacterPos( i ); float distance = std::abs( pos.x - targetX ); if( distance < closestDistance ) { closestIndex = i; closestDistance = distance; } } cursorPosition.x = closestIndex; cursor.setPosition( line->findCharacterPos( closestIndex ) ); } } void setCursorDown() { if( cursorPosition.y < lines.size() - 1 ) { float targetX = cursor.getGlobalBounds().left; cursorPosition.y += 1; sf::Text * line = lines[ cursorPosition.y ]; size_t lineLength = line->getString().toWideString().size(); size_t closestIndex = 0; float closestDistance = std::abs( line->findCharacterPos( 0 ).x - targetX ); for( size_t i = 1; i <= lineLength; ++i ) { sf::Vector2f pos = line->findCharacterPos( i ); float distance = std::abs( pos.x - targetX ); if( distance < closestDistance ) { closestIndex = i; closestDistance = distance; } } cursorPosition.x = closestIndex; cursor.setPosition( line->findCharacterPos( closestIndex ) ); } }
void cursorPositioning() { if( lines.size() == 0 ) { cursor.setPosition( 0, 0 ); return; } float line_length = lines[ cursorPosition.y ]->getString().toWideString().size(); sf::Vector2f pos; pos.x = lines[ cursorPosition.y ]->findCharacterPos( cursorPosition.x ).x; pos.y = cursorPosition.y * font.getLineSpacing( characterSize ); cursor.setPosition( pos ); }
int main() { sf::View view( sf::FloatRect( 0, 0, 480, 640 ) ); window = new sf::RenderWindow( sf::VideoMode( view.getSize().x, view.getSize().y ), "Easy Notepad v2", sf::Style::Titlebar | sf::Style::Close ); font = sf::Font(); font.loadFromFile( "arial.ttf" ); characterSize = 17; textColor = sf::Color( 192, 192, 192 ); backgroundColor = sf::Color( 48, 48, 48 ); lines = wrap_text( L"Gracz najpierw zagaduje handlarza gdyż ten jest najbliżej. Handlarz oferuje skórzane ubranie w zamian za dostarczenie kilku skór od myśliwego, " L"którego gracz mijał wcześniej. Zielarka da graczowi trochę złota w zamian za przyniesienie kilku roślin leczniczych. " L"U kowala gracz może zakupić oręż - zwyczajny prosty miecz gdyż jest to niewprawiony kowal w miecznictwie. " L"Zaś do wieży mędrca nie da się dostać. Gracz rusza spowrotem do myśliwego po skóry, lecz ten jest nieufny, " L"ale ostatecznie zgadza się i daje graczowi skóry.\n" L"Gracz wraca ze skórami do handlarza i odbiera nowe ubranie \"skórzane kurtka\" oraz \"skórzane spodnie\"." L"Handlarz jednak jeszcze jedno zadanie ma dla gracza. Dostawa towarów ze wschodu się opóźnia i trzeba sprawdzić " L"co się z nią stało i tak gracz rusza z kolejnym zadaniem \"spóźniona dostawa\"." ); std::cout << "Current directory: " << std::filesystem::current_path() << std::endl; cursor = sf::RectangleShape( sf::Vector2f( 2, characterSize ) ); cursor.setFillColor( sf::Color::Red ); sf::Clock clock; while( window->isOpen() ) { mousePosition = sf::Mouse::getPosition( * window ); worldMousePosition = window->mapPixelToCoords( mousePosition ); currentTime = timeClock.getElapsedTime(); sf::Event event; while( window->pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window->close(); if( event.type == sf::Event::Resized ) { sf::View view; view.setSize( static_cast < float >( event.size.width ), static_cast < float >( event.size.height ) ); view.setCenter( view.getSize() / 2.f ); window->setView( view ); } else if( event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left ) { } else if( event.type == sf::Event::TextEntered ) { if( event.text.unicode < 128 ) { std::wstring line = lines[ cursorPosition.y ]->getString().toWideString(); if( event.text.unicode == '\b' ) { if( line.size() > 0 && cursorPosition != sf::Vector2i( 0, 0 ) ) { if( cursorPosition.x > 0 && cursorPosition.y > 0 ) { std::wstring prev_line = lines[ cursorPosition.y - 1 ]->getString().toWideString(); std::wstring current_line = lines[ cursorPosition.y ]->getString().toWideString(); std::vector < sf::Text * > new_lines = wrap_text( prev_line + L"\n" + current_line ); delete lines[ cursorPosition.y - 1 ]; delete lines[ cursorPosition.y ]; lines[ cursorPosition.y - 1 ] = new_lines[ 0 ]; lines[ cursorPosition.y ] = new_lines[ 1 ]; } } } else if( event.text.unicode == 32 ) { line.insert( cursorPosition.x, 1, ' ' ); cursorPosition.x += 1; } else if( event.text.unicode == 13 ) { line.insert( cursorPosition.x, 1, '\n' ); cursorPosition.x += 1; } else { line.insert( cursorPosition.x, 1, char( event.text.unicode ) ); lines[ cursorPosition.y ]->setString( line ); cursorPosition.x += 1; } lines[ cursorPosition.y ]->setString( line ); std::wstring text = L""; for( auto & line: lines ) text = text + line->getString().toWideString(); wrap_text( text ); } cursorPositioning(); } else if( event.type == sf::Event::KeyPressed ) { if( event.key.code == sf::Keyboard::Left ) { if( cursorPosition.x > 0 ) cursorPosition.x -= 1; else if( cursorPosition.y > 0 ) { cursorPosition.y -= 1; cursorPosition.x = lines[ cursorPosition.y ]->getString().toWideString().size(); } } else if( event.key.code == sf::Keyboard::Right ) { if( cursorPosition.x < lines[ cursorPosition.y ]->getString().getSize() ) cursorPosition.x += 1; else if( cursorPosition.y < lines.size() - 1 ) { cursorPosition.x = 0; cursorPosition.y += 1; } } else if( event.key.code == sf::Keyboard::Up ) { setCursorUp(); } else if( event.key.code == sf::Keyboard::Down ) { setCursorDown(); } cursorPositioning(); } } window->clear( backgroundColor ); for( auto & line: lines ) window->draw( * line ); if( std::fmod( currentTime.asSeconds(), 0.6f ) < 0.3f ) window->draw( cursor ); window->display(); } }
|
|
nanoant20 |
» 2025-06-03 20:09:44 mam kilka uwag co do projektu "Easy-Notepad" umieszczonego na github ponieważ zamknąłeś temat [cmake] Jak napisać cmake ? zrobię trochę off-topic , możesz to zostawić tak, jak jest – Ty decydujesz, co zrobić ponieważ nie używam MSVS tylko g++ / clang , po użyciu komendy konfiguracji procesu budowania projektu cmake .. -G "MinGW Makefiles" dostaje error: \Easy-Notepad-main\EasyNotepad\main.cpp: In function 'int main()': C:\Users\3195\Downloads\Easy-Notepad-main\EasyNotepad\main.cpp:444:18: error: 'fmod' is not a member of 'std' 444 | if (std::fmod(currentTime.asSeconds(), 0.6f) < 0.3f) | ^~~~
O "holy moly" error: main.cpp:444:18: w linijce if( std::fmod( currentTime.asSeconds(), 0.6f ) < 0.3f ) a) w innym wątku poruszyłem temat , że kompilator MSVS dołącza nagłówki w sposób ukryty error wskazuje na brak biblioteki < cmath > b) Wykonujesz porównanie zmiennoprzecinkowe, co jest problematyczne ponieważ liczby zmiennoprzecinkowe posiadają reprezentację przybliżoną Ponieważ do pisania swoich aplikacji używasz Visual Studio 2022 uważam że powinieneś o tym powiadomić swoich potencjalnych użytkowników, na pewno będzie ta uwaga przydatna przy Twoim 2 projekcie "Editor-RPG2D" teraz uwagi jeśli chodzi o CMakeList.txt Jeżeli użytkownik pobrał SFML, a musi to zrobić żeby skompilować sobie program, inaczej nie dołączy, np. #include <SFML/Graphics.hpp> to po co ten katalog / folder "libs" z .dll'kami sfml'a ? Sam piszesz w punkcie 2. Pobierz bibliotekę SFML i umieść ją w katalogu C:\SFML-2.6.2 więc biblioteki te powinny być kopiowane z powyższego katalagu / folderu Osobiście uważam że poniższa część to bardzo zła praktyka. Powoduje, że ten projekt można zbudować tylko na komputerze z identyczną konfigurację jak twoja Twoje ręczne ustawienie, powinieneś zastąpić ustawiając ścieżkę do katalogu głównego SFML i potem zdefiniować sobie jakąś zmienną, za pomocą której będziesz się odwoływał, np. coś takiego "${SFML_BIN}/sfml-graphics-d-2.dll" set(DEBUG_DLLS libs/sfml-graphics-d-2.dll libs/sfml-system-d-2.dll libs/sfml-window-d-2.dll )
set(RELEASE_DLLS libs/sfml-graphics-2.dll libs/sfml-system-2.dll libs/sfml-window-2.dll )
# copy files to Release foreach(ASSET ${RELEASE_DLLS}) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/Release COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/EasyNotepad/${ASSET} ${CMAKE_BINARY_DIR}/Release COMMENT "Copying ${ASSET} to Release folder" VERBATIM CONFIGURATIONS Release ) endforeach()
# copy files to Debug foreach(ASSET ${DEBUG_DLLS}) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/Debug COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/EasyNotepad/${ASSET} ${CMAKE_BINARY_DIR}/Debug COMMENT "Copying ${ASSET} to Debug folder" VERBATIM CONFIGURATIONS Debug ) endforeach()
dalej jeżeli chodzi o CMakeList.txt budując program użytkownik może wybierać / zadecydować czy chce zbudować Release czy Debug # add the executable add_executable(${PROJECT_NAME} EasyNotepad/main.cpp)
powyższa linijka decyduje że program buduje się z oknem konsoli cmd , nawet jeżeli uesr będzie budował projekt z flagą cmake --build . --config Release dostanie w gratisie okno cmd dalej, masz tam pętlę, które tworzą katalogi / foldery dla Release i Debug. Trafiają tam dll'ki sfml, czcionka , jest to w porządku , ale sam program "EasyNotepad.exe" chyba już tam nie trafia ? tylko się buduje w katalogu / folderze "build" Sam "program.exe" nie uruchomi się bez tych dll'ek , chyba że ktoś w systemie dodał SMFL do zmiennej PATH nie wiem czy działa w MSVS Visual Studio, ale zmień na: # set the Debugger Working Directory in Visual Studio (Debug/Release) set_target_properties(${PROJECT_NAME} PROPERTIES # VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIG>" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIG>" )
Testowanie oraz debugowanie jest kluczowym elementem i fundamentem pracy programisty , który zapewnia jakość. Zalecam przeprowadzanie dokładnych testów na każdym etapie budowy. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-06-03 20:16:35 OK. Poprawię cMake jak tylko napiszę Notepad v2. Wtedy ten pierwszy skasuję i zabiorę się za cMake. Szczerze mówiąc to pierwszy raz pisałem cmake i dlatego tak słabo. Napisałem tak jak doradził ChatGPT i widocznie nie jest w tym zbyt dobry .. Nadal mam problem z backspace (// backspace). Już poprawnie usuwa znak ze środka linii, ale nadal niepoprawnie gdy kasujemy znak z początku linii. Liczę na waszą pomoc :-) #include <SFML/Graphics.hpp> #include <iostream> #include <vector> #include <cmath> #include <filesystem>
sf::RenderWindow * window;
sf::Font font; short characterSize; sf::Color textColor; sf::Color backgroundColor;
std::vector < sf::Text * > lines;
sf::Vector2i mousePosition; sf::Vector2f worldMousePosition;
sf::RectangleShape cursor; sf::Vector2i cursorPosition = sf::Vector2i( 0, 0 );
sf::Clock timeClock; sf::Time currentTime;
std::vector < sf::Text * > wrap_text( std::wstring text ) { std::vector < sf::Text * > lines; std::wstring line = L""; std::wstring word = L""; std::cout << window->getSize().x << "\n"; for( auto & character: text ) { if( character == L'\n' ) { if( sf::Text( line + word, font, characterSize ).getGlobalBounds().width > window->getSize().x ) { lines.push_back( new sf::Text( line, font, characterSize ) ); line = word; word = L""; } else { lines.push_back( new sf::Text( line + word, font, characterSize ) ); line = L""; word = L""; } } else if( character == L' ' || character == L'\t' ) { if( sf::Text( line + word, font, characterSize ).getGlobalBounds().width > window->getSize().x ) { lines.push_back( new sf::Text( line, font, characterSize ) ); line = word + character; word = L""; } else { line = line + word + character; word = L""; } } else word = word + character; } if( line != L"" || word != L"" ) { lines.push_back( new sf::Text( line + word, font, characterSize ) ); } for( int i = 0; i < lines.size(); i++ ) { lines[ i ]->setPosition( sf::Vector2f( 0, i * font.getLineSpacing( characterSize ) ) ); lines[ i ]->setFillColor( textColor ); } return lines; }
void setCursorUp() { if( cursorPosition.y > 0 ) { float targetX = cursor.getGlobalBounds().left; cursorPosition.y -= 1; sf::Text * line = lines[ cursorPosition.y ]; size_t lineLength = line->getString().toWideString().size(); size_t closestIndex = 0; float closestDistance = std::abs( line->findCharacterPos( 0 ).x - targetX ); for( size_t i = 1; i <= lineLength; ++i ) { sf::Vector2f pos = line->findCharacterPos( i ); float distance = std::abs( pos.x - targetX ); if( distance < closestDistance ) { closestIndex = i; closestDistance = distance; } } cursorPosition.x = closestIndex; cursor.setPosition( line->findCharacterPos( closestIndex ) ); } } void setCursorDown() { if( cursorPosition.y < lines.size() - 1 ) { float targetX = cursor.getGlobalBounds().left; cursorPosition.y += 1; sf::Text * line = lines[ cursorPosition.y ]; size_t lineLength = line->getString().toWideString().size(); size_t closestIndex = 0; float closestDistance = std::abs( line->findCharacterPos( 0 ).x - targetX ); for( size_t i = 1; i <= lineLength; ++i ) { sf::Vector2f pos = line->findCharacterPos( i ); float distance = std::abs( pos.x - targetX ); if( distance < closestDistance ) { closestIndex = i; closestDistance = distance; } } cursorPosition.x = closestIndex; cursor.setPosition( line->findCharacterPos( closestIndex ) ); } }
void cursorPositioning() { if( lines.size() == 0 ) { cursor.setPosition( 0, 0 ); return; } float line_length = lines[ cursorPosition.y ]->getString().toWideString().size(); sf::Vector2f pos; pos.x = lines[ cursorPosition.y ]->findCharacterPos( cursorPosition.x ).x; pos.y = cursorPosition.y * font.getLineSpacing( characterSize ); cursor.setPosition( pos ); }
int main() { sf::View view( sf::FloatRect( 0, 0, 480, 640 ) ); window = new sf::RenderWindow( sf::VideoMode( view.getSize().x, view.getSize().y ), "Easy Notepad v2", sf::Style::Titlebar | sf::Style::Close ); font = sf::Font(); font.loadFromFile( "arial.ttf" ); characterSize = 17; textColor = sf::Color( 192, 192, 192 ); backgroundColor = sf::Color( 48, 48, 48 ); lines = wrap_text( L"Gracz najpierw zagaduje handlarza gdyż ten jest najbliżej. Handlarz oferuje skórzane ubranie w zamian za dostarczenie kilku skór od myśliwego, " L"którego gracz mijał wcześniej. Zielarka da graczowi trochę złota w zamian za przyniesienie kilku roślin leczniczych. " L"U kowala gracz może zakupić oręż - zwyczajny prosty miecz gdyż jest to niewprawiony kowal w miecznictwie. " L"Zaś do wieży mędrca nie da się dostać. Gracz rusza spowrotem do myśliwego po skóry, lecz ten jest nieufny, " L"ale ostatecznie zgadza się i daje graczowi skóry.\n" L"Gracz wraca ze skórami do handlarza i odbiera nowe ubranie \"skórzane kurtka\" oraz \"skórzane spodnie\"." L"Handlarz jednak jeszcze jedno zadanie ma dla gracza. Dostawa towarów ze wschodu się opóźnia i trzeba sprawdzić " L"co się z nią stało i tak gracz rusza z kolejnym zadaniem \"spóźniona dostawa\"." ); std::cout << "Current directory: " << std::filesystem::current_path() << std::endl; cursor = sf::RectangleShape( sf::Vector2f( 2, characterSize ) ); cursor.setFillColor( sf::Color::Red ); sf::Clock clock; while( window->isOpen() ) { mousePosition = sf::Mouse::getPosition( * window ); worldMousePosition = window->mapPixelToCoords( mousePosition ); currentTime = timeClock.getElapsedTime(); sf::Event event; while( window->pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window->close(); if( event.type == sf::Event::Resized ) { sf::View view; view.setSize( static_cast < float >( event.size.width ), static_cast < float >( event.size.height ) ); view.setCenter( view.getSize() / 2.f ); window->setView( view ); } else if( event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left ) { } else if( event.type == sf::Event::TextEntered ) { if( event.text.unicode < 128 ) { std::wstring line = lines[ cursorPosition.y ]->getString().toWideString(); if( event.text.unicode == '\b' ) { if( line.size() > 0 && cursorPosition != sf::Vector2i( 0, 0 ) ) { if( cursorPosition.x == 0 && cursorPosition.y > 0 ) { std::wstring prev_line = lines[ cursorPosition.y - 1 ]->getString().toWideString(); std::wstring current_line = lines[ cursorPosition.y ]->getString().toWideString(); std::vector < sf::Text * > new_lines = wrap_text( prev_line + current_line ); delete lines[ cursorPosition.y - 1 ]; delete lines[ cursorPosition.y ]; lines[ cursorPosition.y - 1 ] = new_lines[ 0 ]; lines[ cursorPosition.y ] = new_lines[ 1 ]; } else if( cursorPosition.x > 0 ) { line.erase( cursorPosition.x - 1, 1 ); cursorPosition.x -= 1; } } } else if( event.text.unicode == 32 ) { line.insert( cursorPosition.x, 1, ' ' ); cursorPosition.x += 1; } else if( event.text.unicode == 13 ) { line.insert( cursorPosition.x, 1, '\n' ); cursorPosition.y += 1; cursorPosition.x = 0; } else { line.insert( cursorPosition.x, 1, char( event.text.unicode ) ); lines[ cursorPosition.y ]->setString( line ); cursorPosition.x += 1; } lines[ cursorPosition.y ]->setString( line ); std::wstring text = L""; for( auto & line: lines ) text = text + line->getString().toWideString() + L"\n"; lines = wrap_text( text ); } cursorPositioning(); } else if( event.type == sf::Event::KeyPressed ) { if( event.key.code == sf::Keyboard::Left ) { if( cursorPosition.x > 0 ) cursorPosition.x -= 1; else if( cursorPosition.y > 0 ) { cursorPosition.y -= 1; cursorPosition.x = lines[ cursorPosition.y ]->getString().toWideString().size(); } } else if( event.key.code == sf::Keyboard::Right ) { if( cursorPosition.x < lines[ cursorPosition.y ]->getString().getSize() ) cursorPosition.x += 1; else if( cursorPosition.y < lines.size() - 1 ) { cursorPosition.x = 0; cursorPosition.y += 1; } } else if( event.key.code == sf::Keyboard::Up ) { setCursorUp(); } else if( event.key.code == sf::Keyboard::Down ) { setCursorDown(); } cursorPositioning(); } } window->clear( backgroundColor ); for( auto & line: lines ) window->draw( * line ); if( std::fmod( currentTime.asSeconds(), 0.6f ) < 0.3f ) window->draw( cursor ); window->display(); } }
|
|
termistor |
» 2025-06-03 21:36:01 Witaj! Dziękuję za szczegółowy opis problemu. Poniżej znajdziesz analizę i propozycje rozwiązania problemów z Twoim kodem: 1. Problem z backspace przy wierszachW klasie TextEditor występuje błąd logiki obsługi backspace w przypadku scalania linii. Gdy kursor znajduje się na początku linii (x=0), kod próbuje usunąć poprzednią linię i połączyć ją z bieżącą. Jednak po scaleniu linii, wynikowy wektor new_lines może zawierać więcej niż 2 elementy (jeśli tekst nie mieści się w jednej linii). Obecnie kod próbuje przypisać tylko dwa pierwsze elementy: lines[cursorPosition.y - 1] = new_lines[0]; lines[cursorPosition.y] = new_lines[1];
To prowadzi do out-of-bounds przy dostępie do new_lines[1] , jeśli wektor new_lines ma mniej niż 2 elementy. Proponowane rozwiązanie: - Zastąp fragment: lines[cursorPosition.y - 1] = new_lines[0]; lines[cursorPosition.y] = new_lines[1];
- Nowym kodem: // Usuń stare linie for (auto& line : lines) { delete line; } lines.clear();
// Dodaj nowe linie lines.insert(lines.begin(), new_lines.begin(), new_lines.end());
- Dodatkowo, po scaleniu linii, zaktualizuj pozycję kursora: cursorPosition.x = 0; cursorPosition.y = 0;
2. Problem z CMake i SFMLTwój CMakeLists.txt powinien zawierać: cmake_minimum_required(VERSION 3.10) project(TextEditor)
find_package(SFML REQUIRED COMPONENTS graphics window system)
add_executable(TextEditor main.cpp)
target_link_libraries(TextEditor SFML::graphics SFML::window SFML::system)
Upewnij się, że: - SFML jest zainstalowany w systemie (np. przez apt-get install libsfml-dev na Linuxie) - Zmienna środowiskowa SFML_DIR wskazuje na katalog z plikami cmake SFML (jeśli nie korzystasz z systemowej instalacji) - Plik arial.ttf znajduje się w tym samym katalogu co wykonywalny program 3. Błąd kompilacji: "undefined reference to 'main'"Ten błąd oznacza, że kompilator nie znalazł funkcji main() . Twoja implementacja zawiera pętlę główną w klasie TextEditor , ale brakuje konkretnego main.cpp z funkcją main() . Proponowany kod startowy: #include <SFML/Graphics.hpp> #include "TextEditor.h"
int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "Text Editor"); TextEditor editor(window); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } editor.handleInput(); editor.update(); editor.render(window); } return 0; }
4. Ulepszenia kodu- Zarządzanie pamięcią: Obecnie masz wycieki pamięci, ponieważ nie usuwasz wszystkich obiektów sf::Text* . Dodaj destruktor do klasy TextEditor : TextEditor::~TextEditor() { for (auto* line : lines_) { delete line; } }
- Obsługa kursora: Aktualna implementacja nie zaktualizuje pozycji kursora po scaleniu linii. Dodaj po scaleniu linii: cursorPosition.y = 0; cursorPosition.x = 0;
- Optymalizacja: Funkcja wrap_text może być wywoływana za często. Zastąp ją aktualizacją tylko zmodyfikowanych linii. 5. Dodatkowe sugestie- Dodaj obsługę klawiszy strzałek do nawigacji w obrębie linii - Wprowadź limit szybkości wpisywania (antispam) - Dodaj zapisywanie i wczytywanie plików - Ulepsz wygląd kursora (np. zmienna intensywność) Jeśli potrzebujesz pomocy z konkretnym aspektem działania programu, chętnie pomogę. Pamiętaj, że najlepiej testować zmiany krok po kroku, sprawdzając stan pamięci i poprawność indeksów tablic. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-06-03 21:41:01 No z tym kasowaniem linii to mi kasuje cały tekst a nie jedną linię :-/
|
|
« 1 » 2 3 |