From 1251d2f72baf7995fc980e903b865cd26c607575 Mon Sep 17 00:00:00 2001 From: pointer-to-bios Date: Tue, 23 Jul 2024 17:04:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=9F=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 39 ++++++ .gitignore | 6 + CMakeLists.txt | 31 +++++ LICENSE.md | 50 ++++++++ README.md | 105 +++++++++++++++ include/query.hpp | 8 ++ src/main.cpp | 267 +++++++++++++++++++++++++++++++++++++++ src/query.cpp | 1 + 8 files changed, 507 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 include/query.hpp create mode 100644 src/main.cpp create mode 100644 src/query.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..43dabfc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: [push, pull_request] + +defaults: + run: + shell: bash + +jobs: + build: + name: ${{ matrix.platform.name }} ${{ matrix.config.name }} + runs-on: ${{ matrix.platform.os }} + + strategy: + fail-fast: false + matrix: + platform: + - { name: Windows VS2019, os: windows-2019 } + - { name: Windows VS2022, os: windows-2022 } + - { name: Linux GCC, os: ubuntu-latest } + - { name: Linux Clang, os: ubuntu-latest, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } + - { name: macOS, os: macos-latest } + config: + - { name: Shared, flags: -DBUILD_SHARED_LIBS=TRUE } + - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE } + + steps: + - name: Install Linux Dependencies + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install libxrandr-dev libxcursor-dev libudev-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev + + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure + run: cmake -B build ${{matrix.platform.flags}} ${{matrix.config.flags}} + + - name: Build + run: cmake --build build --config Release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1c5e90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build +out +.cache +.idea +.vs +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e4d2a81 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.16) +project(CMakeSFMLProject LANGUAGES CXX) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) + +include(FetchContent) +FetchContent_Declare(SFML + GIT_REPOSITORY https://github.com/SFML/SFML.git + GIT_TAG 2.6.x) +FetchContent_MakeAvailable(SFML) + +FetchContent_Declare( + xlnt + GIT_REPOSITORY https://github.com/pointertobios/xlnt.git + GIT_TAG master) +FetchContent_MakeAvailable(xlnt) + +add_executable(main src/main.cpp) + +target_link_libraries(main PRIVATE sfml-graphics xlnt) +target_compile_features(main PRIVATE cxx_std_17) +target_include_directories(main PRIVATE include) + +if(WIN32) + add_custom_command( + TARGET main + COMMENT "Copy OpenAL DLL" + PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${SFML_SOURCE_DIR}/extlibs/bin/$,x64,x86>/openal32.dll $ + VERBATIM) +endif() diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..6a85499 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,50 @@ +# CMake SFML Project Licenses + +*This software is available under 2 licenses -- choose whichever you prefer.* + +## Public Domain + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +## MIT License + +Copyright (c) 2022 Lukas Drrenberger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1553a92 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# CMake SFML Project Template + +This repository template should allow for a fast and hassle-free kick start of your next SFML project using CMake. +Thanks to [GitHub's nature of templates](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template), you can fork this repository without inheriting its Git history. + +The template starts out very basic, but might receive additional features over time: + +- Basic CMake script to build your project and link SFML on any operating system +- Basic [GitHub Actions](https://github.com/features/actions) script for all major platforms + +## How to Use + +1. Install Git and CMake. Use your system's package manager if available. +1. Follow the above instructions about how to use GitHub's project template feature to create your own project. +1. Clone your new GitHub repo and open the repo in your text editor of choice. +1. Open [CMakeLists.txt](CMakeLists.txt). Rename the project and the executable to whatever name you want. +1. If you want to add or remove any .cpp files, change the source files listed in the [`add_executable`](CMakeLists.txt#L10) call in CMakeLists.txt to match the source files your project requires. If you plan on keeping the default main.cpp file then no changes are required. +1. If you use Linux, install SFML's dependencies using your system package manager. On Ubuntu and other Debian-based distributions you can use the following commands: + ``` + sudo apt update + sudo apt install \ + libxrandr-dev \ + libxcursor-dev \ + libudev-dev \ + libfreetype-dev \ + libopenal-dev \ + libflac-dev \ + libvorbis-dev \ + libgl1-mesa-dev \ + libegl1-mesa-dev + ``` +1. Configure and build your project. Most popular IDEs support CMake projects with very little effort on your part. + - [VS Code](https://code.visualstudio.com) via the [CMake extension](https://code.visualstudio.com/docs/cpp/cmake-linux) + - [Visual Studio](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170) + - [CLion](https://www.jetbrains.com/clion/features/cmake-support.html) + - [Qt Creator](https://doc.qt.io/qtcreator/creator-project-cmake.html) + + Using CMake from the command line is straightforward as well. + + For a single-configuration generator (typically the case on Linux and macOS): + ``` + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build + ``` + + For a multi-configuration generator (typically the case on Windows): + ``` + cmake -S . -B build + cmake --build build --config Release + ``` +1. Enjoy! + +## Upgrading SFML + +SFML is found via CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module. +FetchContent automatically downloads SFML from GitHub and builds it alongside your own code. +Beyond the convenience of not having to install SFML yourself, this ensures ABI compatability and simplifies things like specifying static versus shared libraries. + +Modifying what version of SFML you want is as easy as changing the [`GIT_TAG`](CMakeLists.txt#L7) argument. +Currently it uses the latest in-development version of SFML 2 via the `2.6.x` tag. +If you're feeling adventurous and want to give SFML 3 a try, use the `master` tag. +Beware, this requires changing your code to suit the modified API! +The nice folks in the [SFML community](https://github.com/SFML/SFML#community) can help you with that transition and the bugs you may encounter along the way. + +## But I want to... + +Modify CMake options by adding them as configuration parameters (with a `-D` flag) or by modifying the contents of CMakeCache.txt and rebuilding. + +### Use Static Libraries + +By default SFML builds shared libraries and this default is inherited by your project. +CMake's [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) option lets you pick static or shared libraries for the entire project. + +### Change Compilers + +See the variety of [`CMAKE__COMPILER`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html) options. +In particular you'll want to modify `CMAKE_CXX_COMPILER` to point to the C++ compiler you wish to use. + +### Change Compiler Optimizations + +CMake abstracts away specific optimizer flags through the [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) option. +By default this project recommends `Release` builds which enable optimizations. +Other build types include `Debug` builds which enable debug symbols but disable optimizations. +If you're using a multi-configuration generator (as is often the case on Windows), you can modify the [`CMAKE_CONFIGURATION_TYPES`](https://cmake.org/cmake/help/latest/variable/CMAKE_CONFIGURATION_TYPES.html#variable:CMAKE_CONFIGURATION_TYPES) option. + +### Change Generators + +While CMake will attempt to pick a suitable default generator, some systems offer a number of generators to choose from. +Ubuntu, for example, offers Makefiles and Ninja as two potential options. +For a list of generators, click [here](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). +To modify the generator you're using you must reconfigure your project providing a `-G` flag with a value corresponding to the generator you want. +You can't simply modify an entry in the CMakeCache.txt file unlike the above options. +Then you may rebuild your project with this new generator. + +## More Reading + +Here are some useful resources if you want to learn more about CMake: + +- [How to Use CMake Without the Agonizing Pain - Part 1](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-1.html) +- [How to Use CMake Without the Agonizing Pain - Part 2](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-2.html) +- [Better CMake YouTube series by Jefferon Amstutz](https://www.youtube.com/playlist?list=PL8i3OhJb4FNV10aIZ8oF0AA46HgA2ed8g) + +## License + +The source code is dual licensed under Public Domain and MIT -- choose whichever you prefer. diff --git a/include/query.hpp b/include/query.hpp new file mode 100644 index 0000000..da06fb9 --- /dev/null +++ b/include/query.hpp @@ -0,0 +1,8 @@ +#ifndef QUERY_HPP +#define QUERY_HPP + +class Query { + +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f2e0872 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include +#include + +int font_size = 15; +float width = 800.f; +float height = 600.f; + +const int border_size = 50; + +std::wstring intToWString(int value) +{ + std::wostringstream woss; + woss << value; + return woss.str(); +} + +std::wstring stringToWString(const std::string &str) +{ + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +std::string wstringToString(const std::wstring &wstr) +{ + std::wstring_convert> converter; + return converter.to_bytes(wstr); +} + +struct QueryResult +{ + std::wstring keyword; + std::wstring content; +}; + +bool containsIgnoreCase(const std::wstring &haystack, const std::wstring &needle) +{ + std::wstring lowercaseHaystack = haystack; + std::transform(lowercaseHaystack.begin(), lowercaseHaystack.end(), lowercaseHaystack.begin(), ::tolower); + std::wstring lowercaseNeedle = needle; + std::transform(lowercaseNeedle.begin(), lowercaseNeedle.end(), lowercaseNeedle.begin(), ::tolower); + return lowercaseHaystack.find(lowercaseNeedle) != std::wstring::npos; +} + +std::vector queryData(const std::wstring &keyword, const std::vector &data) +{ + std::vector results; + for (const auto &entry : data) + { + if (containsIgnoreCase(entry.keyword, keyword)) + { + results.push_back(entry); + } + } + return results; +} + +static int line_counter = 1; +// static int extra_line_counter = 0; +void setTextWithWrap(sf::Text &text, const std::wstring &content, const sf::Font &font, unsigned int characterSize, float maxWidth) +{ + line_counter = 1; + text.setString(L""); + std::wstring line; + float lineWidth = 0.f; + float lineHeight = static_cast(font.getLineSpacing(characterSize)); + for (auto &ch : content) + { + sf::Glyph glyph = font.getGlyph(ch, characterSize, false); + if (ch == L'\n') + { + line_counter++; + text.setString(text.getString() + line + L"\n"); + // text.setString(text.getString() + line + L"\n" + intToWString(line_counter) + L">>"); + line.clear(); + lineWidth = 0.f; + } + else + { + lineWidth += glyph.advance; + if (lineWidth > maxWidth) + { + line_counter++; + text.setString(text.getString() + line + L"\n"); + // text.setString(text.getString() + line + L"\n" + intToWString(line_counter) + L">>"); + // line_counter++; + line.clear(); + lineWidth = glyph.advance; + } + line += ch; + } + } + text.setString(text.getString() + line); +} + +int main() +{ + std::wstring resultStr; + float offset = 0.f; + + // ¶ÁÈ¡ExcelÎļþ + xlnt::workbook wb; + try + { + wb.load("data.xlsx"); + } + catch (const std::exception &e) + { + std::cerr << "Error loading Excel file: " << e.what() << std::endl; + return -1; + } + + xlnt::worksheet ws = wb.active_sheet(); + std::vector data; + for (auto row : ws.rows(false)) + { + QueryResult entry; + entry.keyword = stringToWString(row[0].to_string()); + entry.content = stringToWString(row[1].to_string()); + data.push_back(entry); + } + + sf::RenderWindow window(sf::VideoMode(width, height), L"查询"); + + sf::Font font; + if (!font.loadFromFile("msyh.ttc")) + { // ʹÓÃÖ§³ÖÖÐÎĵÄ×ÖÌåÎļþ + std::cerr << "Error loading font\n"; + return -1; + } + + // Input box + sf::RectangleShape inputBox(sf::Vector2f(400, 30)); + inputBox.setPosition(border_size, border_size); + inputBox.setSize(sf::Vector2f(width - border_size * 2 - 120, 30)); + inputBox.setFillColor(sf::Color::White); + inputBox.setOutlineColor(sf::Color::Black); + inputBox.setOutlineThickness(2); + + std::wstring inputText; + sf::Text inputTextBox; + inputTextBox.setFont(font); + inputTextBox.setCharacterSize(20); + inputTextBox.setFillColor(sf::Color::Black); + inputTextBox.setPosition(55, 55); + + // Button + sf::RectangleShape button(sf::Vector2f(100, 30)); + button.setPosition(width - border_size - button.getSize().x, 50); + button.setFillColor(sf::Color::Green); + button.setOutlineColor(sf::Color::Black); + button.setOutlineThickness(2); + + sf::Text buttonText; + buttonText.setFont(font); + buttonText.setString(L"查询"); + buttonText.setCharacterSize(20); + buttonText.setFillColor(sf::Color::Black); + buttonText.setPosition( + button.getPosition().x + button.getSize().x / 2 - // + buttonText.getCharacterSize() * buttonText.getString().getSize() / 2, + 52); + + // Query result area + sf::RectangleShape resultBox(sf::Vector2f(width * 7.f / 8 - 4, height * 5.f / 6)); + resultBox.setPosition(2, 2); + resultBox.setFillColor(sf::Color::White); + resultBox.setOutlineColor(sf::Color::Black); + resultBox.setOutlineThickness(2); + + sf::Text resultText; + resultText.setFont(font); + resultText.setCharacterSize(font_size); + resultText.setFillColor(sf::Color::Black); + resultText.setPosition(7, 0); + + sf::View resultView(sf::FloatRect(0.f, 0.f, width * 7.f / 8 - 2, height * 5.f / 6 - 100)); + resultView.setViewport(sf::FloatRect(1.f / 16, 100.f / height, 7.f / 8, 1.f)); + + while (window.isOpen()) + { + sf::Event event; + while (window.pollEvent(event)) + { + if (event.type == sf::Event::Closed) + window.close(); + if (event.type == sf::Event::TextEntered) + { + if (event.text.unicode == 8) + { // Backspace¼üµÄUnicodeÖµÊÇ8 + if (!inputText.empty()) + { + inputText.pop_back(); + inputTextBox.setString(inputText); + } + } + else + { + inputText += static_cast(event.text.unicode); + inputTextBox.setString(inputText); + } + } + if (event.type == sf::Event::MouseButtonPressed) + { + if (event.mouseButton.button == sf::Mouse::Left) + { + sf::Vector2i mousePos = sf::Mouse::getPosition(window); + if (button.getGlobalBounds().contains(mousePos.x, mousePos.y)) + { + // Perform query + std::vector results = queryData(inputText, data); + resultStr.clear(); + for (const auto &result : results) + { + resultStr += result.keyword + L"\n" + result.content + L"\n\n"; + } + } + } + } + if (event.type == sf::Event::MouseWheelScrolled) + { + if (event.mouseWheelScroll.wheel == sf::Mouse::VerticalWheel) + { + offset -= event.mouseWheelScroll.delta * 20.f; + } + } + // catch the resize events + if (event.type == sf::Event::Resized) + { + width = event.size.width; + height = event.size.height; + inputBox.setSize(sf::Vector2f(width - border_size * 2 - 120, 30)); + button.setPosition(width - border_size - button.getSize().x, 50); + buttonText.setPosition( + button.getPosition().x + button.getSize().x / 2 - // + buttonText.getCharacterSize() * buttonText.getString().getSize() / 2, + 52); + resultBox.setSize(sf::Vector2f(width - border_size * 2 - 4, height - 100.f - border_size - 4)); + resultView.setViewport(sf::FloatRect(border_size / width, 100.f / height, (width - border_size * 2) / width, (height - 100.f - border_size) / height)); + } + } + + setTextWithWrap(resultText, resultStr, font, font_size, width * 7.f / 8 - 14); + + window.setView(sf::View(sf::FloatRect(0.f, 0.f, width, height))); + window.clear(sf::Color(0x40, 0x80, 0x40)); + window.draw(inputBox); + window.draw(inputTextBox); + window.draw(button); + window.draw(buttonText); + + resultView.reset(sf::FloatRect(0.f, 0.f, width - border_size * 2, height - 100.f - border_size)); + window.setView(resultView); + window.draw(resultBox); + resultView.move(sf::Vector2f(0, offset)); + window.setView(resultView); + window.draw(resultText); + + window.display(); + } + + return 0; +} diff --git a/src/query.cpp b/src/query.cpp new file mode 100644 index 0000000..2905fc7 --- /dev/null +++ b/src/query.cpp @@ -0,0 +1 @@ +#include