├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── application.cpp ├── application.hpp ├── base_text_buffer.cpp ├── base_text_buffer.hpp ├── color.cpp ├── color.hpp ├── coord.cpp ├── coord.hpp ├── cpp_highlighter.cpp ├── cpp_highlighter.hpp ├── current_dir.cpp ├── current_dir.hpp ├── deja_vu_sans_mono.cpp ├── deja_vu_sans_mono.hpp ├── deliting_object.hpp ├── dialog.cpp ├── dialog.hpp ├── full_file_name.cpp ├── full_file_name.hpp ├── isearch_buffer.cpp ├── isearch_buffer.hpp ├── key_event.cpp ├── key_event.hpp ├── layout.cpp ├── layout.hpp ├── layoutable.cpp ├── layoutable.hpp ├── main.cpp ├── main_window.cpp ├── main_window.hpp ├── open_dialog.cpp ├── open_dialog.hpp ├── paint_device.hpp ├── paint_event.hpp ├── painter.cpp ├── painter.hpp ├── resize_event.hpp ├── save_dialog.cpp ├── save_dialog.hpp ├── screen.cpp ├── screen.hpp ├── status_bar.cpp ├── status_bar.hpp ├── tabs.cpp ├── tabs.hpp ├── text.txt ├── text_file.cpp ├── text_file.hpp ├── text_input_event.cpp ├── text_input_event.hpp ├── to_utf16.cpp ├── to_utf16.hpp ├── to_utf8.cpp ├── to_utf8.hpp ├── undo_stack.cpp ├── undo_stack.hpp ├── widget.cpp └── widget.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | *~ 15 | /tmp/* 16 | /texteditor 17 | /CMakeFiles/* 18 | /CMakeCache.txt 19 | /Makefile 20 | /cmake_install.cmake 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(texteditor) 4 | 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -g -Wall") 6 | 7 | aux_source_directory(. SOURCE_FILES) 8 | add_executable(texteditor ${SOURCE_FILES}) 9 | 10 | include(FindPkgConfig) 11 | 12 | pkg_search_module(SDL2 REQUIRED sdl2) 13 | include_directories(${SDL2_INCLUDE_DIRS}) 14 | target_link_libraries(texteditor ${SDL2_LIBRARIES}) 15 | 16 | pkg_search_module(SDL2TTF REQUIRED SDL2_ttf) 17 | include_directories(${SDL2TTF_INCLUDE_DIRS}) 18 | target_link_libraries(texteditor ${SDL2TTF_LIBRARIES}) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Anton Te 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Text Editor 2 | =========== 3 | 4 | As a C++ software developer I need a good text editor. 5 | 6 | The main goal of this project is to give to people oportunity to learn how to program in C++. 7 | 8 | Key features: 9 | 10 | - Performance 11 | - Linux/Mac OS X 12 | - Syntax Highlighting 13 | - C++ code compilation 14 | - Debugger 15 | - Easy code navigation 16 | - Work with git, hg and svn 17 | - Terminal emulator 18 | - Remote work via ssh 19 | - Keyboard driven 20 | - C++ plug-ins 21 | - Tabs 22 | 23 | Possible dependencies: 24 | 25 | - C++ 11 language 26 | - SDL 27 | - OpenGL 28 | - FreeType 29 | -------------------------------------------------------------------------------- /application.cpp: -------------------------------------------------------------------------------- 1 | #include "to_utf16.hpp" 2 | #include "paint_event.hpp" 3 | #include "resize_event.hpp" 4 | #include "key_event.hpp" 5 | #include "text_input_event.hpp" 6 | #include "application.hpp" 7 | #include "widget.hpp" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | Application *Application::instance_ = nullptr; 16 | 17 | Application::Application(int &, char **): 18 | focusWidget_(nullptr), 19 | needUpdateWithoutRedraw_(nullptr), 20 | lastUpdate_(0) 21 | { 22 | if (instance_ != nullptr) 23 | throw std::runtime_error("The program can have only one instance of Application"); 24 | instance_ = this; 25 | if (SDL_Init(SDL_INIT_EVERYTHING) != 0) 26 | throw std::runtime_error("SDL_Init Error: " + std::string(SDL_GetError())); 27 | if (TTF_Init() != 0) 28 | throw std::runtime_error("TTF_Init error: " + std::string(SDL_GetError())); 29 | } 30 | 31 | Application::~Application() 32 | { 33 | SDL_Quit(); 34 | instance_ = nullptr; 35 | } 36 | 37 | int Application::exec() 38 | { 39 | bool done = false; 40 | while (!done) 41 | { 42 | SDL_Event e; 43 | if (SDL_WaitEvent(&e)) 44 | { 45 | switch (e.type) 46 | { 47 | case SDL_WINDOWEVENT: 48 | { 49 | Widget *w = widgetByWindowId(e.window.windowID); 50 | switch (e.window.event) 51 | { 52 | case SDL_WINDOWEVENT_SHOWN: 53 | std::cout << "Window " << e.window.windowID << " shown" << std::endl; 54 | break; 55 | case SDL_WINDOWEVENT_HIDDEN: 56 | std::cout << "Window " << e.window.windowID << " hidden" << std::endl; 57 | break; 58 | case SDL_WINDOWEVENT_EXPOSED: 59 | { 60 | needUpdateWithoutRedraw_ = w; 61 | break; 62 | } 63 | case SDL_WINDOWEVENT_MOVED: 64 | break; 65 | case SDL_WINDOWEVENT_RESIZED: 66 | { 67 | w->resize(e.window.data1, e.window.data2); 68 | #if __APPLE__==1 69 | SDL_RenderPresent(w->renderer_); // hack for MacOS X 70 | #endif 71 | break; 72 | } 73 | case SDL_WINDOWEVENT_MINIMIZED: 74 | std::cout << "Window " << e.window.windowID << " minimized" << std::endl; 75 | break; 76 | case SDL_WINDOWEVENT_MAXIMIZED: 77 | std::cout << "Window " << e.window.windowID << " maximized" << std::endl; 78 | break; 79 | case SDL_WINDOWEVENT_RESTORED: 80 | std::cout << "Window " << e.window.windowID << " restored" << std::endl; 81 | break; 82 | case SDL_WINDOWEVENT_ENTER: 83 | std::cout << "Mouse entered window " << e.window.windowID << std::endl; 84 | break; 85 | case SDL_WINDOWEVENT_LEAVE: 86 | std::cout << "Mouse left window " << e.window.windowID << std::endl; 87 | break; 88 | case SDL_WINDOWEVENT_FOCUS_GAINED: 89 | std::cout << "Window " << e.window.windowID << " gained keyboard focus" << std::endl; 90 | break; 91 | case SDL_WINDOWEVENT_FOCUS_LOST: 92 | std::cout << "Window " << e.window.windowID << " lost keyboard focus" << std::endl; 93 | break; 94 | case SDL_WINDOWEVENT_CLOSE: 95 | std::cout << "Window " << e.window.windowID << " closed" << std::endl; 96 | break; 97 | default: 98 | std::cout << "Window " << e.window.windowID << " got unknown event " << static_cast(e.window.event) << std::endl; 99 | break; 100 | } 101 | break; 102 | } 103 | case SDL_KEYDOWN: 104 | { 105 | KeyEvent ke { static_cast(e.key.keysym.sym), SDL_GetModState(), static_cast(e.key.repeat) }; 106 | auto w = focusWidget(); 107 | if (!w) 108 | w = widgetByWindowId(e.key.windowID); 109 | else if (w->ancestor() != widgetByWindowId(e.key.windowID)) 110 | { 111 | std::cerr << "Unknown windowID " << e.key.windowID << std::endl; 112 | break; 113 | } 114 | while (w) 115 | { 116 | if (w->keyPressEvent(ke)) 117 | break; 118 | w = w->parent(); 119 | } 120 | break; 121 | } 122 | case SDL_KEYUP: 123 | { 124 | KeyEvent ke { static_cast(e.key.keysym.sym), SDL_GetModState(), static_cast(e.key.repeat) }; 125 | auto w = focusWidget(); 126 | if (!w) 127 | w = widgetByWindowId(e.key.windowID); 128 | else if (w->ancestor() != widgetByWindowId(e.key.windowID)) 129 | { 130 | std::cerr << "Unknown windowID " << e.key.windowID << std::endl; 131 | break; 132 | } 133 | 134 | while (w) 135 | { 136 | if (w->keyReleaseEvent(ke)) 137 | break; 138 | w = w->parent(); 139 | } 140 | break; 141 | } 142 | case SDL_TEXTINPUT: 143 | { 144 | TextInputEvent tie { toUtf16(e.text.text) }; 145 | auto w = focusWidget(); 146 | if (!w) 147 | w = widgetByWindowId(e.key.windowID); 148 | else if (w->ancestor() != widgetByWindowId(e.key.windowID)) 149 | { 150 | std::cerr << "Unknown windowID " << e.key.windowID << std::endl; 151 | break; 152 | } 153 | 154 | while (w) 155 | { 156 | if (w->textInputEvent(tie)) 157 | break; 158 | w = w->parent(); 159 | } 160 | break; 161 | } 162 | case SDL_QUIT: 163 | done = true; 164 | break; 165 | } 166 | } 167 | const auto isEmpty = (SDL_PeepEvents(&e, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 0); 168 | if (isEmpty || SDL_GetTicks() > lastUpdate_ + 1000 / 60) 169 | { 170 | auto x = SDL_GetTicks(); 171 | for (auto w: widgetList_) 172 | if (w->needRepaint()) 173 | { 174 | PaintEvent e; 175 | w->internalPaint(e); 176 | } 177 | if (needUpdateWithoutRedraw_) 178 | { 179 | needUpdateWithoutRedraw_->updateWithoutRedraw(); 180 | needUpdateWithoutRedraw_ = nullptr; 181 | } 182 | lastUpdate_ = SDL_GetTicks(); 183 | std::cout << "Update time: " << lastUpdate_ - x << " " << 184 | (lastUpdate_ - x > 0 ? 1000 / (lastUpdate_ - x) : 999)<< "fps" << std::endl; 185 | } 186 | for (auto obj: deletingObjects_) 187 | delete obj; 188 | deletingObjects_.clear(); 189 | } 190 | return 0; 191 | } 192 | 193 | Application *Application::instance() 194 | { 195 | return instance_; 196 | } 197 | 198 | void Application::addWidget(Widget *w) 199 | { 200 | widgetList_.push_back(w); 201 | } 202 | 203 | void Application::removeWidget(Widget *w) 204 | { 205 | widgetList_.erase(std::remove(begin(widgetList_), end(widgetList_), w), end(widgetList_)); 206 | } 207 | 208 | Widget *Application::widgetByWindowId(Uint32 id) 209 | { 210 | for (const auto w: widgetList_) 211 | if (id == w->windowId()) 212 | return w; 213 | return nullptr; 214 | } 215 | 216 | void Application::setFocusWidget(Widget *value) 217 | { 218 | focusWidget_ = value; 219 | } 220 | 221 | Widget *Application::focusWidget() const 222 | { 223 | return focusWidget_; 224 | } 225 | 226 | void Application::clearFocus() 227 | { 228 | focusWidget_ = nullptr; 229 | } 230 | -------------------------------------------------------------------------------- /application.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "deliting_object.hpp" 3 | #include 4 | #include 5 | 6 | class Widget; 7 | 8 | class Application 9 | { 10 | friend class Widget; 11 | public: 12 | Application(int &argc, char **argv); 13 | ~Application(); 14 | template 15 | void queueDelete(T *obj) 16 | { 17 | deletingObjects_.push_back(new DeletingObject(obj)); 18 | } 19 | int exec(); 20 | static Application *instance(); 21 | private: 22 | static Application *instance_; 23 | std::vector widgetList_; 24 | Widget *focusWidget_; 25 | Widget *needUpdateWithoutRedraw_; 26 | std::vector deletingObjects_; 27 | Uint32 lastUpdate_; 28 | void addWidget(Widget *); 29 | void removeWidget(Widget *); 30 | Widget *widgetByWindowId(Uint32); 31 | void setFocusWidget(Widget *); 32 | Widget *focusWidget() const; 33 | void clearFocus(); 34 | }; 35 | -------------------------------------------------------------------------------- /base_text_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "base_text_buffer.hpp" 2 | #include "screen.hpp" 3 | 4 | BaseTextBuffer::BaseTextBuffer(): 5 | isReadOnly_{false}, 6 | cursor_{0, 0}, 7 | highlighter_{nullptr} 8 | {} 9 | 10 | BaseTextBuffer::~BaseTextBuffer() 11 | { 12 | delete highlighter_; 13 | } 14 | 15 | const std::wstring &BaseTextBuffer::operator[](int line) const 16 | { 17 | return buffer_[line]; 18 | } 19 | 20 | std::wstring &BaseTextBuffer::operator[](int line) 21 | { 22 | return buffer_[line]; 23 | } 24 | 25 | int BaseTextBuffer::size() const 26 | { 27 | return buffer_.size(); 28 | } 29 | 30 | void BaseTextBuffer::undo(Coord &cursor) 31 | { 32 | undoStack_.undo(cursor); 33 | } 34 | 35 | void BaseTextBuffer::redo(Coord &cursor) 36 | { 37 | undoStack_.redo(cursor); 38 | } 39 | 40 | 41 | void BaseTextBuffer::render(Screen *screen) const 42 | { 43 | if (highlighter_) 44 | highlighter_->update({0, screen->vScroll()}, {0, screen->vScroll() + screen->heightCh()}); 45 | for (int y = 0; y < screen->heightCh(); ++y) 46 | { 47 | const auto yy = y + screen->vScroll(); 48 | const auto &line = yy < size() ? operator[](yy) : L""; 49 | for (int x = 0; x < screen->widthCh(); ++x) 50 | { 51 | const auto xx = x + screen->hScroll(); 52 | Color fgColor = Black; 53 | Color bgColor = White; 54 | if (highlighter_) 55 | { 56 | fgColor = highlighter_->fgColor(xx, yy); 57 | bgColor = highlighter_->bgColor(xx, yy); 58 | } 59 | if (!screen->isSelected({ xx, yy })) 60 | screen->ch(x, y) = { xx < static_cast(line.size()) ? line[xx] : L'\0', fgColor, bgColor }; 61 | else 62 | screen->ch(x, y) = { xx < static_cast(line.size()) ? line[xx] : L'\0', Black, Gray90 }; 63 | } 64 | } 65 | screen->update(); 66 | } 67 | 68 | 69 | std::wstring BaseTextBuffer::preInsert(Coord &, const std::wstring &value) 70 | { 71 | return value; 72 | } 73 | 74 | void BaseTextBuffer::insert(Coord &cursor, const std::wstring &refValue) 75 | { 76 | auto value = preInsert(cursor, refValue); 77 | if (!value.empty()) 78 | undoStack_.push(cursor, 79 | [value, this](Coord &c) -> int 80 | { 81 | internalInsert(c, value); 82 | return value.size(); 83 | }, 84 | [this](Coord &c, int size) 85 | { 86 | internalDelete(c, size); 87 | }); 88 | postInsert(cursor, value); 89 | } 90 | 91 | void BaseTextBuffer::postInsert(Coord &, const std::wstring &) 92 | { 93 | } 94 | 95 | void BaseTextBuffer::internalInsert(Coord &cursor, const std::wstring &value) 96 | { 97 | if (isReadOnly()) 98 | return; 99 | for (wchar_t c: value) 100 | { 101 | auto &line = buffer_[cursor.y]; 102 | if (c != L'\n') 103 | { 104 | line.insert(begin(line) + cursor.x, c); 105 | ++cursor.x; 106 | } 107 | else 108 | { 109 | std::wstring tmp { begin(line) + cursor.x, end(line) }; 110 | line.erase(begin(line) + cursor.x, end(line)); 111 | buffer_.insert(begin(buffer_) + cursor.y + 1, tmp); 112 | ++cursor.y; 113 | cursor.x = 0; 114 | } 115 | } 116 | } 117 | 118 | int BaseTextBuffer::preDel(Coord &, int value) 119 | { 120 | return value; 121 | } 122 | 123 | void BaseTextBuffer::del(Coord &cursor, int value) 124 | { 125 | value = preDel(cursor, value); 126 | if (value > 0) 127 | undoStack_.push(cursor, 128 | [value, this](Coord &c) -> std::wstring 129 | { 130 | return internalDelete(c, value); 131 | }, 132 | [this](Coord &c, const std::wstring &str) 133 | { 134 | Coord tmp = c; 135 | internalInsert(c, str); 136 | c = tmp; 137 | }); 138 | postDel(cursor, value); 139 | } 140 | 141 | void BaseTextBuffer::postDel(Coord &, int) 142 | { 143 | } 144 | 145 | std::wstring BaseTextBuffer::internalDelete(const Coord cursor, int value) 146 | { 147 | if (isReadOnly()) 148 | return L""; 149 | std::wstring result; 150 | for (int i = 0; i < value; ++i) 151 | { 152 | auto &line = buffer_[cursor.y]; 153 | if (begin(line) + cursor.x != end(line)) 154 | { 155 | result += line[cursor.x]; 156 | line.erase(begin(line) + cursor.x); 157 | } 158 | else 159 | { 160 | if (cursor.y < static_cast(buffer_.size()) - 1) 161 | { 162 | result += L"\n"; 163 | auto tmp = buffer_[cursor.y + 1]; 164 | buffer_.erase(begin(buffer_) + cursor.y + 1); 165 | buffer_[cursor.y] += tmp; 166 | } 167 | } 168 | } 169 | return result; 170 | } 171 | 172 | int BaseTextBuffer::preBackspace(Coord &, int value) 173 | { 174 | return value; 175 | } 176 | 177 | void BaseTextBuffer::backspace(Coord &cursor, int value) 178 | { 179 | value = preBackspace(cursor, value); 180 | if (value > 0) 181 | undoStack_.push(cursor, 182 | [value, this](Coord &c) -> std::pair 183 | { 184 | auto result = internalBackspace(c, value); 185 | return std::make_pair(result, c); 186 | }, 187 | [this](Coord &c, const std::pair &s) 188 | { 189 | Coord ccc = s.second; 190 | internalInsert(ccc, s.first); 191 | c = ccc; 192 | }); 193 | postBackspace(cursor, value); 194 | } 195 | 196 | void BaseTextBuffer::postBackspace(Coord &, int) 197 | { 198 | } 199 | 200 | std::wstring BaseTextBuffer::internalBackspace(Coord &cursor, int value) 201 | { 202 | if (isReadOnly()) 203 | return L""; 204 | std::wstring result; 205 | int count = 0; 206 | for (int i = 0; i < value; ++i) 207 | { 208 | if (cursor.x > 0) 209 | --cursor.x; 210 | else 211 | { 212 | if (cursor.y > 0) 213 | { 214 | --cursor.y; 215 | cursor.x = buffer_[cursor.y].size(); 216 | } 217 | else 218 | break; 219 | } 220 | ++count; 221 | } 222 | return internalDelete(cursor, count); 223 | } 224 | 225 | bool BaseTextBuffer::isReadOnly() const 226 | { 227 | return isReadOnly_; 228 | } 229 | 230 | void BaseTextBuffer::setReadOnly(bool value) 231 | { 232 | isReadOnly_ = value; 233 | } 234 | 235 | std::wstring BaseTextBuffer::name() const 236 | { 237 | return name_; 238 | } 239 | 240 | void BaseTextBuffer::setName(const std::wstring &value) 241 | { 242 | name_ = value; 243 | } 244 | 245 | Coord BaseTextBuffer::cursor() const 246 | { 247 | return cursor_; 248 | } 249 | 250 | void BaseTextBuffer::setCursor(Coord value) 251 | { 252 | cursor_ = value; 253 | } 254 | 255 | bool BaseTextBuffer::isModified() const 256 | { 257 | return undoStack_.isModified(); 258 | } 259 | 260 | void BaseTextBuffer::clearModified() 261 | { 262 | undoStack_.clearModified(); 263 | } 264 | -------------------------------------------------------------------------------- /base_text_buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "coord.hpp" 3 | #include "cpp_highlighter.hpp" 4 | #include "undo_stack.hpp" 5 | #include 6 | #include 7 | 8 | class Screen; 9 | 10 | class BaseTextBuffer 11 | { 12 | public: 13 | BaseTextBuffer(); 14 | virtual ~BaseTextBuffer(); 15 | const std::wstring &operator[](int line) const; 16 | std::wstring &operator[](int line); 17 | int size() const; 18 | void undo(Coord &cursor); 19 | void redo(Coord &cursor); 20 | bool canUndo() const; 21 | bool canRedo() const; 22 | bool isModified() const; 23 | void clearModified(); 24 | void render(Screen *) const; 25 | void insert(Coord &cursor, const std::wstring &); 26 | void del(Coord &cursor, int = 1); 27 | void backspace(Coord &cursor, int = 1); 28 | bool isReadOnly() const; 29 | void setReadOnly(bool); 30 | std::wstring name() const; 31 | void setName(const std::wstring &); 32 | Coord cursor() const; 33 | void setCursor(Coord); 34 | protected: 35 | std::vector buffer_; 36 | bool isReadOnly_; 37 | UndoStack undoStack_; 38 | std::wstring name_; 39 | Coord cursor_; 40 | CppHighlighter *highlighter_; 41 | virtual std::wstring preInsert(Coord &cursor, const std::wstring &); 42 | virtual void postInsert(Coord &cursor, const std::wstring &); 43 | virtual int preDel(Coord &cursor, int = 1); 44 | virtual void postDel(Coord &cursor, int = 1); 45 | virtual int preBackspace(Coord &cursor, int = 1); 46 | virtual void postBackspace(Coord &cursor, int = 1); 47 | private: 48 | void internalInsert(Coord &cursor, const std::wstring &); 49 | std::wstring internalDelete(const Coord cursor, int = 1); 50 | std::wstring internalBackspace(Coord &cursor, int = 1); 51 | }; 52 | -------------------------------------------------------------------------------- /color.cpp: -------------------------------------------------------------------------------- 1 | #include "color.hpp" 2 | 3 | SDL_Color toSdlColor(Color value) 4 | { 5 | return { static_cast((value >> 24) & 0xff), 6 | static_cast((value >> 16) & 0xff), 7 | static_cast((value >> 8) & 0xff), 8 | static_cast(value & 0xff)}; 9 | } 10 | -------------------------------------------------------------------------------- /color.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | typedef uint32_t Color; 6 | SDL_Color toSdlColor(Color); 7 | 8 | enum { 9 | IndianRed = 0xB0171F00, 10 | Crimson = 0xDC143C00, 11 | Lightpink = 0xFFB6C100, 12 | Lightpink1 = 0xFFAEB900, 13 | Lightpink2 = 0xEEA2AD00, 14 | Lightpink3 = 0xCD8C9500, 15 | Lightpink4 = 0x8B5F6500, 16 | Pink = 0xFFC0CB00, 17 | Pink1 = 0xFFB5C500, 18 | Pink2 = 0xEEA9B800, 19 | Pink3 = 0xCD919E00, 20 | Pink4 = 0x8B636C00, 21 | Palevioletred = 0xDB709300, 22 | Palevioletred1 = 0xFF82AB00, 23 | Palevioletred2 = 0xEE799F00, 24 | Palevioletred3 = 0xCD688900, 25 | Palevioletred4 = 0x8B475D00, 26 | Lavenderblush = 0xFFF0F500, 27 | Lavenderblush2 = 0xEEE0E500, 28 | Lavenderblush3 = 0xCDC1C500, 29 | Lavenderblush4 = 0x8B838600, 30 | Violetred1 = 0xFF3E9600, 31 | Violetred2 = 0xEE3A8C00, 32 | Violetred3 = 0xCD327800, 33 | Violetred4 = 0x8B225200, 34 | Hotpink = 0xFF69B400, 35 | Hotpink1 = 0xFF6EB400, 36 | Hotpink2 = 0xEE6AA700, 37 | Hotpink3 = 0xCD609000, 38 | Hotpink4 = 0x8B3A6200, 39 | Raspberry = 0x87265700, 40 | Deeppink = 0xFF149300, 41 | Deeppink2 = 0xEE128900, 42 | Deeppink3 = 0xCD107600, 43 | Deeppink4 = 0x8B0A5000, 44 | Maroon1 = 0xFF34B300, 45 | Maroon2 = 0xEE30A700, 46 | Maroon3 = 0xCD299000, 47 | Maroon4 = 0x8B1C6200, 48 | Mediumvioletred = 0xC7158500, 49 | Violetred = 0xD0209000, 50 | Orchid = 0xDA70D600, 51 | Orchid1 = 0xFF83FA00, 52 | Orchid2 = 0xEE7AE900, 53 | Orchid3 = 0xCD69C900, 54 | Orchid4 = 0x8B478900, 55 | Thistle = 0xD8BFD800, 56 | Thistle1 = 0xFFE1FF00, 57 | Thistle2 = 0xEED2EE00, 58 | Thistle3 = 0xCDB5CD00, 59 | Thistle4 = 0x8B7B8B00, 60 | Plum1 = 0xFFBBFF00, 61 | Plum2 = 0xEEAEEE00, 62 | Plum3 = 0xCD96CD00, 63 | Plum4 = 0x8B668B00, 64 | Plum = 0xDDA0DD00, 65 | Violet = 0xEE82EE00, 66 | Magenta = 0xFF00FF00, 67 | Magenta2 = 0xEE00EE00, 68 | Magenta3 = 0xCD00CD00, 69 | Darkmagenta = 0x8B008B00, 70 | Purple = 0x80008000, 71 | Mediumorchid = 0xBA55D300, 72 | Mediumorchid1 = 0xE066FF00, 73 | Mediumorchid2 = 0xD15FEE00, 74 | Mediumorchid3 = 0xB452CD00, 75 | Mediumorchid4 = 0x7A378B00, 76 | Darkviolet = 0x9400D300, 77 | Darkorchid = 0x9932CC00, 78 | Darkorchid1 = 0xBF3EFF00, 79 | Darkorchid2 = 0xB23AEE00, 80 | Darkorchid3 = 0x9A32CD00, 81 | Darkorchid4 = 0x68228B00, 82 | Indigo = 0x4B008200, 83 | Blueviolet = 0x8A2BE200, 84 | Purple1 = 0x9B30FF00, 85 | Purple2 = 0x912CEE00, 86 | Purple3 = 0x7D26CD00, 87 | Purple4 = 0x551A8B00, 88 | Mediumpurple = 0x9370DB00, 89 | Mediumpurple1 = 0xAB82FF00, 90 | Mediumpurple2 = 0x9F79EE00, 91 | Mediumpurple3 = 0x8968CD00, 92 | Mediumpurple4 = 0x5D478B00, 93 | Darkslateblue = 0x483D8B00, 94 | Lightslateblue = 0x8470FF00, 95 | Mediumslateblue = 0x7B68EE00, 96 | Slateblue = 0x6A5ACD00, 97 | Slateblue1 = 0x836FFF00, 98 | Slateblue2 = 0x7A67EE00, 99 | Slateblue3 = 0x6959CD00, 100 | Slateblue4 = 0x473C8B00, 101 | Ghostwhite = 0xF8F8FF00, 102 | Lavender = 0xE6E6FA00, 103 | Blue = 0x0000FF00, 104 | Blue2 = 0x0000EE00, 105 | Mediumblue = 0x0000CD00, 106 | Darkblue = 0x00008B00, 107 | Navy = 0x00008000, 108 | Midnightblue = 0x19197000, 109 | Cobalt = 0x3D59AB00, 110 | Royalblue = 0x4169E100, 111 | Royalblue1 = 0x4876FF00, 112 | Royalblue2 = 0x436EEE00, 113 | Royalblue3 = 0x3A5FCD00, 114 | Royalblue4 = 0x27408B00, 115 | Cornflowerblue = 0x6495ED00, 116 | Lightsteelblue = 0xB0C4DE00, 117 | Lightsteelblue1 = 0xCAE1FF00, 118 | Lightsteelblue2 = 0xBCD2EE00, 119 | Lightsteelblue3 = 0xA2B5CD00, 120 | Lightsteelblue4 = 0x6E7B8B00, 121 | Lightslategray = 0x77889900, 122 | Slategray = 0x70809000, 123 | Slategray1 = 0xC6E2FF00, 124 | Slategray2 = 0xB9D3EE00, 125 | Slategray3 = 0x9FB6CD00, 126 | Slategray4 = 0x6C7B8B00, 127 | Dodgerblue = 0x1E90FF00, 128 | Dodgerblue2 = 0x1C86EE00, 129 | Dodgerblue3 = 0x1874CD00, 130 | Dodgerblue4 = 0x104E8B00, 131 | Aliceblue = 0xF0F8FF00, 132 | Steelblue = 0x4682B400, 133 | Steelblue1 = 0x63B8FF00, 134 | Steelblue2 = 0x5CACEE00, 135 | Steelblue3 = 0x4F94CD00, 136 | Steelblue4 = 0x36648B00, 137 | Lightskyblue = 0x87CEFA00, 138 | Lightskyblue1 = 0xB0E2FF00, 139 | Lightskyblue2 = 0xA4D3EE00, 140 | Lightskyblue3 = 0x8DB6CD00, 141 | Lightskyblue4 = 0x607B8B00, 142 | Skyblue1 = 0x87CEFF00, 143 | Skyblue2 = 0x7EC0EE00, 144 | Skyblue3 = 0x6CA6CD00, 145 | Skyblue4 = 0x4A708B00, 146 | Skyblue = 0x87CEEB00, 147 | Deepskyblue = 0x00BFFF00, 148 | Deepskyblue2 = 0x00B2EE00, 149 | Deepskyblue3 = 0x009ACD00, 150 | Deepskyblue4 = 0x00688B00, 151 | Peacock = 0x33A1C900, 152 | Lightblue = 0xADD8E600, 153 | Lightblue1 = 0xBFEFFF00, 154 | Lightblue2 = 0xB2DFEE00, 155 | Lightblue3 = 0x9AC0CD00, 156 | Lightblue4 = 0x68838B00, 157 | Powderblue = 0xB0E0E600, 158 | Cadetblue1 = 0x98F5FF00, 159 | Cadetblue2 = 0x8EE5EE00, 160 | Cadetblue3 = 0x7AC5CD00, 161 | Cadetblue4 = 0x53868B00, 162 | Turquoise1 = 0x00F5FF00, 163 | Turquoise2 = 0x00E5EE00, 164 | Turquoise3 = 0x00C5CD00, 165 | Turquoise4 = 0x00868B00, 166 | Cadetblue = 0x5F9EA000, 167 | Darkturquoise = 0x00CED100, 168 | Azure = 0xF0FFFF00, 169 | Azure2 = 0xE0EEEE00, 170 | Azure3 = 0xC1CDCD00, 171 | Azure4 = 0x838B8B00, 172 | Lightcyan = 0xE0FFFF00, 173 | Lightcyan2 = 0xD1EEEE00, 174 | Lightcyan3 = 0xB4CDCD00, 175 | Lightcyan4 = 0x7A8B8B00, 176 | Paleturquoise1 = 0xBBFFFF00, 177 | Paleturquoise2 = 0xAEEEEE00, 178 | Paleturquoise3 = 0x96CDCD00, 179 | Paleturquoise4 = 0x668B8B00, 180 | Darkslategray = 0x2F4F4F00, 181 | Darkslategray1 = 0x97FFFF00, 182 | Darkslategray2 = 0x8DEEEE00, 183 | Darkslategray3 = 0x79CDCD00, 184 | Darkslategray4 = 0x528B8B00, 185 | Cyan = 0x00FFFF00, 186 | Cyan2 = 0x00EEEE00, 187 | Cyan3 = 0x00CDCD00, 188 | Darkcyan = 0x008B8B00, 189 | Teal = 0x00808000, 190 | Mediumturquoise = 0x48D1CC00, 191 | Lightseagreen = 0x20B2AA00, 192 | Manganeseblue = 0x03A89E00, 193 | Turquoise = 0x40E0D000, 194 | Coldgrey = 0x808A8700, 195 | Turquoiseblue = 0x00C78C00, 196 | Aquamarine = 0x7FFFD400, 197 | Aquamarine2 = 0x76EEC600, 198 | Mediumaquamarine = 0x66CDAA00, 199 | Aquamarine4 = 0x458B7400, 200 | Mediumspringgreen = 0x00FA9A00, 201 | Mintcream = 0xF5FFFA00, 202 | Springgreen = 0x00FF7F00, 203 | Springgreen1 = 0x00EE7600, 204 | Springgreen2 = 0x00CD6600, 205 | Springgreen3 = 0x008B4500, 206 | Mediumseagreen = 0x3CB37100, 207 | Seagreen1 = 0x54FF9F00, 208 | Seagreen2 = 0x4EEE9400, 209 | Seagreen3 = 0x43CD8000, 210 | Seagreen = 0x2E8B5700, 211 | Emeraldgreen = 0x00C95700, 212 | Mint = 0xBDFCC900, 213 | Cobaltgreen = 0x3D914000, 214 | Honeydew = 0xF0FFF000, 215 | Honeydew2 = 0xE0EEE000, 216 | Honeydew3 = 0xC1CDC100, 217 | Honeydew4 = 0x838B8300, 218 | Darkseagreen = 0x8FBC8F00, 219 | Darkseagreen1 = 0xC1FFC100, 220 | Darkseagreen2 = 0xB4EEB400, 221 | Darkseagreen3 = 0x9BCD9B00, 222 | Darkseagreen4 = 0x698B6900, 223 | Palegreen = 0x98FB9800, 224 | Palegreen1 = 0x9AFF9A00, 225 | Lightgreen = 0x90EE9000, 226 | Palegreen3 = 0x7CCD7C00, 227 | Palegreen4 = 0x548B5400, 228 | Limegreen = 0x32CD3200, 229 | Forestgreen = 0x228B2200, 230 | Lime = 0x00FF0000, 231 | Green2 = 0x00EE0000, 232 | Green3 = 0x00CD0000, 233 | Green4 = 0x008B0000, 234 | Green = 0x00800000, 235 | Darkgreen = 0x00640000, 236 | Sapgreen = 0x30801400, 237 | Lawngreen = 0x7CFC0000, 238 | Chartreuse = 0x7FFF0000, 239 | Chartreuse2 = 0x76EE0000, 240 | Chartreuse3 = 0x66CD0000, 241 | Chartreuse4 = 0x458B0000, 242 | Greenyellow = 0xADFF2F00, 243 | Darkolivegreen1 = 0xCAFF7000, 244 | Darkolivegreen2 = 0xBCEE6800, 245 | Darkolivegreen3 = 0xA2CD5A00, 246 | Darkolivegreen4 = 0x6E8B3D00, 247 | Darkolivegreen = 0x556B2F00, 248 | Olivedrab = 0x6B8E2300, 249 | Olivedrab1 = 0xC0FF3E00, 250 | Olivedrab2 = 0xB3EE3A00, 251 | Yellowgreen = 0x9ACD3200, 252 | Olivedrab4 = 0x698B2200, 253 | Ivory = 0xFFFFF000, 254 | Ivory2 = 0xEEEEE000, 255 | Ivory3 = 0xCDCDC100, 256 | Ivory4 = 0x8B8B8300, 257 | Beige = 0xF5F5DC00, 258 | Lightyellow = 0xFFFFE000, 259 | Lightyellow2 = 0xEEEED100, 260 | Lightyellow3 = 0xCDCDB400, 261 | Lightyellow4 = 0x8B8B7A00, 262 | Lightgoldenrodyellow = 0xFAFAD200, 263 | Yellow = 0xFFFF0000, 264 | Yellow2 = 0xEEEE0000, 265 | Yellow3 = 0xCDCD0000, 266 | Yellow4 = 0x8B8B0000, 267 | Warmgrey = 0x80806900, 268 | Olive = 0x80800000, 269 | Darkkhaki = 0xBDB76B00, 270 | Khaki1 = 0xFFF68F00, 271 | Khaki2 = 0xEEE68500, 272 | Khaki3 = 0xCDC67300, 273 | Khaki4 = 0x8B864E00, 274 | Khaki = 0xF0E68C00, 275 | Palegoldenrod = 0xEEE8AA00, 276 | Lemonchiffon = 0xFFFACD00, 277 | Lemonchiffon2 = 0xEEE9BF00, 278 | Lemonchiffon3 = 0xCDC9A500, 279 | Lemonchiffon4 = 0x8B897000, 280 | Lightgoldenrod1 = 0xFFEC8B00, 281 | Lightgoldenrod2 = 0xEEDC8200, 282 | Lightgoldenrod3 = 0xCDBE7000, 283 | Lightgoldenrod4 = 0x8B814C00, 284 | Banana = 0xE3CF5700, 285 | Gold = 0xFFD70000, 286 | Gold2 = 0xEEC90000, 287 | Gold3 = 0xCDAD0000, 288 | Gold4 = 0x8B750000, 289 | Cornsilk = 0xFFF8DC00, 290 | Cornsilk2 = 0xEEE8CD00, 291 | Cornsilk3 = 0xCDC8B100, 292 | Cornsilk4 = 0x8B887800, 293 | Goldenrod = 0xDAA52000, 294 | Goldenrod1 = 0xFFC12500, 295 | Goldenrod2 = 0xEEB42200, 296 | Goldenrod3 = 0xCD9B1D00, 297 | Goldenrod4 = 0x8B691400, 298 | Darkgoldenrod = 0xB8860B00, 299 | Darkgoldenrod1 = 0xFFB90F00, 300 | Darkgoldenrod2 = 0xEEAD0E00, 301 | Darkgoldenrod3 = 0xCD950C00, 302 | Darkgoldenrod4 = 0x8B650800, 303 | Orange = 0xFFA50000, 304 | Orange2 = 0xEE9A0000, 305 | Orange3 = 0xCD850000, 306 | Orange4 = 0x8B5A0000, 307 | Floralwhite = 0xFFFAF000, 308 | Oldlace = 0xFDF5E600, 309 | Wheat = 0xF5DEB300, 310 | Wheat1 = 0xFFE7BA00, 311 | Wheat2 = 0xEED8AE00, 312 | Wheat3 = 0xCDBA9600, 313 | Wheat4 = 0x8B7E6600, 314 | Moccasin = 0xFFE4B500, 315 | Papayawhip = 0xFFEFD500, 316 | Blanchedalmond = 0xFFEBCD00, 317 | Navajowhite = 0xFFDEAD00, 318 | Navajowhite2 = 0xEECFA100, 319 | Navajowhite3 = 0xCDB38B00, 320 | Navajowhite4 = 0x8B795E00, 321 | Eggshell = 0xFCE6C900, 322 | Tan = 0xD2B48C00, 323 | Brick = 0x9C661F00, 324 | Cadmiumyellow = 0xFF991200, 325 | Antiquewhite = 0xFAEBD700, 326 | Antiquewhite1 = 0xFFEFDB00, 327 | Antiquewhite2 = 0xEEDFCC00, 328 | Antiquewhite3 = 0xCDC0B000, 329 | Antiquewhite4 = 0x8B837800, 330 | Burlywood = 0xDEB88700, 331 | Burlywood1 = 0xFFD39B00, 332 | Burlywood2 = 0xEEC59100, 333 | Burlywood3 = 0xCDAA7D00, 334 | Burlywood4 = 0x8B735500, 335 | Bisque = 0xFFE4C400, 336 | Bisque2 = 0xEED5B700, 337 | Bisque3 = 0xCDB79E00, 338 | Bisque4 = 0x8B7D6B00, 339 | Melon = 0xE3A86900, 340 | Carrot = 0xED912100, 341 | Darkorange = 0xFF8C0000, 342 | Darkorange1 = 0xFF7F0000, 343 | Darkorange2 = 0xEE760000, 344 | Darkorange3 = 0xCD660000, 345 | Darkorange4 = 0x8B450000, 346 | Tan1 = 0xFFA54F00, 347 | Tan2 = 0xEE9A4900, 348 | Peru = 0xCD853F00, 349 | Tan4 = 0x8B5A2B00, 350 | Linen = 0xFAF0E600, 351 | Peachpuff = 0xFFDAB900, 352 | Peachpuff2 = 0xEECBAD00, 353 | Peachpuff3 = 0xCDAF9500, 354 | Peachpuff4 = 0x8B776500, 355 | Seashell = 0xFFF5EE00, 356 | Seashell2 = 0xEEE5DE00, 357 | Seashell3 = 0xCDC5BF00, 358 | Seashell4 = 0x8B868200, 359 | Sandybrown = 0xF4A46000, 360 | Rawsienna = 0xC7611400, 361 | Chocolate = 0xD2691E00, 362 | Chocolate1 = 0xFF7F2400, 363 | Chocolate2 = 0xEE762100, 364 | Chocolate3 = 0xCD661D00, 365 | Ssaddlebrown = 0x8B451300, 366 | Ivoryblack = 0x29242100, 367 | Flesh = 0xFF7D4000, 368 | Cadmiumorange = 0xFF610300, 369 | Burntsienna = 0x8A360F00, 370 | Sienna = 0xA0522D00, 371 | Sienna1 = 0xFF824700, 372 | Sienna2 = 0xEE794200, 373 | Sienna3 = 0xCD683900, 374 | Sienna4 = 0x8B472600, 375 | Lightsalmon = 0xFFA07A00, 376 | Lightsalmon2 = 0xEE957200, 377 | Lightsalmon3 = 0xCD816200, 378 | Lightsalmon4 = 0x8B574200, 379 | Coral = 0xFF7F5000, 380 | Orangered = 0xFF450000, 381 | Orangered2 = 0xEE400000, 382 | Orangered3 = 0xCD370000, 383 | Orangered4 = 0x8B250000, 384 | Sepia = 0x5E261200, 385 | Darksalmon = 0xE9967A00, 386 | Salmon1 = 0xFF8C6900, 387 | Salmon2 = 0xEE826200, 388 | Salmon3 = 0xCD705400, 389 | Salmon4 = 0x8B4C3900, 390 | Coral1 = 0xFF725600, 391 | Coral2 = 0xEE6A5000, 392 | Coral3 = 0xCD5B4500, 393 | Coral4 = 0x8B3E2F00, 394 | Burntumber = 0x8A332400, 395 | Tomato = 0xFF634700, 396 | Tomato2 = 0xEE5C4200, 397 | Tomato3 = 0xCD4F3900, 398 | Tomato4 = 0x8B362600, 399 | Salmon = 0xFA807200, 400 | Mistyrose = 0xFFE4E100, 401 | Mistyrose2 = 0xEED5D200, 402 | Mistyrose3 = 0xCDB7B500, 403 | Mistyrose4 = 0x8B7D7B00, 404 | Snow = 0xFFFAFA00, 405 | Snow2 = 0xEEE9E900, 406 | Snow3 = 0xCDC9C900, 407 | Snow4 = 0x8B898900, 408 | Rosybrown = 0xBC8F8F00, 409 | Rosybrown1 = 0xFFC1C100, 410 | Rosybrown2 = 0xEEB4B400, 411 | Rosybrown3 = 0xCD9B9B00, 412 | Rosybrown4 = 0x8B696900, 413 | Lightcoral = 0xF0808000, 414 | Indianred = 0xCD5C5C00, 415 | Indianred1 = 0xFF6A6A00, 416 | Indianred2 = 0xEE636300, 417 | Indianred4 = 0x8B3A3A00, 418 | Indianred3 = 0xCD555500, 419 | Brown = 0xA52A2A00, 420 | Brown1 = 0xFF404000, 421 | Brown2 = 0xEE3B3B00, 422 | Brown3 = 0xCD333300, 423 | Brown4 = 0x8B232300, 424 | Firebrick = 0xB2222200, 425 | Firebrick1 = 0xFF303000, 426 | Firebrick2 = 0xEE2C2C00, 427 | Firebrick3 = 0xCD262600, 428 | Firebrick4 = 0x8B1A1A00, 429 | Red = 0xFF000000, 430 | Red2 = 0xEE000000, 431 | Red3 = 0xCD000000, 432 | Rarkred = 0x8B000000, 433 | Maroon = 0x80000000, 434 | SgiBeet = 0x8E388E00, 435 | SgiSlateblue = 0x7171C600, 436 | SgiLightblue = 0x7D9EC000, 437 | SgiTeal = 0x388E8E00, 438 | SgiChartreuse = 0x71C67100, 439 | SgiOlivedrab = 0x8E8E3800, 440 | SgiBrightgray = 0xC5C1AA00, 441 | SgiSalmon = 0xC6717100, 442 | SgiDarkgray = 0x55555500, 443 | SgiGray12 = 0x1E1E1E00, 444 | SgiGray16 = 0x28282800, 445 | SgiGray32 = 0x51515100, 446 | SgiGray36 = 0x5B5B5B00, 447 | SgiGray52 = 0x84848400, 448 | SgiGray56 = 0x8E8E8E00, 449 | SgiLightgray = 0xAAAAAA00, 450 | SgiGray72 = 0xB7B7B700, 451 | SgiGray76 = 0xC1C1C100, 452 | SgiGray92 = 0xEAEAEA00, 453 | SgiGray96 = 0xF4F4F400, 454 | White = 0xFFFFFF00, 455 | WhiteSmoke = 0xF5F5F500, 456 | Gainsboro = 0xDCDCDC00, 457 | Lightgrey = 0xD3D3D300, 458 | Silver = 0xC0C0C000, 459 | Darkgray = 0xA9A9A900, 460 | Gray = 0x80808000, 461 | Dimgray = 0x69696900, 462 | Black = 0x00000000, 463 | Gray99 = 0xFCFCFC00, 464 | Gray98 = 0xFAFAFA00, 465 | Gray97 = 0xF7F7F700, 466 | Gray95 = 0xF2F2F200, 467 | Gray94 = 0xF0F0F000, 468 | Gray93 = 0xEDEDED00, 469 | Gray92 = 0xEBEBEB00, 470 | Gray91 = 0xE8E8E800, 471 | Gray90 = 0xE5E5E500, 472 | Gray89 = 0xE3E3E300, 473 | Gray88 = 0xE0E0E000, 474 | Gray87 = 0xDEDEDE00, 475 | Gray86 = 0xDBDBDB00, 476 | Gray85 = 0xD9D9D900, 477 | Gray84 = 0xD6D6D600, 478 | Gray83 = 0xD4D4D400, 479 | Gray82 = 0xD1D1D100, 480 | Gray81 = 0xCFCFCF00, 481 | Gray80 = 0xCCCCCC00, 482 | Gray79 = 0xC9C9C900, 483 | Gray78 = 0xC7C7C700, 484 | Gray77 = 0xC4C4C400, 485 | Gray76 = 0xC2C2C200, 486 | Gray75 = 0xBFBFBF00, 487 | Gray74 = 0xBDBDBD00, 488 | Gray73 = 0xBABABA00, 489 | Gray72 = 0xB8B8B800, 490 | Gray71 = 0xB5B5B500, 491 | Gray70 = 0xB3B3B300, 492 | Gray69 = 0xB0B0B000, 493 | Gray68 = 0xADADAD00, 494 | Gray67 = 0xABABAB00, 495 | Gray66 = 0xA8A8A800, 496 | Gray65 = 0xA6A6A600, 497 | Gray64 = 0xA3A3A300, 498 | Gray63 = 0xA1A1A100, 499 | Gray62 = 0x9E9E9E00, 500 | Gray61 = 0x9C9C9C00, 501 | Gray60 = 0x99999900, 502 | Gray59 = 0x96969600, 503 | Gray58 = 0x94949400, 504 | Gray57 = 0x91919100, 505 | Gray56 = 0x8F8F8F00, 506 | Gray55 = 0x8C8C8C00, 507 | Gray54 = 0x8A8A8A00, 508 | Gray53 = 0x87878700, 509 | Gray52 = 0x85858500, 510 | Gray51 = 0x82828200, 511 | Gray50 = 0x7F7F7F00, 512 | Gray49 = 0x7D7D7D00, 513 | Gray48 = 0x7A7A7A00, 514 | Gray47 = 0x78787800, 515 | Gray46 = 0x75757500, 516 | Gray45 = 0x73737300, 517 | Gray44 = 0x70707000, 518 | Gray43 = 0x6E6E6E00, 519 | Gray42 = 0x6B6B6B00, 520 | Gray40 = 0x66666600, 521 | Gray39 = 0x63636300, 522 | Gray38 = 0x61616100, 523 | Gray37 = 0x5E5E5E00, 524 | Gray36 = 0x5C5C5C00, 525 | Gray35 = 0x59595900, 526 | Gray34 = 0x57575700, 527 | Gray33 = 0x54545400, 528 | Gray32 = 0x52525200, 529 | Gray31 = 0x4F4F4F00, 530 | Gray30 = 0x4D4D4D00, 531 | Gray29 = 0x4A4A4A00, 532 | Gray28 = 0x47474700, 533 | Gray27 = 0x45454500, 534 | Gray26 = 0x42424200, 535 | Gray25 = 0x40404000, 536 | Gray24 = 0x3D3D3D00, 537 | Gray23 = 0x3B3B3B00, 538 | Gray22 = 0x38383800, 539 | Gray21 = 0x36363600, 540 | Gray20 = 0x33333300, 541 | Gray19 = 0x30303000, 542 | Gray18 = 0x2E2E2E00, 543 | Gray17 = 0x2B2B2B00, 544 | Gray16 = 0x29292900, 545 | Gray15 = 0x26262600, 546 | Gray14 = 0x24242400, 547 | Gray13 = 0x21212100, 548 | Gray12 = 0x1F1F1F00, 549 | Gray11 = 0x1C1C1C00, 550 | Gray10 = 0x1A1A1A00, 551 | Gray9 = 0x17171700, 552 | Gray8 = 0x14141400, 553 | Gray7 = 0x12121200, 554 | Gray6 = 0x0F0F0F00, 555 | Gray5 = 0x0D0D0D00, 556 | Gray4 = 0x0A0A0A00, 557 | Gray3 = 0x08080800, 558 | Gray2 = 0x05050500, 559 | Gray1 = 0x03030300 560 | }; 561 | -------------------------------------------------------------------------------- /coord.cpp: -------------------------------------------------------------------------------- 1 | #include "coord.hpp" 2 | 3 | bool operator==(const Coord &x, const Coord &y) 4 | { 5 | return x.x == y.x && x.y == y.y; 6 | } 7 | -------------------------------------------------------------------------------- /coord.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Coord 4 | { 5 | int x; 6 | int y; 7 | }; 8 | 9 | bool operator==(const Coord &x, const Coord &y); 10 | -------------------------------------------------------------------------------- /cpp_highlighter.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp_highlighter.hpp" 2 | #include "base_text_buffer.hpp" 3 | #include 4 | #include 5 | 6 | static const char *keywords[] = { 7 | "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", 8 | "break", "case", "catch", "char", "char16_t", "char32_t", "class", "compl", "const", 9 | "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", 10 | "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", 11 | "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", 12 | "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", 13 | "register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_assert", 14 | "static_cast", "struct", "switch", "template", "this", "thread_local", "throw", "true", 15 | "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", 16 | "volatile", "wchar_t", "while", "xor", "xor_eq" }; 17 | 18 | 19 | CppHighlighter::CppHighlighter(BaseTextBuffer *textBuffer): 20 | textBuffer_(textBuffer), 21 | keywords_{ std::begin(keywords), std::end(keywords) } 22 | { 23 | toFg_[Keyword] = Green; 24 | toBg_[Keyword] = White; 25 | 26 | toFg_[Ident] = Black; 27 | toBg_[Ident] = White; 28 | 29 | toFg_[Comment] = Brown; 30 | toBg_[Comment] = White; 31 | 32 | toFg_[Macro] = Violetred; 33 | toBg_[Macro] = White; 34 | 35 | toFg_[StringLiteral] = Steelblue; 36 | toBg_[StringLiteral] = White; 37 | 38 | toFg_[Number] = Blue; 39 | toBg_[Number] = White; 40 | 41 | toFg_[Other] = Black; 42 | toBg_[Other] = White; 43 | } 44 | 45 | Color CppHighlighter::fgColor(int x, int y) const 46 | { 47 | if (y < static_cast(types_.size())) 48 | if (x < static_cast(types_[y].size())) 49 | return toFg_[types_[y][x]]; 50 | return Black; 51 | } 52 | 53 | Color CppHighlighter::bgColor(int x, int y) const 54 | { 55 | if (y < static_cast(types_.size())) 56 | if (x < static_cast(types_[y].size())) 57 | return toBg_[types_[y][x]]; 58 | return White; 59 | } 60 | 61 | static bool isAllSpaces(const std::wstring &str) 62 | { 63 | for (auto c: str) 64 | if (!iswspace(c)) 65 | return false; 66 | return true; 67 | } 68 | 69 | std::pair CppHighlighter::getToken(int &x, int &y) 70 | { 71 | int count = 0; 72 | Type type = Other; 73 | if (!outOfRange(x, y)) 74 | { 75 | auto c = ch(x, y); 76 | if (iswalpha(c) || c == L'_') 77 | { 78 | std::string ident; 79 | while (!outOfRange(x, y)) 80 | { 81 | auto c = ch(x, y); 82 | if (iswalnum(c) || c == L'_') 83 | ident += c; 84 | else 85 | break; 86 | ++count; 87 | moveForward(x, y); 88 | } 89 | if (keywords_.find(ident) != std::end(keywords_)) 90 | type = Keyword; 91 | else 92 | type = Ident; 93 | } 94 | else if (iswdigit(c)) 95 | { 96 | while (!outOfRange(x, y)) 97 | { 98 | auto c = ch(x, y); 99 | if (!iswxdigit(c)) 100 | break; 101 | ++count; 102 | moveForward(x, y); 103 | } 104 | type = Number; 105 | } 106 | else if ((x == 0 || isAllSpaces((*textBuffer_)[y].substr(0, x - 1))) && c == '#') 107 | { 108 | ++count; 109 | moveForward(x, y); 110 | while (!outOfRange(x, y)) 111 | { 112 | auto c = ch(x, y); 113 | if (!iswalnum(c) && c != L'_') 114 | break; 115 | ++count; 116 | moveForward(x, y); 117 | } 118 | type = Macro; 119 | } 120 | else if (c == L'"' || c == L'\'') 121 | { 122 | wchar_t openChar = c; 123 | ++count; 124 | moveForward(x, y); 125 | while (!outOfRange(x, y)) 126 | { 127 | auto c = ch(x, y); 128 | if (c == L'\\') 129 | { 130 | ++count; 131 | moveForward(x, y); 132 | c = ch(x, y); 133 | if (iswdigit(c) && c != L'0') 134 | { 135 | ++count; 136 | moveForward(x, y); 137 | ++count; 138 | moveForward(x, y); 139 | } 140 | else if (c == L'x') 141 | { 142 | ++count; 143 | moveForward(x, y); 144 | ++count; 145 | moveForward(x, y); 146 | } 147 | else if (c == L'u') 148 | { 149 | ++count; 150 | moveForward(x, y); 151 | ++count; 152 | moveForward(x, y); 153 | ++count; 154 | moveForward(x, y); 155 | ++count; 156 | moveForward(x, y); 157 | } 158 | else if (c == L'U') 159 | { 160 | ++count; 161 | moveForward(x, y); 162 | ++count; 163 | moveForward(x, y); 164 | ++count; 165 | moveForward(x, y); 166 | ++count; 167 | moveForward(x, y); 168 | ++count; 169 | moveForward(x, y); 170 | ++count; 171 | moveForward(x, y); 172 | ++count; 173 | moveForward(x, y); 174 | ++count; 175 | moveForward(x, y); 176 | } 177 | } 178 | else if (c == openChar) 179 | { 180 | ++count; 181 | moveForward(x, y); 182 | break; 183 | } 184 | ++count; 185 | moveForward(x, y); 186 | } 187 | type = StringLiteral; 188 | } 189 | else 190 | { 191 | int tmpX = x; 192 | int tmpY = y; 193 | moveForward(tmpX, tmpY); 194 | auto c2 = ch(tmpX, tmpY); 195 | if (c == L'/' && c2 == L'/') 196 | { 197 | x = tmpX; 198 | y = tmpY; 199 | count = (*textBuffer_)[y].size() - x + 2; 200 | x = (*textBuffer_)[y].size(); 201 | moveForward(x, y); 202 | type = Comment; 203 | } 204 | else if (c == L'/' && c2 == L'*') 205 | { 206 | x = tmpX; 207 | y = tmpY; 208 | while (!outOfRange(x, y)) 209 | { 210 | c = c2; 211 | c2 = ch(x, y); 212 | if (c == L'*' && c2 == L'/') 213 | { 214 | ++count; 215 | moveForward(x, y); 216 | break; 217 | } 218 | ++count; 219 | moveForward(x, y); 220 | } 221 | type = Comment; 222 | } 223 | else 224 | { 225 | type = Other; 226 | ++count; 227 | moveForward(x, y); 228 | } 229 | } 230 | } 231 | return std::make_pair(type, count); 232 | } 233 | 234 | void CppHighlighter::update(const Coord &start, const Coord &end) 235 | { 236 | types_.resize(textBuffer_->size()); 237 | for (int y = 0; y < textBuffer_->size(); ++y) 238 | { 239 | auto &line = (*textBuffer_)[y]; 240 | types_[y].resize(line.size() + 1); 241 | } 242 | 243 | int x = start.x; 244 | int y = start.y; 245 | auto t = types_[y][x]; 246 | auto c = 0; 247 | while (!outOfRange(x, y)) 248 | { 249 | int tmpX = x; 250 | int tmpY = y; 251 | moveBackward(tmpX, tmpY); 252 | if (outOfRange(tmpX, tmpY)) 253 | break; 254 | if (t != types_[tmpY][tmpX]) 255 | { 256 | t = types_[tmpY][tmpX]; 257 | ++c; 258 | if (c > 2) 259 | break; 260 | } 261 | x = tmpX; 262 | y = tmpY; 263 | } 264 | while (!outOfRange(x, y)) 265 | { 266 | std::pair token = getToken(x, y); 267 | int count = token.second; 268 | int tmpX = x; 269 | int tmpY = y; 270 | for (; count > 0; --count) 271 | { 272 | moveBackward(tmpX, tmpY); 273 | if (!outOfRange(tmpX, tmpY)) 274 | { 275 | types_[tmpY][tmpX] = token.first; 276 | } 277 | } 278 | if (y > end.y || (y == end.y && x > end.x)) 279 | break; 280 | } 281 | } 282 | 283 | void CppHighlighter::moveForward(int &x, int &y) const 284 | { 285 | if (outOfRange(x, y)) 286 | return; 287 | ++x; 288 | if (x > static_cast((*textBuffer_)[y].size())) 289 | { 290 | x = 0; 291 | ++y; 292 | } 293 | } 294 | 295 | void CppHighlighter::moveBackward(int &x, int &y) const 296 | { 297 | --x; 298 | if (x < 0) 299 | { 300 | --y; 301 | if (y >= 0) 302 | x = static_cast((*textBuffer_)[y].size()); 303 | else 304 | x = 0; 305 | if (outOfRange(x, y)) 306 | return; 307 | } 308 | } 309 | 310 | bool CppHighlighter::outOfRange(int x, int y) const 311 | { 312 | if (y < 0 || y >= static_cast(textBuffer_->size())) 313 | return true; 314 | if (x < 0 || x > static_cast((*textBuffer_)[y].size())) 315 | return true; 316 | return false; 317 | } 318 | 319 | wchar_t CppHighlighter::ch(int x, int y) const 320 | { 321 | if (!outOfRange(x, y)) 322 | return (*textBuffer_)[y][x]; 323 | else 324 | return L'\0'; 325 | } 326 | -------------------------------------------------------------------------------- /cpp_highlighter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "color.hpp" 3 | #include "coord.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class BaseTextBuffer; 10 | 11 | class CppHighlighter 12 | { 13 | public: 14 | CppHighlighter(BaseTextBuffer *); 15 | Color fgColor(int x, int y) const; 16 | Color bgColor(int x, int y) const; 17 | void update(const Coord &start, const Coord &end); 18 | private: 19 | enum Type { Keyword, Ident, Comment, Macro, StringLiteral, Number, Other }; 20 | std::vector > types_; 21 | BaseTextBuffer *textBuffer_; 22 | std::set keywords_; 23 | std::pair getToken(int &x, int &y); 24 | void moveForward(int &x, int &y) const; 25 | void moveBackward(int &x, int &y) const; 26 | bool outOfRange(int x, int y) const; 27 | wchar_t ch(int x, int y) const; 28 | mutable std::map toFg_; 29 | mutable std::map toBg_; 30 | }; 31 | -------------------------------------------------------------------------------- /current_dir.cpp: -------------------------------------------------------------------------------- 1 | #include "current_dir.hpp" 2 | #include "to_utf16.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | std::string getCurrentDir() 8 | { 9 | char *tmp = getcwd(nullptr, MAXPATHLEN); 10 | std::string result = tmp; 11 | free(tmp); 12 | return result; 13 | } 14 | -------------------------------------------------------------------------------- /current_dir.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | std::string getCurrentDir(); 5 | -------------------------------------------------------------------------------- /deja_vu_sans_mono.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum { DejaVuSansMonoLen = 333636 }; 4 | extern const unsigned char DejaVuSansMono[DejaVuSansMonoLen]; 5 | -------------------------------------------------------------------------------- /deliting_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class BaseDeletingObject 4 | { 5 | public: 6 | virtual ~BaseDeletingObject() = default; 7 | }; 8 | 9 | template 10 | class DeletingObject: public BaseDeletingObject 11 | { 12 | public: 13 | DeletingObject(T *obj): 14 | obj_(obj) 15 | {} 16 | virtual ~DeletingObject() 17 | { 18 | delete obj_; 19 | } 20 | private: 21 | T *obj_; 22 | }; 23 | -------------------------------------------------------------------------------- /dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "dialog.hpp" 2 | #include 3 | 4 | Dialog::Dialog(const std::wstring &message, Answers answers): 5 | message_(message), 6 | answers_(answers) 7 | { 8 | std::wostringstream tmp; 9 | tmp << message; 10 | bool first = true; 11 | if ((answers_ & Yes) != 0) 12 | { 13 | if (first) 14 | tmp << L" ("; 15 | else 16 | tmp << L"/"; 17 | tmp << L"y"; 18 | first = false; 19 | } 20 | if ((answers_ & No) != 0) 21 | { 22 | if (first) 23 | tmp << L"("; 24 | else 25 | tmp << L"/"; 26 | tmp << L"n"; 27 | first = false; 28 | } 29 | if ((answers_ & Cancel) != 0) 30 | { 31 | if (first) 32 | tmp << L"("; 33 | else 34 | tmp << L"/"; 35 | tmp << L"c"; 36 | first = false; 37 | } 38 | if (!first) 39 | tmp << L"): "; 40 | buffer_.push_back(tmp.str()); 41 | } 42 | 43 | std::wstring Dialog::preInsert(Coord &, const std::wstring &value) 44 | { 45 | if ((answers_ & Yes) != 0 && value.find(L'y') != std::wstring::npos) 46 | result(Yes); 47 | if ((answers_ & No) != 0 && value.find(L'n') != std::wstring::npos) 48 | result(No); 49 | if ((answers_ & Cancel) != 0 && value.find(L'c') != std::wstring::npos) 50 | result(Cancel); 51 | return L""; 52 | } 53 | 54 | int Dialog::preBackspace(Coord &, int) 55 | { 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /dialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "base_text_buffer.hpp" 3 | #include 4 | #include 5 | 6 | class Dialog: public BaseTextBuffer 7 | { 8 | public: 9 | enum Answer { Yes = 1, No = 2, Cancel = 4}; 10 | typedef unsigned Answers; 11 | Dialog(const std::wstring &message, Answers = Yes | No | Cancel); 12 | std::function result; 13 | private: 14 | std::wstring message_; 15 | Answers answers_; 16 | virtual std::wstring preInsert(Coord &cursor, const std::wstring &value); 17 | virtual int preBackspace(Coord &cursor, int value); 18 | }; 19 | -------------------------------------------------------------------------------- /full_file_name.cpp: -------------------------------------------------------------------------------- 1 | #include "full_file_name.hpp" 2 | #include "current_dir.hpp" 3 | 4 | std::string getFullFileName(const std::string &fileName) 5 | { 6 | if (fileName.empty() || fileName[0] == L'/') 7 | return fileName; 8 | else 9 | return getCurrentDir() + '/' + fileName; 10 | } 11 | -------------------------------------------------------------------------------- /full_file_name.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | std::string getFullFileName(const std::string &fileName); 5 | -------------------------------------------------------------------------------- /isearch_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "isearch_buffer.hpp" 2 | #include "screen.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | IsearchBuffer::IsearchBuffer(Screen *screen): 8 | screen_(screen), 9 | initialCursor_(screen_->cursor()) 10 | { 11 | buffer_.push_back(L"I-search:"); 12 | } 13 | 14 | void IsearchBuffer::postInsert(Coord &, const std::wstring &value) 15 | { 16 | assert(screen_->textBuffer()); 17 | std::wstring tmp; 18 | tmp.resize(value.size()); 19 | std::transform(begin(value), end(value), begin(tmp), ::tolower); 20 | searchString_ += tmp; 21 | search(); 22 | } 23 | 24 | int IsearchBuffer::preBackspace(Coord &, int value) 25 | { 26 | if (searchString_.size() > 0) 27 | return value; 28 | else 29 | return 0; 30 | } 31 | 32 | void IsearchBuffer::postBackspace(Coord &, int value) 33 | { 34 | if (searchString_.size() > 0) 35 | { 36 | searchString_.resize(searchString_.size() - value); 37 | screen_->setCursor(initialCursor_); 38 | search(); 39 | } 40 | } 41 | 42 | void IsearchBuffer::findNext() 43 | { 44 | if (searchString_.empty()) 45 | return; 46 | screen_->moveCursorRight(); 47 | if (!search()) 48 | { 49 | screen_->setCursor(initialCursor_); 50 | search(); 51 | } 52 | } 53 | 54 | bool IsearchBuffer::search() 55 | { 56 | size_t x = std::max(0, static_cast(screen_->cursor().x - searchString_.size())); 57 | int y = screen_->cursor().y; 58 | while (y < screen_->textBuffer()->size()) 59 | { 60 | auto tmpStr = (*screen_->textBuffer())[y]; 61 | std::transform(begin(tmpStr), end(tmpStr), begin(tmpStr), ::tolower); 62 | auto tmp = tmpStr.find(searchString_, x); 63 | if (tmp != std::wstring::npos) 64 | { 65 | screen_->setCursor(tmp + searchString_.size(), y); 66 | screen_->setStartSelection(Coord{ static_cast(tmp), y }); 67 | screen_->setEndSelection(Coord{ static_cast(tmp + searchString_.size()), y }); 68 | return true; 69 | } 70 | ++y; 71 | x = 0; 72 | } 73 | return false; 74 | } 75 | -------------------------------------------------------------------------------- /isearch_buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "base_text_buffer.hpp" 3 | 4 | class Screen; 5 | 6 | class IsearchBuffer: public BaseTextBuffer 7 | { 8 | public: 9 | IsearchBuffer(Screen *); 10 | void findNext(); 11 | private: 12 | Screen *screen_; 13 | std::wstring searchString_; 14 | Coord initialCursor_; 15 | bool search(); 16 | virtual void postInsert(Coord &cursor, const std::wstring &); 17 | virtual int preBackspace(Coord &cursor, int = 1); 18 | virtual void postBackspace(Coord &cursor, int = 1); 19 | }; 20 | -------------------------------------------------------------------------------- /key_event.cpp: -------------------------------------------------------------------------------- 1 | #include "key_event.hpp" 2 | 3 | KeyEvent::KeyEvent(Key key, unsigned modifiers, bool autoRepeat): 4 | autoRepeat_(autoRepeat), 5 | key_(key), 6 | modifiers_(modifiers) 7 | { 8 | } 9 | 10 | bool KeyEvent::isAutoRepeat() const 11 | { 12 | return autoRepeat_; 13 | } 14 | 15 | KeyEvent::Key KeyEvent::key() const 16 | { 17 | return key_; 18 | } 19 | 20 | unsigned KeyEvent::modifiers() const 21 | { 22 | return modifiers_; 23 | } 24 | -------------------------------------------------------------------------------- /key_event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class KeyEvent 5 | { 6 | public: 7 | enum Key { 8 | K0 = SDLK_0, 9 | K1 = SDLK_1, 10 | K2 = SDLK_2, 11 | K3 = SDLK_3, 12 | K4 = SDLK_4, 13 | K5 = SDLK_5, 14 | K6 = SDLK_6, 15 | K7 = SDLK_7, 16 | K8 = SDLK_8, 17 | K9 = SDLK_9, 18 | KA = SDLK_a, 19 | KAcBack = SDLK_AC_BACK, // the Back key (application control keypad) 20 | KAcBookmarks = SDLK_AC_BOOKMARKS, // the Bookmarks key (application control keypad) 21 | KAcForward = SDLK_AC_FORWARD, // the Forward key (application control keypad) 22 | KAcHome = SDLK_AC_HOME, // the Home key (application control keypad) 23 | KAcRefresh = SDLK_AC_REFRESH,// the Refresh key (application control keypad) 24 | KAcSearch = SDLK_AC_SEARCH, // the Search key (application control keypad) 25 | KAcStop = SDLK_AC_STOP, // the Stop key (application control keypad) 26 | KAgain = SDLK_AGAIN, // the Again key (Redo) 27 | KAltErase = SDLK_ALTERASE, // Erase-Eaze 28 | KQuote = SDLK_QUOTE, 29 | KApplication = SDLK_APPLICATION, // the Application / Compose / Context Menu (Windows) key 30 | KAudioMute = SDLK_AUDIOMUTE, // the Mute volume key 31 | KAudioNext = SDLK_AUDIONEXT, // the Next Track media key 32 | KAudioPlay = SDLK_AUDIOPLAY, // the Play media key 33 | KAudioPrev = SDLK_AUDIOPREV, // the Previous Track media key 34 | KAudioStop = SDLK_AUDIOSTOP, // the Stop media key 35 | KB = SDLK_b, 36 | KBackslash = SDLK_BACKSLASH, 37 | KBackspace = SDLK_BACKSPACE, 38 | KBrightnessDown = SDLK_BRIGHTNESSDOWN, 39 | KBrightnessUp = SDLK_BRIGHTNESSUP, 40 | KC = SDLK_c, 41 | KCalculator = SDLK_CALCULATOR, 42 | KCancel = SDLK_CANCEL, 43 | KCapsLock = SDLK_CAPSLOCK, 44 | KClear = SDLK_CLEAR, 45 | KClearAgain = SDLK_CLEARAGAIN, 46 | KComma = SDLK_COMMA, 47 | KComputer = SDLK_COMPUTER, 48 | KCopy = SDLK_COPY, 49 | KCrSel = SDLK_CRSEL, 50 | KCurrencySubUnit = SDLK_CURRENCYSUBUNIT, 51 | KCurrencyUnit = SDLK_CURRENCYUNIT, 52 | KCut = SDLK_CUT, 53 | KD = SDLK_d, 54 | KDecimalSeparator = SDLK_DECIMALSEPARATOR, 55 | KDelete = SDLK_DELETE, 56 | KDisplaySwitch = SDLK_DISPLAYSWITCH, 57 | KDown = SDLK_DOWN, 58 | KE = SDLK_e, 59 | KEject = SDLK_EJECT, 60 | KEnd = SDLK_END, 61 | KEquals = SDLK_EQUALS, 62 | KEscape = SDLK_ESCAPE, 63 | KExecute = SDLK_EXECUTE, 64 | KExSel = SDLK_EXSEL, 65 | KF = SDLK_f, 66 | KF1 = SDLK_F1, 67 | KF10 = SDLK_F10, 68 | KF11 = SDLK_F11, 69 | KF12 = SDLK_F12, 70 | KF13 = SDLK_F13, 71 | KF14 = SDLK_F14, 72 | KF15 = SDLK_F15, 73 | KF16 = SDLK_F16, 74 | KF17 = SDLK_F17, 75 | KF18 = SDLK_F18, 76 | KF19 = SDLK_F19, 77 | KF2 = SDLK_F2, 78 | KF20 = SDLK_F20, 79 | KF21 = SDLK_F21, 80 | KF22 = SDLK_F22, 81 | KF23 = SDLK_F23, 82 | KF24 = SDLK_F24, 83 | KF3 = SDLK_F3, 84 | KF4 = SDLK_F4, 85 | KF5 = SDLK_F5, 86 | KF6 = SDLK_F6, 87 | KF7 = SDLK_F7, 88 | KF8 = SDLK_F8, 89 | KF9 = SDLK_F9, 90 | KFind = SDLK_FIND, 91 | KG = SDLK_g, 92 | KBackquate = SDLK_BACKQUOTE, 93 | KH = SDLK_h, 94 | KHelp = SDLK_HELP, 95 | KHome = SDLK_HOME, 96 | KI = SDLK_i, 97 | KInsert = SDLK_INSERT, 98 | KJ = SDLK_j, 99 | KK = SDLK_k, 100 | KKbdIllumDown = SDLK_KBDILLUMDOWN, // the Keyboard Illumination Down key 101 | KKbdIllumToggle = SDLK_KBDILLUMTOGGLE, // the Keyboard Illumination Toggle key 102 | KKbdIllumUp = SDLK_KBDILLUMUP, // the Keyboard Illumination Up key 103 | KKeypad0 = SDLK_KP_0, 104 | KKeypad00 = SDLK_KP_00, 105 | KKeypad000 = SDLK_KP_000, 106 | KKeypad1 = SDLK_KP_1, 107 | KKeypad2 = SDLK_KP_2, 108 | KKeypad3 = SDLK_KP_3, 109 | KKeypad4 = SDLK_KP_4, 110 | KKeypad5 = SDLK_KP_5, 111 | KKeypad6 = SDLK_KP_6, 112 | KKeypad7 = SDLK_KP_7, 113 | KKeypad8 = SDLK_KP_8, 114 | KKeypad9 = SDLK_KP_9, 115 | KKeypadA = SDLK_KP_A, 116 | KKeypadAmpersand = SDLK_KP_AMPERSAND, 117 | KKeypadAt = SDLK_KP_AT, 118 | KKeypadB = SDLK_KP_B, 119 | KKeypadBackspace = SDLK_KP_BACKSPACE, 120 | KKeypadBinary = SDLK_KP_BINARY, 121 | KKeypadC = SDLK_KP_C, 122 | KKeypadClear = SDLK_KP_CLEAR, 123 | KKeypadClearEntry = SDLK_KP_CLEARENTRY, 124 | KKeypadColon = SDLK_KP_COLON, 125 | KKeypadComma = SDLK_KP_COMMA, 126 | KKeypadD = SDLK_KP_D, 127 | KKeypadDblAmpersand = SDLK_KP_DBLAMPERSAND, 128 | KKeypadDblVerticalBar = SDLK_KP_DBLVERTICALBAR, 129 | KKeypadDecimal = SDLK_KP_DECIMAL, 130 | KKeypadDivide = SDLK_KP_DIVIDE, 131 | KKeypadE = SDLK_KP_E, 132 | KKeypadEnter = SDLK_KP_ENTER, 133 | KKeypadEquals = SDLK_KP_EQUALS, 134 | KKeypadEqualsAs400 = SDLK_KP_EQUALSAS400, 135 | KKeypadExclam = SDLK_KP_EXCLAM, 136 | KKeypadF = SDLK_KP_F, 137 | KKeypadFreater = SDLK_KP_GREATER, 138 | KKeypadHash = SDLK_KP_HASH, 139 | KKeypadHexadecimal = SDLK_KP_HEXADECIMAL, 140 | KKeypadLeftBrace = SDLK_KP_LEFTBRACE, 141 | KKeypadLeftParen = SDLK_KP_LEFTPAREN, 142 | KKeypadLess = SDLK_KP_LESS, 143 | KKeypadMemAdd = SDLK_KP_MEMADD, 144 | KKeypadMemClear = SDLK_KP_MEMCLEAR, 145 | KKeypadMemDivide = SDLK_KP_MEMDIVIDE, 146 | KKeypadMemMultiply = SDLK_KP_MEMMULTIPLY, 147 | KKeypadMemRecall = SDLK_KP_MEMRECALL, 148 | KKeypadMemStore = SDLK_KP_MEMSTORE, 149 | KKeypadMemSubtract = SDLK_KP_MEMSUBTRACT, 150 | KKeypadMinus = SDLK_KP_MINUS, 151 | KKeypadMultiply = SDLK_KP_MULTIPLY, 152 | KKeypadOctal = SDLK_KP_OCTAL, 153 | KKeypadPercent = SDLK_KP_PERCENT, 154 | KKeypadPeriod = SDLK_KP_PERIOD, 155 | KKeypadPlus = SDLK_KP_PLUS, 156 | KKeypadPlusMinus = SDLK_KP_PLUSMINUS, 157 | KKeypadPower = SDLK_KP_POWER, 158 | KKeypadRightBrace = SDLK_KP_RIGHTBRACE, 159 | KKeypadRightParen = SDLK_KP_RIGHTPAREN, 160 | KKeypadSpace = SDLK_KP_SPACE, 161 | KKeypadTab = SDLK_KP_TAB, 162 | KKeypadVerticalBar = SDLK_KP_VERTICALBAR, 163 | KKeypadXor = SDLK_KP_XOR, 164 | KL = SDLK_l, 165 | KLeftAlt = SDLK_LALT, 166 | KLeftCtrl = SDLK_LCTRL, 167 | KLeft = SDLK_LEFT, 168 | KLeftBracket = SDLK_LEFTBRACKET, 169 | KLeftGui = SDLK_LGUI, 170 | KLeftShift = SDLK_LSHIFT, 171 | KM = SDLK_m, 172 | KMail = SDLK_MAIL, 173 | KMediaSelect = SDLK_MEDIASELECT, 174 | KMenu = SDLK_MENU, 175 | KMinus = SDLK_MINUS, 176 | KModeSwitch = SDLK_MODE, 177 | KMute = SDLK_MUTE, 178 | KN = SDLK_n, 179 | KNumlock = SDLK_NUMLOCKCLEAR, 180 | KO = SDLK_o, 181 | KOper = SDLK_OPER, 182 | KOut = SDLK_OUT, 183 | KP = SDLK_p, 184 | KPageDown = SDLK_PAGEDOWN, 185 | KPageUp = SDLK_PAGEUP, 186 | KPaste = SDLK_PASTE, 187 | KPause = SDLK_PAUSE, 188 | KPeriod = SDLK_PERIOD, 189 | KPower = SDLK_POWER, // The USB document says this is a status flag, not a physical key - but some Mac keyboards do have a power key. 190 | KPrintScreen = SDLK_PRINTSCREEN, 191 | KPrior = SDLK_PRIOR, 192 | KQ = SDLK_q, 193 | KR = SDLK_r, 194 | KRightAlt = SDLK_RALT, 195 | KRightCtrl = SDLK_RCTRL, 196 | KReturn = SDLK_RETURN, 197 | KReturn2 = SDLK_RETURN2, 198 | KRightGUI = SDLK_RGUI, // windows, command (apple), meta 199 | KRight = SDLK_RIGHT, 200 | KRightBracket = SDLK_RIGHTBRACKET, 201 | KRightShift = SDLK_RSHIFT, 202 | KS = SDLK_s, 203 | KScrollLock = SDLK_SCROLLLOCK, 204 | KSelect = SDLK_SELECT, 205 | KSemicolon = SDLK_SEMICOLON, 206 | KSeparator = SDLK_SEPARATOR, 207 | KSlash = SDLK_SLASH, 208 | KSleep = SDLK_SLEEP, 209 | KSpace = SDLK_SPACE, 210 | KStop = SDLK_STOP, 211 | KSysReq = SDLK_SYSREQ, 212 | KT = SDLK_t, 213 | KTab = SDLK_TAB, 214 | KThousandsSeparator = SDLK_THOUSANDSSEPARATOR, 215 | KU = SDLK_u, 216 | KUndo = SDLK_UNDO, 217 | KUnknown = SDLK_UNKNOWN, 218 | KUp = SDLK_UP, 219 | KV = SDLK_v, 220 | KVolumeDown = SDLK_VOLUMEDOWN, 221 | KVolumeUp = SDLK_VOLUMEUP, 222 | KW = SDLK_w, 223 | KWww = SDLK_WWW, 224 | KX = SDLK_x, 225 | KY = SDLK_y, 226 | KZ = SDLK_z, 227 | KAmpersand = SDLK_AMPERSAND, 228 | KAsterisk = SDLK_ASTERISK, 229 | KAt = SDLK_AT, 230 | KCaret = SDLK_CARET, 231 | KColon = SDLK_COLON, 232 | KDollar = SDLK_DOLLAR, 233 | KExclaim = SDLK_EXCLAIM, 234 | KGreater = SDLK_GREATER, 235 | KHash = SDLK_HASH, 236 | KLeftParen = SDLK_LEFTPAREN, 237 | KLess = SDLK_LESS, 238 | KPercent = SDLK_PERCENT, 239 | KPlus = SDLK_PLUS, 240 | KQuestion = SDLK_QUESTION, 241 | KQuoteDbl = SDLK_QUOTEDBL, 242 | KRightParen = SDLK_RIGHTPAREN, 243 | KUnderscore = SDLK_UNDERSCORE 244 | }; 245 | enum Modifier { 246 | MNone = KMOD_NONE, // 0 (no modifier is applicable) 247 | MLShift = KMOD_LSHIFT, // the left Shift key is down 248 | MRShift = KMOD_RSHIFT, // the right Shift key is down 249 | MLCtrl = KMOD_LCTRL, // the left Ctrl (Control) key is down 250 | MRCtrl = KMOD_RCTRL, // the right Ctrl (Control) key is down 251 | MLAlt = KMOD_LALT, // the left Alt key is down 252 | MRAlt = KMOD_RALT, // the right Alt key is down 253 | MLGui = KMOD_LGUI, // the left GUI key (often the Windows key) is down 254 | MRGui = KMOD_RGUI, // the right GUI key (often the Windows key) is down 255 | MNum = KMOD_NUM, // the Num Lock key (may be located on an extended keypad) is down 256 | MCaps = KMOD_CAPS, // the Caps Lock key is down 257 | MMode = KMOD_MODE, // the AltGr key is down 258 | MCtrl = KMOD_CTRL, // (KMOD_LCTRL|KMOD_RCTRL) 259 | MShift = KMOD_SHIFT, // (KMOD_LSHIFT|KMOD_RSHIFT) 260 | MAlt = KMOD_ALT, // (KMOD_LALT|KMOD_RALT) 261 | MGui = KMOD_GUI, // (KMOD_LGUI|KMOD_RGUI) 262 | MRESERVED = KMOD_RESERVED, // reserved for future use 263 | }; 264 | KeyEvent(Key, unsigned modifiers = MNone, bool autoRepeat = false); 265 | bool isAutoRepeat() const; 266 | Key key() const; 267 | unsigned modifiers() const; 268 | private: 269 | bool autoRepeat_; 270 | Key key_; 271 | unsigned modifiers_; 272 | }; 273 | -------------------------------------------------------------------------------- /layout.cpp: -------------------------------------------------------------------------------- 1 | #include "layout.hpp" 2 | #include "widget.hpp" 3 | #include 4 | #include 5 | 6 | Layout::Layout(Style style): 7 | style_(style), 8 | left_(0), 9 | top_(0), 10 | width_(0), 11 | height_(0) 12 | { 13 | } 14 | 15 | void Layout::addLayoutable(Layoutable *value) 16 | { 17 | layoutablesList_.push_back(value); 18 | value->setParentLayout(this); 19 | resize(width_, height_); 20 | } 21 | 22 | void Layout::removeLayoutable(Layoutable *value) 23 | { 24 | auto iter = std::find(begin(layoutablesList_), end(layoutablesList_), value); 25 | if (iter != end(layoutablesList_)) 26 | { 27 | layoutablesList_.erase(iter); 28 | resize(width_, height_); 29 | } 30 | } 31 | 32 | void Layout::setLeft(int value) 33 | { 34 | left_ = value; 35 | } 36 | 37 | void Layout::setTop(int value) 38 | { 39 | top_ = value; 40 | } 41 | 42 | static std::vector calculateDimensions(std::vector> minMax, int dimension) 43 | { 44 | std::vector result; 45 | std::vector untouchableList; 46 | for (size_t i = 0; i < minMax.size(); ++i) 47 | { 48 | result.push_back((i + 1) * dimension / minMax.size() - i * dimension / minMax.size()); 49 | untouchableList.push_back(false); 50 | } 51 | auto touchableCount = minMax.size(); 52 | auto remDim = dimension; 53 | auto resultIter = begin(result); 54 | auto untouchableListIter = begin(untouchableList); 55 | for (auto &l: minMax) 56 | { 57 | if (*resultIter < l.first) 58 | { 59 | *resultIter = l.first; 60 | *untouchableListIter = true; 61 | --touchableCount; 62 | remDim -= *resultIter; 63 | } 64 | else if (*resultIter > l.second) 65 | { 66 | *resultIter = l.second; 67 | *untouchableListIter = true; 68 | --touchableCount; 69 | remDim -= *resultIter; 70 | } 71 | ++resultIter; 72 | ++untouchableListIter; 73 | } 74 | untouchableListIter = begin(untouchableList); 75 | int i = 0; 76 | for (auto &dim: result) 77 | { 78 | if (!*untouchableListIter) 79 | dim = (i + 1) * remDim / touchableCount - i * remDim / touchableCount; 80 | ++i; 81 | ++untouchableListIter; 82 | } 83 | return result; 84 | } 85 | 86 | void Layout::resize(int width, int height) 87 | { 88 | width_ = width; 89 | height_ = height; 90 | if (style_ == Vertical) 91 | { 92 | std::vector > minMax; 93 | for (auto &l: layoutablesList_) 94 | minMax.push_back(std::make_pair(l->minHeight(), l->maxHeight())); 95 | auto heights = calculateDimensions(minMax, height_); 96 | auto heightsIter = begin(heights); 97 | auto t = top_; 98 | for (auto &l: layoutablesList_) 99 | { 100 | l->setLeft(left_); 101 | l->setTop(t); 102 | l->resize(width, *heightsIter); 103 | t += *heightsIter; 104 | ++heightsIter; 105 | } 106 | } 107 | else // if style == Horizontal 108 | { 109 | std::vector > minMax; 110 | for (auto &l: layoutablesList_) 111 | minMax.push_back(std::make_pair(l->minWidth(), l->maxWidth())); 112 | auto widths = calculateDimensions(minMax, width_); 113 | auto widthsIter = begin(widths); 114 | auto lt = left_; 115 | for (auto &l: layoutablesList_) 116 | { 117 | l->setLeft(lt); 118 | l->setTop(top_); 119 | l->resize(*widthsIter, height_); 120 | lt += *widthsIter; 121 | ++widthsIter; 122 | } 123 | } 124 | } 125 | 126 | int Layout::maxHeight() const 127 | { 128 | return std::numeric_limits::max(); 129 | } 130 | 131 | int Layout::minHeight() const 132 | { 133 | return 0; 134 | } 135 | 136 | int Layout::maxWidth() const 137 | { 138 | return std::numeric_limits::max(); 139 | } 140 | 141 | int Layout::minWidth() const 142 | { 143 | return 0; 144 | } 145 | 146 | void Layout::setStyle(Style value) 147 | { 148 | if (style_ != value) 149 | { 150 | style_ = value; 151 | resize(width_, height_); 152 | } 153 | } 154 | 155 | std::vector Layout::children() const 156 | { 157 | return layoutablesList_; 158 | } 159 | -------------------------------------------------------------------------------- /layout.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "layoutable.hpp" 3 | #include 4 | 5 | class Widget; 6 | 7 | class Layout: public Layoutable 8 | { 9 | public: 10 | enum Style { Vertical, Horizontal }; 11 | Layout(Style); 12 | void addLayoutable(Layoutable *); 13 | void removeLayoutable(Layoutable *); 14 | virtual void setLeft(int); 15 | virtual void setTop(int); 16 | virtual void resize(int width, int height); 17 | virtual int maxHeight() const; 18 | virtual int minHeight() const; 19 | virtual int maxWidth() const; 20 | virtual int minWidth() const; 21 | void setStyle(Style); 22 | std::vector children() const; 23 | private: 24 | std::vector layoutablesList_; 25 | Style style_; 26 | int left_; 27 | int top_; 28 | int width_; 29 | int height_; 30 | }; 31 | -------------------------------------------------------------------------------- /layoutable.cpp: -------------------------------------------------------------------------------- 1 | #include "layoutable.hpp" 2 | #include "layout.hpp" 3 | 4 | Layoutable::Layoutable(): 5 | parentLayout_(nullptr) 6 | {} 7 | 8 | Layoutable::~Layoutable() 9 | { 10 | if (parentLayout_) 11 | parentLayout_->removeLayoutable(this); 12 | } 13 | 14 | void Layoutable::setParentLayout(Layout *value) 15 | { 16 | parentLayout_ = value; 17 | } 18 | 19 | Layout *Layoutable::parentLayout() const 20 | { 21 | return parentLayout_; 22 | } 23 | -------------------------------------------------------------------------------- /layoutable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Layout; 4 | 5 | class Layoutable 6 | { 7 | public: 8 | Layoutable(); 9 | virtual ~Layoutable(); 10 | virtual void setLeft(int) = 0; 11 | virtual void setTop(int) = 0; 12 | virtual void resize(int width, int height) = 0; 13 | virtual int maxHeight() const = 0; 14 | virtual int minHeight() const = 0; 15 | virtual int maxWidth() const = 0; 16 | virtual int minWidth() const = 0; 17 | void setParentLayout(Layout *); 18 | Layout *parentLayout() const; 19 | private: 20 | Layout *parentLayout_; 21 | }; 22 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.hpp" 2 | #include "application.hpp" 3 | #include "main_window.hpp" 4 | 5 | int main(int argc, char **argv) 6 | { 7 | Application a(argc, argv); 8 | MainWindow mainWindow; 9 | return a.exec(); 10 | } 11 | -------------------------------------------------------------------------------- /main_window.cpp: -------------------------------------------------------------------------------- 1 | #include "main_window.hpp" 2 | #include "key_event.hpp" 3 | #include "open_dialog.hpp" 4 | #include "save_dialog.hpp" 5 | #include "text_file.hpp" 6 | #include "dialog.hpp" 7 | #include "application.hpp" 8 | #include "full_file_name.hpp" 9 | #include 10 | #include 11 | #include 12 | 13 | MainWindow::MainWindow(Widget *parent): 14 | Widget(parent), 15 | activeScreen_(nullptr), 16 | layout_(Layout::Vertical), 17 | tabs_(this), 18 | statusBar_(this), 19 | screenLayout_(Layout::Vertical) 20 | { 21 | activeScreen_ = new Screen(this); 22 | using namespace std::placeholders; 23 | tabs_.setTextBuffer = std::bind(&MainWindow::setTextBuffer, this, _1); 24 | tabs_.deleteTextBuffer = std::bind(&MainWindow::deleteTextBuffer, this, _1); 25 | activeScreen_->setStatusBar(&statusBar_); 26 | setLayout(&layout_); 27 | layout_.addLayoutable(&tabs_); 28 | screenLayout_.addLayoutable(activeScreen_); 29 | layout_.addLayoutable(&screenLayout_); 30 | layout_.addLayoutable(&statusBar_); 31 | activeScreen_->setFocus(); 32 | } 33 | 34 | bool MainWindow::keyPressEvent(KeyEvent &e) 35 | { 36 | std::cout << __func__ << " key: " << e.key() << " modifiers: " << e.modifiers() << std::endl; 37 | bool result1 = true; 38 | if ((e.modifiers() & (KeyEvent::MLCtrl | KeyEvent::MRCtrl)) != 0) 39 | { 40 | switch (e.key()) 41 | { 42 | case KeyEvent::K2: 43 | wholeScreen(); 44 | break; 45 | case KeyEvent::K3: 46 | split(Layout::Vertical); 47 | break; 48 | case KeyEvent::K4: 49 | split(Layout::Horizontal); 50 | break; 51 | case KeyEvent::KO: 52 | { 53 | auto &buffersList = tabs_.textBuffersList(); 54 | auto tmp = std::find_if(std::begin(buffersList), std::end(buffersList), 55 | [](BaseTextBuffer *x) 56 | { 57 | return dynamic_cast(x); 58 | }); 59 | if (tmp == std::end(buffersList)) 60 | { 61 | auto openDialog = new OpenDialog(activeScreen_); 62 | using namespace std::placeholders; 63 | openDialog->openFile = std::bind(&MainWindow::openFile, this, _1, _2); 64 | tabs_.addTextBuffer(openDialog); 65 | } 66 | else 67 | tabs_.setActiveTextBuffer(*tmp); 68 | break; 69 | } 70 | case KeyEvent::KS: 71 | save(); 72 | break; 73 | case KeyEvent::KN: 74 | tabs_.addTextBuffer(new TextFile); 75 | break; 76 | case KeyEvent::KW: 77 | if (dynamic_cast(tabs_.activeTextBuffer()) && tabs_.activeTextBuffer()->isModified()) 78 | { 79 | if (!statusBar_.textBuffer()) 80 | { 81 | auto d = new Dialog(L"The file is modified. Do you want to save it before closing?"); 82 | statusBar_.setTextBuffer(d); 83 | using namespace std::placeholders; 84 | d->result = std::bind(&MainWindow::closeActiveTextBuffer, this, _1); 85 | } 86 | } 87 | else 88 | tabs_.closeActiveTextBuffer(); 89 | break; 90 | case KeyEvent::KPageUp: 91 | case KeyEvent::KLeft: 92 | tabs_.switchToPrevTextBuffer(); 93 | break; 94 | case KeyEvent::KPageDown: 95 | case KeyEvent::KRight: 96 | tabs_.switchToNextTextBuffer(); 97 | break; 98 | case KeyEvent::KTab: 99 | switchToNextScreen(); 100 | break; 101 | default: 102 | result1 = false; 103 | } 104 | } 105 | else if ((e.modifiers() & (KeyEvent::MLAlt | KeyEvent::MRAlt)) != 0) 106 | { 107 | switch (e.key()) 108 | { 109 | case KeyEvent::KLeft: 110 | switchToPrevScreen(); 111 | break; 112 | case KeyEvent::KRight: 113 | switchToNextScreen(); 114 | break; 115 | default: 116 | result1 = false; 117 | } 118 | } 119 | else 120 | result1 = false; 121 | 122 | bool result2 = true; 123 | if ((e.modifiers() & (KeyEvent::MCtrl | KeyEvent::MShift)) != 0) 124 | { 125 | switch (e.key()) 126 | { 127 | case KeyEvent::KLeft: 128 | tabs_.moveTextBufferLeft(); 129 | break; 130 | case KeyEvent::KRight: 131 | tabs_.moveTextBufferRight(); 132 | break; 133 | default: 134 | result2 = false; 135 | } 136 | } 137 | else 138 | result2 = false; 139 | 140 | return result1 || result2; 141 | } 142 | 143 | void MainWindow::openFile(OpenDialog *, const std::string &fileName) 144 | { 145 | auto &buffersList = tabs_.textBuffersList(); 146 | auto fullFileName = getFullFileName(fileName); 147 | auto tmp = std::find_if(std::begin(buffersList), std::end(buffersList), 148 | [&fullFileName](BaseTextBuffer *x) 149 | { 150 | if (auto textFile = dynamic_cast(x)) 151 | return textFile->fileName() == fullFileName; 152 | else 153 | return false; 154 | }); 155 | if (tmp == std::end(buffersList)) 156 | tabs_.addTextBuffer(new TextFile(fileName)); 157 | else 158 | tabs_.setActiveTextBuffer(*tmp); 159 | } 160 | 161 | void MainWindow::saveAs(SaveDialog *sender, TextFile *textFile, const std::string &fileName) 162 | { 163 | tabs_.closeTextBuffer(sender); 164 | tabs_.setActiveTextBuffer(textFile); 165 | textFile->saveAs(fileName); 166 | } 167 | 168 | void MainWindow::saveAndClose(SaveDialog *sender, TextFile *textFile, const std::string &fileName) 169 | { 170 | tabs_.closeTextBuffer(sender); 171 | tabs_.closeTextBuffer(textFile); 172 | textFile->saveAs(fileName); 173 | } 174 | 175 | void MainWindow::closeActiveTextBuffer(Dialog::Answer value) 176 | { 177 | auto d = statusBar_.textBuffer(); 178 | Application::instance()->queueDelete(d); 179 | statusBar_.setTextBuffer(nullptr); 180 | if (auto textFile = dynamic_cast(activeScreen_->textBuffer())) 181 | { 182 | switch (value) 183 | { 184 | case Dialog::Yes: 185 | if (textFile->fileName().empty()) 186 | { 187 | auto saveDialog = new SaveDialog(activeScreen_, textFile); 188 | tabs_.addTextBuffer(saveDialog); 189 | activeScreen_->setCursor(0, 1); 190 | using namespace std::placeholders; 191 | saveDialog->saveAs = std::bind(&MainWindow::saveAndClose, this, _1, _2, _3); 192 | } 193 | else 194 | { 195 | textFile->save(); 196 | tabs_.closeActiveTextBuffer(); 197 | } 198 | break; 199 | case Dialog::No: 200 | tabs_.closeActiveTextBuffer(); 201 | break; 202 | case Dialog::Cancel: 203 | break; 204 | 205 | } 206 | tabs_.update(); 207 | } 208 | } 209 | 210 | void MainWindow::save() 211 | { 212 | if (auto textFile = dynamic_cast(activeScreen_->textBuffer())) 213 | { 214 | if (textFile->fileName().empty()) 215 | { 216 | auto saveDialog = new SaveDialog(activeScreen_, textFile); 217 | tabs_.addTextBuffer(saveDialog); 218 | activeScreen_->setCursor(0, 1); 219 | using namespace std::placeholders; 220 | saveDialog->saveAs = std::bind(&MainWindow::saveAs, this, _1, _2, _3); 221 | } 222 | else 223 | textFile->save(); 224 | tabs_.update(); 225 | } 226 | } 227 | 228 | static void markForDeleteRecursively(Layoutable *value) 229 | { 230 | if (auto layout = dynamic_cast(value)) 231 | { 232 | auto children = layout->children(); 233 | for (auto child: children) 234 | { 235 | markForDeleteRecursively(child); 236 | child->parentLayout()->removeLayoutable(child); 237 | Application::instance()->queueDelete(child); 238 | } 239 | } 240 | } 241 | 242 | void MainWindow::wholeScreen() 243 | { 244 | markForDeleteRecursively(&screenLayout_); 245 | auto screen = activeScreen_; 246 | activeScreen_ = new Screen(this); 247 | activeScreen_->setStatusBar(&statusBar_); 248 | screenLayout_.addLayoutable(activeScreen_); 249 | activeScreen_->setTextBuffer(screen->textBuffer()); 250 | activeScreen_->setFocus(); 251 | } 252 | 253 | void MainWindow::split(Layout::Style style) 254 | { 255 | auto layout = activeScreen_->parentLayout(); 256 | layout->setStyle(style); 257 | auto l1 = new Layout(Layout::Vertical); 258 | auto l2 = new Layout(Layout::Vertical); 259 | layout->removeLayoutable(activeScreen_); 260 | layout->addLayoutable(l1); 261 | layout->addLayoutable(l2); 262 | l1->addLayoutable(activeScreen_); 263 | auto s2 = new Screen(this); 264 | s2->setStatusBar(&statusBar_); 265 | s2->setTextBuffer(activeScreen_->textBuffer()); 266 | l2->addLayoutable(s2); 267 | } 268 | 269 | static std::vector getListOfScreens(Layoutable *l) 270 | { 271 | std::vector res; 272 | if (auto screen = dynamic_cast(l)) 273 | res.push_back(screen); 274 | else if (auto layout = dynamic_cast(l)) 275 | { 276 | auto children = layout->children(); 277 | for (Layoutable *child: children) 278 | { 279 | std::vector tmp = getListOfScreens(child); 280 | res.insert(end(res), begin(tmp), end(tmp)); 281 | } 282 | } 283 | return res; 284 | } 285 | 286 | void MainWindow::switchToPrevScreen() 287 | { 288 | std::vector list = getListOfScreens(&screenLayout_); 289 | auto iter = std::find(begin(list), end(list), activeScreen_); 290 | assert(iter != end(list)); 291 | if (iter != begin(list)) 292 | --iter; 293 | else 294 | iter = end(list) - 1; 295 | activeScreen_ = *iter; 296 | tabs_.setActiveTextBuffer(activeScreen_->textBuffer()); 297 | activeScreen_->setFocus(); 298 | } 299 | 300 | void MainWindow::switchToNextScreen() 301 | { 302 | std::vector list = getListOfScreens(&screenLayout_); 303 | auto iter = std::find(begin(list), end(list), activeScreen_); 304 | assert(iter != end(list)); 305 | if (iter + 1 != end(list)) 306 | ++iter; 307 | else 308 | iter = begin(list); 309 | activeScreen_ = *iter; 310 | tabs_.setActiveTextBuffer(activeScreen_->textBuffer()); 311 | activeScreen_->setFocus(); 312 | } 313 | 314 | void MainWindow::setTextBuffer(BaseTextBuffer *textBuffer) 315 | { 316 | activeScreen_->setTextBuffer(textBuffer); 317 | } 318 | 319 | static void internalDeleteTextBuffer(Layoutable *l, BaseTextBuffer *textBuffer) 320 | { 321 | if (auto screen = dynamic_cast(l)) 322 | { 323 | if (screen->textBuffer() == textBuffer) 324 | screen->setTextBuffer(nullptr); 325 | } 326 | else if (auto layout = dynamic_cast(l)) 327 | { 328 | auto children = layout->children(); 329 | for (Layoutable *child: children) 330 | internalDeleteTextBuffer(child, textBuffer); 331 | } 332 | } 333 | 334 | 335 | void MainWindow::deleteTextBuffer(BaseTextBuffer *textBuffer) 336 | { 337 | internalDeleteTextBuffer(&screenLayout_, textBuffer); 338 | } 339 | -------------------------------------------------------------------------------- /main_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "tabs.hpp" 3 | #include "screen.hpp" 4 | #include "status_bar.hpp" 5 | #include "layout.hpp" 6 | #include "widget.hpp" 7 | #include "dialog.hpp" 8 | #include 9 | 10 | class OpenDialog; 11 | class SaveDialog; 12 | class TextFile; 13 | 14 | class MainWindow: public Widget 15 | { 16 | public: 17 | MainWindow(Widget *parent = nullptr); 18 | protected: 19 | virtual bool keyPressEvent(KeyEvent &); 20 | private: 21 | Screen *activeScreen_; 22 | Layout layout_; 23 | Tabs tabs_; 24 | StatusBar statusBar_; 25 | Layout screenLayout_; 26 | void openFile(OpenDialog *, const std::string &); 27 | void saveAs(SaveDialog *sender, TextFile *textFile, const std::string &fileName); 28 | void saveAndClose(SaveDialog *sender, TextFile *textFile, const std::string &fileName); 29 | void closeActiveTextBuffer(Dialog::Answer); 30 | void save(); 31 | void wholeScreen(); 32 | void split(Layout::Style); 33 | void switchToPrevScreen(); 34 | void switchToNextScreen(); 35 | void setTextBuffer(BaseTextBuffer *); 36 | void deleteTextBuffer(BaseTextBuffer *); 37 | }; 38 | -------------------------------------------------------------------------------- /open_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "open_dialog.hpp" 2 | #include "to_utf16.hpp" 3 | #include "to_utf8.hpp" 4 | #include "text_file.hpp" 5 | #include "screen.hpp" 6 | #include "current_dir.hpp" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | OpenDialog::OpenDialog(Screen *screen): 15 | screen_(screen) 16 | { 17 | scanDirectory(); 18 | setReadOnly(true); 19 | } 20 | 21 | void OpenDialog::postInsert(Coord &cursor, const std::wstring &value) 22 | { 23 | if (value.find(L"\n") != std::wstring::npos && cursor.y > 0) 24 | { 25 | struct stat buf; 26 | auto fileName = toUtf8(buffer_[screen_->cursor().y]); 27 | stat(fileName.c_str(), &buf); 28 | if (S_ISDIR(buf.st_mode)) 29 | { 30 | auto folderName = buffer_[0]; 31 | folderName = { begin(folderName) + folderName.rfind(L"/") + 1, end(folderName) - 1 }; 32 | auto res = chdir(fileName.c_str()); 33 | (void)res; 34 | scanDirectory(); 35 | int line = -1; 36 | for (size_t i = 0; i < buffer_.size(); ++i) 37 | if (buffer_[i] == folderName) 38 | { 39 | line = i; 40 | break; 41 | } 42 | if (line != -1) 43 | cursor = { 0, line }; 44 | else 45 | cursor = { 0, 1 }; 46 | } 47 | else 48 | { 49 | openFile(this, fileName); 50 | cursor = { 0, 0 }; 51 | } 52 | } 53 | } 54 | 55 | void OpenDialog::scanDirectory() 56 | { 57 | buffer_.clear(); 58 | auto currentDir = toUtf16(getCurrentDir()); 59 | setName(std::wstring{begin(currentDir) + currentDir.rfind(L'/') + 1, end(currentDir)}); 60 | buffer_.push_back(currentDir + L":"); 61 | 62 | auto d = opendir("."); 63 | while (auto de = readdir(d)) 64 | { 65 | auto fileName = toUtf16(de->d_name); 66 | if (fileName == L".." || 67 | (fileName.size() > 0 && fileName[0] != L'.' && fileName[fileName.size() - 1] != L'~')) 68 | buffer_.push_back(fileName); 69 | } 70 | closedir(d); 71 | std::sort(begin(buffer_) + 1, end(buffer_)); 72 | } 73 | -------------------------------------------------------------------------------- /open_dialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "base_text_buffer.hpp" 3 | #include 4 | 5 | class Screen; 6 | 7 | class OpenDialog: public BaseTextBuffer 8 | { 9 | public: 10 | OpenDialog(Screen *screen); 11 | std::function openFile; 12 | private: 13 | Screen *screen_; 14 | void scanDirectory(); 15 | virtual void postInsert(Coord &cursor, const std::wstring &); 16 | }; 17 | -------------------------------------------------------------------------------- /paint_device.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class PaintDevice 5 | { 6 | public: 7 | virtual ~PaintDevice() = default; 8 | virtual SDL_Renderer *renderer() = 0; 9 | virtual int width() const = 0; 10 | virtual int height() const = 0; 11 | }; 12 | -------------------------------------------------------------------------------- /paint_event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class PaintEvent 4 | { 5 | public: 6 | }; 7 | -------------------------------------------------------------------------------- /painter.cpp: -------------------------------------------------------------------------------- 1 | #include "painter.hpp" 2 | #include "paint_device.hpp" 3 | #include "deja_vu_sans_mono.hpp" 4 | #include "color.hpp" 5 | #include 6 | #include 7 | 8 | Painter::Painter(PaintDevice *paintDevice): 9 | renderer_(paintDevice->renderer()), 10 | width_(paintDevice->width()), 11 | height_(paintDevice->height()) 12 | { 13 | font_ = TTF_OpenFontRW(SDL_RWFromMem((void *)DejaVuSansMono, sizeof(DejaVuSansMono)), 1, 10); 14 | if (font_ == nullptr) 15 | throw std::runtime_error("TTF_OpenFont"); 16 | } 17 | 18 | Painter::~Painter() 19 | { 20 | for (auto &i: glyphCache_) 21 | SDL_DestroyTexture(std::get<0>(i.second)); 22 | TTF_CloseFont(font_); 23 | } 24 | 25 | void Painter::drawLine(int x1, int y1, int x2, int y2) 26 | { 27 | SDL_RenderDrawLine(renderer_, x1, y1, x2, y2); 28 | } 29 | 30 | void Painter::drawPoint(int x, int y) 31 | { 32 | SDL_RenderDrawPoint(renderer_, x, y); 33 | } 34 | 35 | void Painter::drawRect(int x, int y, int width, int height) 36 | { 37 | SDL_Rect rect = { x, y, width, height }; 38 | SDL_RenderFillRect(renderer_, &rect); 39 | } 40 | 41 | void Painter::setColor(Color color) 42 | { 43 | SDL_SetRenderDrawColor(renderer_, (color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); 44 | } 45 | 46 | void Painter::renderGlyph(wchar_t ch, int x, int y, Color fg, Color bg) 47 | { 48 | SDL_Texture *texture; 49 | int width, height; 50 | auto iter = glyphCache_.find(std::make_tuple(ch, fg, bg)); 51 | if (iter != end(glyphCache_)) 52 | { 53 | texture = std::get<0>(iter->second); 54 | width = std::get<1>(iter->second); 55 | height = std::get<2>(iter->second); 56 | glyphCacheAge_.erase(std::get<3>(iter->second)); 57 | std::get<3>(iter->second) = glyphCacheAge_.insert(end(glyphCacheAge_), iter->first); 58 | } 59 | else 60 | { 61 | if (glyphCache_.size() > 4096) 62 | { 63 | const auto itemsCount = glyphCache_.size(); 64 | for (size_t j = 0; j < itemsCount / 2; ++j) 65 | { 66 | auto key = glyphCacheAge_.front(); 67 | auto iter = glyphCache_.find(key); 68 | SDL_DestroyTexture(std::get<0>(iter->second)); 69 | glyphCache_.erase(iter); 70 | glyphCacheAge_.erase(begin(glyphCacheAge_)); 71 | } 72 | } 73 | auto surf = TTF_RenderGlyph_Shaded(font_, ch, toSdlColor(fg), toSdlColor(bg)); 74 | if (surf == nullptr) 75 | { 76 | setColor(bg); 77 | drawRect(x, y, glyphWidth(), glyphHeight()); 78 | return; 79 | } 80 | texture = SDL_CreateTextureFromSurface(renderer_, surf); 81 | if (texture == nullptr) 82 | { 83 | SDL_FreeSurface(surf); 84 | throw std::runtime_error("CreateTexture"); 85 | } 86 | width = surf->w; 87 | height = surf->h; 88 | SDL_FreeSurface(surf); 89 | GlyphCacheKey key = std::make_tuple(ch, fg, bg); 90 | glyphCache_.insert(std::make_pair(key, std::make_tuple(texture, width, height, glyphCacheAge_.insert(end(glyphCacheAge_), key)))); 91 | } 92 | SDL_Rect rect { x, y, width, height }; 93 | SDL_RenderCopy(renderer_, texture, nullptr, &rect); 94 | } 95 | 96 | int Painter::glyphWidth() const 97 | { 98 | int minx, maxx, miny, maxy, advance; 99 | TTF_GlyphMetrics(font_, L'W', &minx, &maxx, &miny, &maxy, &advance); 100 | return advance; 101 | } 102 | 103 | int Painter::glyphHeight() const 104 | { 105 | return TTF_FontLineSkip(font_) + 1; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /painter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "color.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class PaintDevice; 11 | 12 | class Painter 13 | { 14 | public: 15 | Painter(PaintDevice *); 16 | ~Painter(); 17 | void drawLine(int x1, int y1, int x2, int y2); 18 | void drawPoint(int x, int y); 19 | void drawRect(int x1, int y1, int x2, int y2); 20 | void setColor(Color); 21 | void renderGlyph(wchar_t ch, int x, int y, Color fg, Color bg); 22 | int glyphWidth() const; 23 | int glyphHeight() const; 24 | private: 25 | SDL_Renderer *renderer_; 26 | int width_; 27 | int height_; 28 | TTF_Font *font_; 29 | typedef std::tuple GlyphCacheKey; 30 | typedef std::map::iterator> > GlyphCache; 31 | GlyphCache glyphCache_; 32 | std::list glyphCacheAge_; 33 | }; 34 | -------------------------------------------------------------------------------- /resize_event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ResizeEvent 4 | { 5 | int width; 6 | int height; 7 | }; 8 | -------------------------------------------------------------------------------- /save_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "save_dialog.hpp" 2 | #include "to_utf16.hpp" 3 | #include "to_utf8.hpp" 4 | #include "text_file.hpp" 5 | #include "screen.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | SaveDialog::SaveDialog(Screen *screen, TextFile *textFile): 16 | screen_(screen), 17 | textFile_(textFile) 18 | { 19 | scanDirectory(); 20 | } 21 | 22 | std::wstring SaveDialog::preInsert(Coord &cursor, const std::wstring &value) 23 | { 24 | if (cursor.y == 1 && value.find(L"\n") == std::wstring::npos) 25 | return value; 26 | else if (value.find(L"\n") != std::wstring::npos && cursor.y > 0) 27 | { 28 | struct stat buf; 29 | auto fileName = toUtf8(buffer_[screen_->cursor().y]); 30 | stat(fileName.c_str(), &buf); 31 | if (S_ISDIR(buf.st_mode)) 32 | { 33 | auto folderName = buffer_[0]; 34 | folderName = { begin(folderName) + folderName.rfind(L"/") + 1, end(folderName) - 1 }; 35 | int res = chdir(fileName.c_str()); 36 | if (res != 0) 37 | { 38 | throw std::runtime_error(std::string("chdir ") + strerror(errno) + " " + std::to_string(errno)); 39 | } 40 | scanDirectory(); 41 | int line = -1; 42 | for (size_t i = 0; i < buffer_.size(); ++i) 43 | if (buffer_[i] == folderName) 44 | { 45 | line = i; 46 | break; 47 | } 48 | if (line != -1) 49 | cursor = { 0, line }; 50 | else 51 | cursor = { 0, 1 }; 52 | } 53 | else 54 | saveAs(this, textFile_, fileName); 55 | } 56 | return L""; 57 | } 58 | 59 | int SaveDialog::preDelete(const Coord cursor, int value) 60 | { 61 | if (cursor.y == 1 && cursor.x <= static_cast(buffer_[screen_->cursor().y].size()) - value) 62 | return value; 63 | else 64 | return 0; 65 | } 66 | 67 | int SaveDialog::preBackspace(Coord &cursor, int value) 68 | { 69 | if (cursor.y == 1 && cursor.x >= value) 70 | return value; 71 | else 72 | return 0; 73 | } 74 | 75 | 76 | void SaveDialog::scanDirectory() 77 | { 78 | buffer_.clear(); 79 | char *tmp = getcwd(nullptr, MAXPATHLEN); 80 | auto currentDir = toUtf16(tmp); 81 | free(tmp); 82 | setName(std::wstring{begin(currentDir) + currentDir.rfind(L'/'), end(currentDir)}); 83 | buffer_.push_back(currentDir + L":"); 84 | 85 | auto d = opendir("."); 86 | buffer_.push_back(L""); 87 | while (auto de = readdir(d)) 88 | { 89 | auto fileName = toUtf16(de->d_name); 90 | if (fileName == L".." || 91 | (fileName.size() > 0 && fileName[0] != L'.' && fileName[fileName.size() - 1] != L'~')) 92 | buffer_.push_back(fileName); 93 | } 94 | closedir(d); 95 | std::sort(begin(buffer_) + 2, end(buffer_)); 96 | } 97 | -------------------------------------------------------------------------------- /save_dialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "base_text_buffer.hpp" 3 | #include 4 | 5 | class Screen; 6 | class TextFile; 7 | 8 | class SaveDialog: public BaseTextBuffer 9 | { 10 | public: 11 | SaveDialog(Screen *screen, TextFile *textFile); 12 | std::function saveAs; 13 | private: 14 | Screen *screen_; 15 | TextFile *textFile_; 16 | void scanDirectory(); 17 | virtual std::wstring preInsert(Coord &cursor, const std::wstring &); 18 | virtual int preDelete(const Coord cursor, int = 1); 19 | virtual int preBackspace(Coord &cursor, int = 1); 20 | }; 21 | -------------------------------------------------------------------------------- /screen.cpp: -------------------------------------------------------------------------------- 1 | #include "screen.hpp" 2 | #include "status_bar.hpp" 3 | #include "isearch_buffer.hpp" 4 | #include "base_text_buffer.hpp" 5 | #include "painter.hpp" 6 | #include "key_event.hpp" 7 | #include "resize_event.hpp" 8 | #include "text_input_event.hpp" 9 | #include "to_utf16.hpp" 10 | #include "to_utf8.hpp" 11 | #include 12 | #include 13 | 14 | Screen::Char::Char(wchar_t ach, Color afg, Color abg): 15 | ch(ach), 16 | fg(afg), 17 | bg(abg) 18 | {} 19 | 20 | bool Screen::Char::operator!=(const Char &value) const 21 | { 22 | return ch != value.ch || fg != value.fg || bg != value.bg; 23 | } 24 | 25 | 26 | Screen::Screen(Widget *parent): 27 | Widget(parent), 28 | cursor_{0, 0}, 29 | startSelection_{-1, -1}, 30 | endSelection_{-1, -1}, 31 | hScroll_{0}, 32 | vScroll_{0}, 33 | textBuffer_{nullptr}, 34 | statusBar_{nullptr}, 35 | prevX_{-1}, 36 | prevY_{-1} 37 | { 38 | Painter p(this); 39 | glyphWidth_ = p.glyphWidth(); 40 | glyphHeight_ = p.glyphHeight(); 41 | ResizeEvent e{width(), height()}; 42 | resizeEvent(e); 43 | } 44 | 45 | Screen::~Screen() 46 | { 47 | if (statusBar_) 48 | delete statusBar_->textBuffer(); 49 | } 50 | 51 | void Screen::paintEvent(PaintEvent &) 52 | { 53 | Painter p(this); 54 | for (size_t y = 0; y < ch_.size(); ++y) 55 | for (size_t x = 0; x < ch_[y].size(); ++x) 56 | { 57 | const auto &prevC = prevCh_[y][x]; 58 | const auto &c = ch_[y][x]; 59 | if (prevC != c || (prevX_ == static_cast(x) && prevY_ == static_cast(y))) 60 | p.renderGlyph(c.ch, x * glyphWidth_, y * glyphHeight_, c.fg, c.bg); 61 | } 62 | auto xx = cursor_.x - hScroll_; 63 | auto yy = cursor_.y - vScroll_; 64 | if (hasFocus()) 65 | p.setColor(Red); 66 | else 67 | p.setColor(Gray); 68 | p.drawLine(xx * glyphWidth_, yy * glyphHeight_, 69 | xx * glyphWidth_, (yy + 1) * glyphHeight_ - 1); 70 | p.drawLine(xx * glyphWidth_ + 1, yy * glyphHeight_, 71 | xx * glyphWidth_ + 1, (yy + 1) * glyphHeight_ - 1); 72 | p.setColor(Gray90); 73 | p.drawLine(0, height() - 1, width() - 1, height() - 1); 74 | p.drawLine(width() - 1, 0, width() - 1, height() - 1); 75 | if (textBuffer_) 76 | { 77 | int sbHeight = std::max(3, 78 | textBuffer_->size() != 0 ? 79 | heightCh() * height() / textBuffer_->size() : 80 | heightCh() * height()); 81 | int sbTop = textBuffer_->size() != 0 ? 82 | vScroll() * height() / textBuffer_->size() : 83 | 0; 84 | p.setColor(Blue); 85 | p.drawLine(width() - 1, sbTop, width() - 1, sbTop + sbHeight); 86 | } 87 | prevX_ = xx; 88 | prevY_ = yy; 89 | prevCh_ = ch_; 90 | } 91 | 92 | bool Screen::keyPressEvent(KeyEvent &e) 93 | { 94 | std::cout << __func__ << " key: " << e.key() << " modifiers: " << e.modifiers() << std::endl; 95 | if (!textBuffer_) 96 | return false; 97 | bool result = true; 98 | switch (e.modifiers()) 99 | { 100 | case KeyEvent::MNone: 101 | switch (e.key()) 102 | { 103 | case KeyEvent::KEscape: 104 | escStatusBar(); 105 | break; 106 | case KeyEvent::KDelete: 107 | escStatusBar(); 108 | if (deleteSelected() == 0) 109 | { 110 | textBuffer_->del(cursor_); 111 | setCursor(cursor_); 112 | render(); 113 | } 114 | break; 115 | case KeyEvent::KBackspace: 116 | if (!statusBar_ || !statusBar_->textBuffer()) 117 | { 118 | if (deleteSelected() == 0) 119 | { 120 | textBuffer_->backspace(cursor_); 121 | setCursor(cursor_); 122 | render(); 123 | } 124 | } 125 | else 126 | { 127 | statusBar_->keyPressEvent(e); 128 | render(); 129 | } 130 | break; 131 | case KeyEvent::KReturn: 132 | { 133 | deleteSelected(); 134 | escStatusBar(); 135 | textBuffer_->insert(cursor_, L"\n"); 136 | setCursor(cursor_); 137 | render(); 138 | break; 139 | } 140 | case KeyEvent::KLeft: 141 | moveCursor(&Screen::moveCursorLeft); 142 | break; 143 | case KeyEvent::KRight: 144 | moveCursor(&Screen::moveCursorRight); 145 | break; 146 | case KeyEvent::KUp: 147 | moveCursor(&Screen::moveCursorUp); 148 | break; 149 | case KeyEvent::KDown: 150 | moveCursor(&Screen::moveCursorDown); 151 | break; 152 | case KeyEvent::KHome: 153 | moveCursor(&Screen::moveCursorHome); 154 | break; 155 | case KeyEvent::KEnd: 156 | moveCursor(&Screen::moveCursorEnd); 157 | break; 158 | case KeyEvent::KPageUp: 159 | moveCursor(&Screen::moveCursorPageUp); 160 | break; 161 | case KeyEvent::KPageDown: 162 | moveCursor(&Screen::moveCursorPageDown); 163 | break; 164 | default: 165 | result = false; 166 | break; 167 | }; 168 | break; 169 | case KeyEvent::MLCtrl: 170 | case KeyEvent::MRCtrl: 171 | switch (e.key()) 172 | { 173 | case KeyEvent::KHome: 174 | setCursor(0, 0); 175 | render(); 176 | break; 177 | case KeyEvent::KEnd: 178 | setCursor((*textBuffer_)[textBuffer_->size() - 1].size(), textBuffer_->size() - 1); 179 | render(); 180 | break; 181 | case KeyEvent::KV: 182 | paste(); 183 | render(); 184 | break; 185 | case KeyEvent::KInsert: 186 | case KeyEvent::KC: 187 | copy(); 188 | render(); 189 | break; 190 | case KeyEvent::KX: 191 | cut(); 192 | render(); 193 | break; 194 | case KeyEvent::KA: 195 | selectAll(); 196 | render(); 197 | case KeyEvent::KF: 198 | startIsearch(); 199 | break; 200 | case KeyEvent::KZ: 201 | { 202 | Coord c = cursor(); 203 | textBuffer_->undo(c); 204 | setCursor(c); 205 | render(); 206 | break; 207 | } 208 | case KeyEvent::KR: 209 | { 210 | Coord c = cursor(); 211 | textBuffer_->redo(c); 212 | setCursor(c); 213 | render(); 214 | break; 215 | } 216 | default: 217 | result = false; 218 | break; 219 | } 220 | break; 221 | case KeyEvent::MLShift: 222 | case KeyEvent::MRShift: 223 | switch (e.key()) 224 | { 225 | case KeyEvent::KLeft: 226 | select(&Screen::moveCursorLeft); 227 | break; 228 | case KeyEvent::KRight: 229 | select(&Screen::moveCursorRight); 230 | break; 231 | case KeyEvent::KUp: 232 | select(&Screen::moveCursorUp); 233 | break; 234 | case KeyEvent::KDown: 235 | select(&Screen::moveCursorDown); 236 | break; 237 | case KeyEvent::KHome: 238 | select(&Screen::moveCursorHome); 239 | break; 240 | case KeyEvent::KEnd: 241 | select(&Screen::moveCursorEnd); 242 | break; 243 | case KeyEvent::KPageUp: 244 | select(&Screen::moveCursorPageUp); 245 | break; 246 | case KeyEvent::KPageDown: 247 | select(&Screen::moveCursorPageDown); 248 | break; 249 | case KeyEvent::KInsert: 250 | paste(); 251 | render(); 252 | break; 253 | case KeyEvent::KDelete: 254 | cut(); 255 | render(); 256 | break; 257 | default: 258 | result = false; 259 | break; 260 | }; 261 | break; 262 | default: 263 | result = false; 264 | break; 265 | } 266 | 267 | return result; 268 | } 269 | 270 | bool Screen::textInputEvent(TextInputEvent &e) 271 | { 272 | if (textBuffer_) 273 | { 274 | if (!statusBar_ || !statusBar_->textBuffer()) 275 | { 276 | deleteSelected(); 277 | textBuffer_->insert(cursor_, e.text()); 278 | setCursor(cursor_); 279 | render(); 280 | } 281 | else 282 | { 283 | statusBar_->textInputEvent(e); 284 | render(); 285 | } 286 | return true; 287 | } 288 | return false; 289 | } 290 | 291 | void Screen::resizeEvent(ResizeEvent &) 292 | { 293 | ch_.resize(heightCh()); 294 | for (auto &r: ch_) 295 | { 296 | r.resize(widthCh()); 297 | } 298 | prevCh_.resize(heightCh()); 299 | for (auto &r: prevCh_) 300 | { 301 | r.resize(widthCh()); 302 | for (auto &i: r) 303 | i.ch = 0xffff; 304 | } 305 | render(); 306 | } 307 | 308 | int Screen::widthCh() const 309 | { 310 | return (width() + glyphWidth_ - 1) / glyphWidth_; 311 | } 312 | 313 | int Screen::heightCh() const 314 | { 315 | return (height() + glyphHeight_ - 1) / glyphHeight_; 316 | } 317 | 318 | Screen::Char &Screen::ch(int x, int y) 319 | { 320 | return ch_[y][x]; 321 | } 322 | 323 | const Screen::Char &Screen::ch(int x, int y) const 324 | { 325 | return ch_[y][x]; 326 | } 327 | 328 | Coord Screen::cursor() const 329 | { 330 | return cursor_; 331 | } 332 | 333 | void Screen::setCursor(Coord value) 334 | { 335 | if (value.y < vScroll_) 336 | { 337 | vScroll_ = value.y; 338 | value.y = vScroll_; 339 | } 340 | if (value.y > std::max(0, vScroll_ + heightCh() - 2)) 341 | { 342 | vScroll_ = value.y - heightCh() + 2; 343 | value.y = vScroll_ + heightCh() - 2; 344 | } 345 | cursor_ = value; 346 | } 347 | 348 | void Screen::setCursor(int x, int y) 349 | { 350 | setCursor(Coord{ x, y }); 351 | } 352 | 353 | BaseTextBuffer *Screen::textBuffer() const 354 | { 355 | return textBuffer_; 356 | } 357 | 358 | void Screen::setTextBuffer(BaseTextBuffer *value) 359 | { 360 | if (value != textBuffer_) 361 | { 362 | setStartSelection(Coord{-1, -1}); 363 | setEndSelection(Coord{-1, -1}); 364 | 365 | if (textBuffer_) 366 | textBuffer_->setCursor(cursor()); 367 | textBuffer_ = value; 368 | if (value) 369 | setCursor(value->cursor()); 370 | else 371 | setCursor(0, 0); 372 | if (textBuffer_) 373 | textBuffer_->render(this); 374 | else 375 | { 376 | for (size_t y = 0; y < ch_.size(); ++y) 377 | for (size_t x = 0; x < ch_[y].size(); ++x) 378 | ch_[y][x] = '\0'; 379 | update(); 380 | } 381 | } 382 | } 383 | 384 | int Screen::hScroll() const 385 | { 386 | return hScroll_; 387 | } 388 | 389 | void Screen::setHScroll(int value) 390 | { 391 | hScroll_ = value; 392 | render(); 393 | } 394 | 395 | int Screen::vScroll() const 396 | { 397 | return vScroll_; 398 | } 399 | 400 | void Screen::setVScroll(int value) 401 | { 402 | vScroll_ = value; 403 | render(); 404 | } 405 | 406 | Coord Screen::startSelection() const 407 | { 408 | return startSelection_; 409 | } 410 | 411 | void Screen::setStartSelection(Coord value) 412 | { 413 | startSelection_ = value; 414 | } 415 | 416 | Coord Screen::endSelection() const 417 | { 418 | return endSelection_; 419 | } 420 | 421 | void Screen::setEndSelection(Coord value) 422 | { 423 | endSelection_ = value; 424 | } 425 | 426 | bool Screen::isSelected(Coord value) const 427 | { 428 | Coord s, e; 429 | if (startSelection().y > endSelection().y || 430 | (startSelection().y == endSelection().y && startSelection().x > endSelection().x)) 431 | { 432 | s = endSelection(); 433 | e = startSelection(); 434 | } 435 | else 436 | { 437 | s = startSelection(); 438 | e = endSelection(); 439 | } 440 | 441 | if (s.x == -1 || 442 | s.y == -1 || 443 | e.x == -1 || 444 | e.y == -1) 445 | return false; 446 | return (value.y > s.y && value.y < e.y) || 447 | (value.y == s.y && value.y != e.y && value.x >= s.x) || 448 | (value.y == e.y && value.y != s.y && value.x < e.x) || 449 | (value.y == s.y && value.y != e.y && value.x >= s.x) || 450 | (value.y == e.y && value.y == s.y && value.x < e.x && value.x >= s.x); 451 | } 452 | 453 | StatusBar *Screen::statusBar() const 454 | { 455 | return statusBar_; 456 | } 457 | void Screen::setStatusBar(StatusBar *value) 458 | { 459 | statusBar_ = value; 460 | } 461 | 462 | 463 | int Screen::glyphHeight() const 464 | { 465 | return glyphHeight_; 466 | } 467 | 468 | int Screen::glyphWidth() const 469 | { 470 | return glyphWidth_; 471 | } 472 | 473 | 474 | void Screen::moveCursorLeft() 475 | { 476 | if (cursor_.x > 0) 477 | --cursor_.x; 478 | else 479 | { 480 | if (cursor_.y > 0) 481 | { 482 | --cursor_.y; 483 | cursor_.x = (*textBuffer_)[cursor_.y].size(); 484 | } 485 | } 486 | setCursor(cursor_); 487 | } 488 | 489 | void Screen::moveCursorRight() 490 | { 491 | if (cursor_.x < static_cast((*textBuffer_)[cursor_.y].size())) 492 | ++cursor_.x; 493 | else 494 | { 495 | if (cursor_.y < textBuffer_->size() - 1) 496 | { 497 | ++cursor_.y; 498 | cursor_.x = 0; 499 | } 500 | } 501 | setCursor(cursor_); 502 | } 503 | 504 | void Screen::moveCursorUp() 505 | { 506 | if (cursor_.y > 0) 507 | { 508 | --cursor_.y; 509 | if (cursor_.x > static_cast((*textBuffer_)[cursor_.y].size())) 510 | cursor_.x = (*textBuffer_)[cursor_.y].size(); 511 | } 512 | setCursor(cursor_); 513 | } 514 | 515 | void Screen::moveCursorDown() 516 | { 517 | if (cursor_.y < textBuffer_->size() - 1) 518 | { 519 | ++cursor_.y; 520 | if (cursor_.x > static_cast((*textBuffer_)[cursor_.y].size())) 521 | cursor_.x = (*textBuffer_)[cursor_.y].size(); 522 | } 523 | setCursor(cursor_); 524 | } 525 | 526 | void Screen::moveCursorHome() 527 | { 528 | cursor_.x = 0; 529 | setCursor(cursor_); 530 | } 531 | 532 | void Screen::moveCursorEnd() 533 | { 534 | cursor_.x = (*textBuffer_)[cursor_.y].size(); 535 | setCursor(cursor_); 536 | } 537 | 538 | void Screen::moveCursorPageUp() 539 | { 540 | vScroll_ -= heightCh() - 1; 541 | if (vScroll_ < 0) 542 | vScroll_ = 0; 543 | cursor_.y -= heightCh() - 1; 544 | if (cursor_.y < vScroll_) 545 | cursor_.y = vScroll_; 546 | else if (cursor_.y >= vScroll_ + heightCh()) 547 | cursor_.y = vScroll_ + heightCh() - 1; 548 | if (cursor_.y >= textBuffer_->size()) 549 | cursor_.y = textBuffer_->size() - 1; 550 | if (cursor_.y < 0) 551 | cursor_.y = 0; 552 | if (cursor_.x > static_cast((*textBuffer_)[cursor_.y].size())) 553 | cursor_.x = (*textBuffer_)[cursor_.y].size(); 554 | setCursor(cursor_); 555 | } 556 | 557 | void Screen::moveCursorPageDown() 558 | { 559 | vScroll_ += heightCh() - 1; 560 | if (vScroll_ >= textBuffer_->size() - 1) 561 | vScroll_ = textBuffer_->size() - 2; 562 | cursor_.y += heightCh() - 1; 563 | if (cursor_.y < vScroll_) 564 | cursor_.y = vScroll_; 565 | else if (cursor_.y >= vScroll_ + heightCh()) 566 | cursor_.y = vScroll_ + heightCh() - 1; 567 | if (cursor_.y >= textBuffer_->size()) 568 | cursor_.y = textBuffer_->size() - 1; 569 | if (cursor_.y < 0) 570 | cursor_.y = 0; 571 | if (cursor_.x > static_cast((*textBuffer_)[cursor_.y].size())) 572 | cursor_.x = (*textBuffer_)[cursor_.y].size(); 573 | setCursor(cursor_); 574 | } 575 | 576 | void Screen::select(void (Screen::*moveCursor)()) 577 | { 578 | if (startSelection().x == -1) 579 | setStartSelection(cursor()); 580 | (this->*moveCursor)(); 581 | setEndSelection(cursor()); 582 | render(); 583 | } 584 | 585 | void Screen::moveCursor(void (Screen::*moveCursorFunc)()) 586 | { 587 | escStatusBar(); 588 | (this->*moveCursorFunc)(); 589 | setStartSelection({-1, -1}); 590 | setEndSelection({-1, -1}); 591 | render(); 592 | } 593 | 594 | std::wstring Screen::getSelected() const 595 | { 596 | if (startSelection().y == endSelection().y) 597 | { 598 | auto s = std::min(startSelection().x, endSelection().x); 599 | auto e = std::max(startSelection().x, endSelection().x); 600 | const auto &line = (*textBuffer_)[startSelection().y]; 601 | std::wstring tmp{ begin(line) + s, begin(line) + e }; 602 | return tmp; 603 | } 604 | else 605 | { 606 | Coord s, e; 607 | if (startSelection().y > endSelection().y || 608 | (startSelection().y == endSelection().y && startSelection().x > endSelection().x)) 609 | { 610 | s = endSelection(); 611 | e = startSelection(); 612 | } 613 | else 614 | { 615 | s = startSelection(); 616 | e = endSelection(); 617 | } 618 | std::wstring tmp; 619 | const auto &firstLine = (*textBuffer_)[s.y] + L'\n'; 620 | tmp += { begin(firstLine) + s.x, end(firstLine) }; 621 | for (int y = s.y + 1; y <= e.y - 1; ++y) 622 | tmp += (*textBuffer_)[y] + L'\n'; 623 | const auto &lastLine = (*textBuffer_)[e.y]; 624 | tmp += { begin(lastLine), begin(lastLine) + e.x }; 625 | return tmp; 626 | } 627 | } 628 | 629 | 630 | int Screen::copy() 631 | { 632 | std::wstring tmp = getSelected(); 633 | if (!tmp.empty()) 634 | SDL_SetClipboardText(toUtf8(tmp).c_str()); 635 | return tmp.size(); 636 | } 637 | 638 | void Screen::paste() 639 | { 640 | setStartSelection(cursor()); 641 | textBuffer_->insert(cursor_, toUtf16(SDL_GetClipboardText())); 642 | setStartSelection({-1, -1}); 643 | setEndSelection({-1, -1}); 644 | } 645 | 646 | void Screen::cut() 647 | { 648 | if (startSelection().y > endSelection().y || 649 | (startSelection().y == endSelection().y && startSelection().x > endSelection().x)) 650 | textBuffer_->del(cursor_, copy()); 651 | else 652 | textBuffer_->backspace(cursor_, copy()); 653 | setStartSelection({-1, -1}); 654 | setEndSelection({-1, -1}); 655 | } 656 | 657 | void Screen::selectAll() 658 | { 659 | setStartSelection({0, 0}); 660 | setEndSelection({static_cast((*textBuffer_)[textBuffer_->size() - 1].size()), 661 | static_cast(textBuffer_->size() - 1)}); 662 | setCursor(endSelection()); 663 | } 664 | 665 | void Screen::startIsearch() 666 | { 667 | if (!statusBar_) 668 | { 669 | if (auto *buffer = dynamic_cast(textBuffer_)) 670 | buffer->findNext(); 671 | } 672 | else 673 | { 674 | if (!statusBar_->textBuffer()) 675 | { 676 | statusBar_->setTextBuffer(new IsearchBuffer(this)); 677 | statusBar_->moveCursorEnd(); 678 | } 679 | else 680 | { 681 | statusBar_->startIsearch(); 682 | render(); 683 | } 684 | } 685 | } 686 | 687 | void Screen::escStatusBar() 688 | { 689 | if (!statusBar_) 690 | return; 691 | if (statusBar_->textBuffer()) 692 | { 693 | delete statusBar_->textBuffer(); 694 | statusBar_->setTextBuffer(nullptr); 695 | } 696 | } 697 | 698 | void Screen::render() 699 | { 700 | if (textBuffer_) 701 | textBuffer_->render(this); 702 | } 703 | 704 | int Screen::deleteSelected() 705 | { 706 | if (startSelection() == Coord{-1, -1} || 707 | endSelection() == Coord{-1, -1}) 708 | return 0; 709 | int result = getSelected().size(); 710 | if (startSelection().y > endSelection().y || 711 | (startSelection().y == endSelection().y && startSelection().x > endSelection().x)) 712 | textBuffer_->del(cursor_, result); 713 | else 714 | textBuffer_->backspace(cursor_, result); 715 | 716 | setStartSelection(Coord{-1, -1}); 717 | setEndSelection(Coord{-1, -1}); 718 | render(); 719 | 720 | return result; 721 | } 722 | -------------------------------------------------------------------------------- /screen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "widget.hpp" 3 | #include "color.hpp" 4 | #include "coord.hpp" 5 | #include "layout.hpp" 6 | #include 7 | 8 | class BaseTextBuffer; 9 | class StatusBar; 10 | 11 | class Screen: public Widget 12 | { 13 | public: 14 | Screen(Widget * = nullptr); 15 | ~Screen(); 16 | struct Char 17 | { 18 | Char(wchar_t = L'\0', Color fg = Black, Color bg = White); 19 | bool operator!=(const Char &) const; 20 | wchar_t ch; 21 | Color fg; 22 | Color bg; 23 | }; 24 | int widthCh() const; 25 | int heightCh() const; 26 | Char &ch(int x, int y); 27 | const Char &ch(int x, int y) const; 28 | Coord cursor() const; 29 | void setCursor(Coord); 30 | void setCursor(int x, int y); 31 | BaseTextBuffer *textBuffer() const; 32 | void setTextBuffer(BaseTextBuffer *); 33 | int hScroll() const; 34 | void setHScroll(int); 35 | int vScroll() const; 36 | void setVScroll(int); 37 | Coord startSelection() const; 38 | void setStartSelection(Coord); 39 | Coord endSelection() const; 40 | void setEndSelection(Coord); 41 | bool isSelected(Coord) const; 42 | StatusBar *statusBar() const; 43 | void setStatusBar(StatusBar *); 44 | 45 | void moveCursorLeft(); 46 | void moveCursorRight(); 47 | void moveCursorUp(); 48 | void moveCursorDown(); 49 | void moveCursorHome(); 50 | void moveCursorEnd(); 51 | void moveCursorPageUp(); 52 | void moveCursorPageDown(); 53 | int copy(); 54 | void paste(); 55 | void cut(); 56 | void selectAll(); 57 | void startIsearch(); 58 | void escStatusBar(); 59 | protected: 60 | int glyphHeight() const; 61 | int glyphWidth() const; 62 | private: 63 | int glyphWidth_; 64 | int glyphHeight_; 65 | Coord cursor_; 66 | Coord startSelection_; 67 | Coord endSelection_; 68 | int hScroll_; 69 | int vScroll_; 70 | BaseTextBuffer *textBuffer_; 71 | StatusBar *statusBar_; 72 | int prevX_; 73 | int prevY_; 74 | std::vector > ch_; 75 | std::vector > prevCh_; 76 | void select(void (Screen::*moveCursor)()); 77 | void moveCursor(void (Screen::*moveCursor)()); 78 | std::wstring getSelected() const; 79 | void render(); 80 | int deleteSelected(); 81 | protected: 82 | virtual void resizeEvent(ResizeEvent &); 83 | virtual void paintEvent(PaintEvent &); 84 | virtual bool keyPressEvent(KeyEvent &); 85 | virtual bool textInputEvent(TextInputEvent &); 86 | }; 87 | -------------------------------------------------------------------------------- /status_bar.cpp: -------------------------------------------------------------------------------- 1 | #include "status_bar.hpp" 2 | 3 | StatusBar::StatusBar(Widget *parent): 4 | Screen(parent) 5 | {} 6 | 7 | int StatusBar::maxHeight() const 8 | { 9 | return glyphHeight(); 10 | } 11 | 12 | int StatusBar::minHeight() const 13 | { 14 | return glyphHeight(); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /status_bar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "screen.hpp" 3 | 4 | class StatusBar: public Screen 5 | { 6 | public: 7 | StatusBar(Widget *parent); 8 | virtual int maxHeight() const; 9 | virtual int minHeight() const; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /tabs.cpp: -------------------------------------------------------------------------------- 1 | #include "tabs.hpp" 2 | #include "painter.hpp" 3 | #include "base_text_buffer.hpp" 4 | #include "screen.hpp" 5 | #include "application.hpp" 6 | #include 7 | 8 | Tabs::Tabs(Widget *parent): 9 | Widget(parent), 10 | activeTextBuffer_(nullptr) 11 | {} 12 | 13 | void Tabs::addTextBuffer(BaseTextBuffer *textBuffer) 14 | { 15 | textBuffersList_.push_back(textBuffer); 16 | setActiveTextBuffer(textBuffer); 17 | } 18 | 19 | void Tabs::closeTextBuffer(BaseTextBuffer *textBuffer) 20 | { 21 | auto iter = find(begin(textBuffersList_), end(textBuffersList_), activeTextBuffer_); 22 | if (iter == end(textBuffersList_)) 23 | return; 24 | textBuffersList_.erase(iter); 25 | Application::instance()->queueDelete(textBuffer); 26 | } 27 | 28 | void Tabs::closeActiveTextBuffer() 29 | { 30 | auto iter = find(begin(textBuffersList_), end(textBuffersList_), activeTextBuffer_); 31 | if (iter == end(textBuffersList_)) 32 | return; 33 | deleteTextBuffer(activeTextBuffer_); 34 | Application::instance()->queueDelete(activeTextBuffer_); 35 | iter = textBuffersList_.erase(iter); 36 | 37 | if (iter != end(textBuffersList_)) 38 | setActiveTextBuffer(*iter); 39 | else if (!textBuffersList_.empty()) 40 | setActiveTextBuffer(textBuffersList_.back()); 41 | else 42 | setActiveTextBuffer(nullptr); 43 | } 44 | 45 | void Tabs::switchToNextTextBuffer() 46 | { 47 | auto iter = find(begin(textBuffersList_), end(textBuffersList_), activeTextBuffer_); 48 | if (iter == end(textBuffersList_)) 49 | return; 50 | ++iter; 51 | if (iter == end(textBuffersList_)) 52 | return; 53 | setActiveTextBuffer(*iter); 54 | } 55 | 56 | void Tabs::switchToPrevTextBuffer() 57 | { 58 | auto iter = find(begin(textBuffersList_), end(textBuffersList_), activeTextBuffer_); 59 | if (iter == end(textBuffersList_)) 60 | return; 61 | if (*iter == textBuffersList_.front()) 62 | return; 63 | --iter; 64 | setActiveTextBuffer(*iter); 65 | } 66 | 67 | void Tabs::moveTextBufferLeft() 68 | { 69 | auto iter = find(begin(textBuffersList_), end(textBuffersList_), activeTextBuffer_); 70 | if (iter == end(textBuffersList_)) 71 | return; 72 | if (*iter == textBuffersList_.front()) 73 | return; 74 | std::swap(*iter, *(iter - 1)); 75 | update(); 76 | } 77 | 78 | void Tabs::moveTextBufferRight() 79 | { 80 | auto iter = find(begin(textBuffersList_), end(textBuffersList_), activeTextBuffer_); 81 | if (iter == end(textBuffersList_) || iter + 1 == end(textBuffersList_)) 82 | return; 83 | std::swap(*iter, *(iter + 1)); 84 | update(); 85 | } 86 | 87 | std::vector &Tabs::textBuffersList() 88 | { 89 | return textBuffersList_; 90 | } 91 | 92 | const std::vector &Tabs::textBuffersList() const 93 | { 94 | return textBuffersList_; 95 | } 96 | 97 | void Tabs::setActiveTextBuffer(BaseTextBuffer *value) 98 | { 99 | activeTextBuffer_ = value; 100 | setTextBuffer(value); 101 | update(); 102 | } 103 | 104 | const BaseTextBuffer *Tabs::activeTextBuffer() const 105 | { 106 | return activeTextBuffer_; 107 | } 108 | 109 | BaseTextBuffer *Tabs::activeTextBuffer() 110 | { 111 | return activeTextBuffer_; 112 | } 113 | 114 | int Tabs::maxHeight() const 115 | { 116 | return 20; 117 | } 118 | 119 | int Tabs::minHeight() const 120 | { 121 | return 20; 122 | } 123 | 124 | void Tabs::paintEvent(PaintEvent &) 125 | { 126 | Painter p(this); 127 | p.setColor(Gray); 128 | p.drawRect(0, 0, width(), height()); 129 | int x = 0; 130 | for (auto buff: textBuffersList_) 131 | { 132 | auto tabName = (buff->isModified() && !buff->isReadOnly() ? L"*" : L"") + buff->name(); 133 | auto w = tabName.size() * p.glyphWidth(); 134 | auto bgColor = activeTextBuffer_ == buff ? Gray76 : Gray40; 135 | p.setColor(bgColor); 136 | p.drawRect(x, 0, w + p.glyphWidth() * 5 / 2, height()); 137 | int chP = x + p.glyphWidth() / 2; 138 | for (auto ch: tabName) 139 | { 140 | p.renderGlyph(ch, chP, (maxHeight() - p.glyphHeight()) / 2, Black, bgColor); 141 | chP += p.glyphWidth(); 142 | } 143 | chP += p.glyphWidth() / 2; 144 | p.renderGlyph(L'X', chP, (maxHeight() - p.glyphHeight()) / 2, Red, bgColor); 145 | 146 | x += w + p.glyphWidth() * 5 / 2; 147 | } 148 | } 149 | 150 | -------------------------------------------------------------------------------- /tabs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "widget.hpp" 3 | #include 4 | #include 5 | 6 | class BaseTextBuffer; 7 | class Screen; 8 | 9 | class Tabs: public Widget 10 | { 11 | public: 12 | Tabs(Widget *parent); 13 | void addTextBuffer(BaseTextBuffer *); 14 | void closeTextBuffer(BaseTextBuffer *); 15 | void closeActiveTextBuffer(); 16 | void switchToNextTextBuffer(); 17 | void switchToPrevTextBuffer(); 18 | void moveTextBufferLeft(); 19 | void moveTextBufferRight(); 20 | std::vector &textBuffersList(); 21 | const std::vector &textBuffersList() const; 22 | void setActiveTextBuffer(BaseTextBuffer *); 23 | const BaseTextBuffer *activeTextBuffer() const; 24 | BaseTextBuffer *activeTextBuffer(); 25 | virtual int maxHeight() const; 26 | virtual int minHeight() const; 27 | std::function setTextBuffer; 28 | std::function deleteTextBuffer; 29 | private: 30 | virtual void paintEvent(PaintEvent &); 31 | std::vector textBuffersList_; 32 | BaseTextBuffer *activeTextBuffer_; 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /text.txt: -------------------------------------------------------------------------------- 1 | ffff#pragma once 2 | #include 3 | #include 4 | 5 | typedef uint32_t Color; 6 | SDL_Color toSdlColor(Color); 7 | 8 | enum { 9 | IndianRed = 0xB0171F00, 10 | Returns the clipboard Crimson = 0xDC143C00, 11 | Crimson = 0xDC143C00, 12 | Crimson = 0xDC143C00, 13 | Lightpink = 0xFFB6C100, 14 | Lightpink1 = 0xFFAEB900, 15 | Lightpink2 = 0xEEA2AD00, 16 | Lightpink3 = 0xCD8C9500, 17 | Lightpink4 = 0x8B5F6500, 18 | Pink = 0xFFC0CB00, 19 | 20 | Return Value 21 | 22 | Returns the clipboard 23 | 24 | Return Value 25 | 26 | Returns the clipboardReturn Value 27 | 28 | Returns the clipboardReturn Value 29 | 30 | Returns the clipboardReturn Value 31 | 32 | Returns the clipboardReturn Value 33 | 34 | Returns the clipboardReturn Value 35 | 36 | Returns the clipboard 37 | 38 | 39 | Pink1 = 0xFFB5C500, 40 | Pink1 = 0xFFB5C500, 41 | Pink1 = 0xFFB5C500, 42 | Pink1 = 0xFFB5C500, 43 | Pink1 = 0xFFB5C500, 44 | Pink1 = 0xFFB5C500, 45 | Pink1 = 0xFFB5C500, 46 | Pink1 = 0xFFB5C500, 47 | Pink1 = 0xFFB5C500, 48 | Pink1 = 0xFFB5C500, 49 | Pink1 = 0xFFB5C500, 50 | Pink1 = 0xFFB5C500, 51 | Pink1 = 0xFFB5C500, 52 | Pink1 = 0xFFB5C500, 53 | Pink1 = 0xFFB5C500, 54 | Pink2 = 0xEEA9B800, 55 | Pink3 = 0xCD919E00, 56 | Pink4 = 0x8B636C00, 57 | Palevioletred = 0xDB709300, 58 | Palevioletred1 = 0xFF82AB00, 59 | Palevi which must be freed which must be freed which must be freed which must be freed which must be freed which must be freed which must be freed 60 | 61 | 62 | 63 | which must be freed which must be freed which must be freed which must be freed which must be freed 64 | 65 | 66 | 67 | which must be freed which must be freed which must be freed which must be freed which must be freed which must be freed 68 | 69 | 70 | oletred2 = 0xEE799F00, 71 | Palevioletred3 = 0xCD688900, 72 | Palevioletred4 = 0x8B475D00, 73 | Lavenderb aklsdfasdf akfj a;kfa 74 | a;skldfja ;skfjas;kf a 75 | a ;fa;slkfj a;kf 76 | asf ;asfjas ;klj f;asklf as 77 | lush = 0xFFF0F500, 78 | Lavenderblush2 = 0xEEE0E500, 79 | Lavenderblush3 = 0xCDC1C500, 80 | Lavenderblush4 = 0x8B838600, 81 | Violetred1 = 0xFF3E9600, 82 | Violetred2 = 0xEE3A8C00, 83 | Violetred3 = 0xCD327800, 84 | Violetred4 = 0x8B225200, 85 | Hotpink = 0xFF69B400, 86 | Hotpink = 0xFF69B400, 87 | Hotpink = 0xFF69B400, 88 | Hotpink = 0xFF69B400, 89 | Hotpink = 0xFF69B400, 90 | Hotpink = 0xFF69B400, 91 | Hotpink = 0xFF69B400, 92 | Returns the clipboard 93 | Hotpink1 = 0xFF6EB400, 94 | Return Value 95 | 96 | 97 | 98 | Hotpink2 = 0xEE6AA700, 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Hotpink3 = 0xCD609000, 108 | Hotpink4 109 | = 0x8B3A6200, 110 | Raspberry = 0x87265700, 111 | Deeppink = 0xFF149300, 112 | Deeppink2 = 0xEE128900, 113 | Deeppink3 = 0xCD107600, 114 | Deeppink4 = 0x8B0A5000, 115 | 116 | 117 | 118 | 119 | 120 | Maroon1 = 0xFF34B300, 121 | Maroon2 = 0xEE30A700, 122 | Maroon3 = 0xCD299000, 123 | Maroon4 = 0x8B1C6200, 124 | Maroon4 = 0x8B1C6200, 125 | Maroon4 = 0x8B1C6200, 126 | Maroon4 = 0x8B1C6200, 127 | Mediumvioletred = 0xC7158500, 128 | Violetred = 0xD0209000, 129 | Orchid = 0xDA70D600, 130 | Orchid1 = 0xFF83FA00, 131 | Orchid2 = 0xEE7AE900, 132 | Orchid3 = 0xCD69C900, 133 | Orchid4 = 0x8B478900, 134 | Thistle = 0xD8BFD800, 135 | Thistle1 = 0xFFE1FF00, 136 | Thistle2 = 0xEED2EE00, 137 | Thistle3 = 0xCDB5CD00, 138 | Thistle4 = 0x8B7B8B00, 139 | Plum1 = 0xFFBBFF00, 140 | Plum2 = 0xEEAEEE00, 141 | Plum3 = 0xCD96CD00, 142 | Plum4 = 0x8B668B00, 143 | Plum = 0xDDA0DD00, 144 | Violet = 0xEE82EE00, 145 | Magenta = 0xFF00FF00, 146 | Magenta2 = 0xEE00EE00, 147 | Magenta3 = 0xCD00CD00, 148 | Darkmagenta = 0x8B008B00, 149 | Purple = 0x80008000, 150 | Mediumorchid = 0xBA55D300, 151 | Mediumorchid1 = 0xE066FF00, 152 | Mediumorchid2 = 0xD15FEE00, 153 | Mediumorchid3 = 0xB452CD00, 154 | Mediumorchid4 = 0x7A378B00, 155 | Darkviolet = 0x9400D300, 156 | Darkorchid = 0x9932CC00, 157 | Darkorchid1 = 0xBF3EFF00, 158 | Darkorchid2 = 0xB23AEE00, 159 | Darkorchid3 = 0x9A32CD00, 160 | Darkorchid4 = 0x68228B00, 161 | Indigo = 0x4B008200, 162 | Blueviolet = 0x8A2BE200, 163 | Purple1 = 0x9B30FF00, 164 | Purple2 = 0x912CEE00, 165 | Purple3 = 0x7D26CD00, 166 | Purple4 = 0x551A8B00, 167 | Mediumpurple = 0x9370DB00, 168 | Mediumpurple1 = 0xAB82FF00, 169 | Mediumpurple2 = 0x9F79EE00, 170 | Mediumpurple3 = 0x8968CD00, 171 | Mediumpurple4 = 0x5D478B00, 172 | Darkslateblue = 0x483D8B00, 173 | Lightslateblue = 0x8470FF00, 174 | Mediumslateblue = 0x7B68EE00, 175 | Slateblue = 0x6A5ACD00, 176 | Slateblue1 = 0x836FFF00, 177 | Slateblue2 = 0x7A67EE00, 178 | Slateblue3 = 0x6959CD00, 179 | Slateblue4 = 0x473C8B00, 180 | Ghostwhite = 0xF8F8FF00, 181 | Lavender = 0xE6E6FA00, 182 | Blue = 0x0000FF00, 183 | Blue2 = 0x0000EE00, 184 | Mediumblue = 0x0000CD00, 185 | Darkblue = 0x00008B00, 186 | Navy = 0x00008000, 187 | Midnightblue = 0x19197000, 188 | Cobalt = 0x3D59AB00, 189 | Royalblue = 0x4169E100, 190 | Royalblue1 = 0x4876FF00, 191 | Royalblue2 = 0x436EEE00, 192 | Royalblue3 = 0x3A5FCD00, 193 | Royalblue4 = 0x27408B00, 194 | Cornflowerblue = 0x6495ED00, 195 | Lightsteelblue = 0xB0C4DE00, 196 | Lightsteelblue1 = 0xCAE1FF00, 197 | Lightsteelblue2 = 0xBCD2EE00, 198 | Lightsteelblue3 = 0xA2B5CD00, 199 | Lightsteelblue4 = 0x6E7B8B00, 200 | Lightslategray = 0x77889900, 201 | Slategray = 0x70809000, 202 | Slategray1 = 0xC6E2FF00, 203 | Slategray2 = 0xB9D3EE00, 204 | Slategray3 = 0x9FB6CD00, 205 | Slategray4 = 0x6C7B8B00, 206 | Dodgerblue = 0x1E90FF00, 207 | Dodgerblue2 = 0x1C86EE00, 208 | Dodgerblue3 = 0x1874CD00, 209 | Dodgerblue4 = 0x104E8B00, 210 | Aliceblue = 0xF0F8FF00, 211 | Steelblue = 0x4682B400, 212 | Steelblue1 = 0x63B8FF00, 213 | Steelblue2 = 0x5CACEE00, 214 | Steelblue3 = 0x4F94CD00, 215 | Steelblue4 = 0x36648B00, 216 | Lightskyblue = 0x87CEFA00, 217 | Lightskyblue1 = 0xB0E2FF00, 218 | Lightskyblue2 = 0xA4D3EE00, 219 | Lightskyblue3 = 0x8DB6CD00, 220 | Lightskyblue4 = 0x607B8B00, 221 | Skyblue1 = 0x87CEFF00, 222 | Skyblue2 = 0x7EC0EE00, 223 | Skyblue3 = 0x6CA6CD00, 224 | Skyblue4 = 0x4A708B00, 225 | Skyblue = 0x87CEEB00, 226 | Deepskyblue = 0x00BFFF00, 227 | Deepskyblue2 = 0x00B2EE00, 228 | Deepskyblue3 = 0x009ACD00, 229 | Deepskyblue4 = 0x00688B00, 230 | Peacock = 0x33A1C900, 231 | Lightblue = 0xADD8E600, 232 | Lightblue1 = 0xBFEFFF00, 233 | Lightblue2 = 0xB2DFEE00, 234 | Lightblue3 = 0x9AC0CD00, 235 | Lightblue4 = 0x68838B00, 236 | Powderblue = 0xB0E0E600, 237 | Cadetblue1 = 0x98F5FF00, 238 | Cadetblue2 = 0x8EE5EE00, 239 | Cadetblue3 = 0x7AC5CD00, 240 | Cadetblue4 = 0x53868B00, 241 | Turquoise1 = 0x00F5FF00, 242 | Turquoise2 = 0x00E5EE00, 243 | Turquoise3 = 0x00C5CD00, 244 | Turquoise4 = 0x00868B00, 245 | Cadetblue = 0x5F9EA000, 246 | Darkturquoise = 0x00CED100, 247 | Azure = 0xF0FFFF00, 248 | Azure2 = 0xE0EEEE00, 249 | Azure3 = 0xC1CDCD00, 250 | Azure4 = 0x838B8B00, 251 | Lightcyan = 0xE0FFFF00, 252 | Lightcyan2 = 0xD1EEEE00, 253 | Lightcyan3 = 0xB4CDCD00, 254 | Lightcyan4 = 0x7A8B8B00, 255 | Paleturquoise1 = 0xBBFFFF00, 256 | Paleturquoise2 = 0xAEEEEE00, 257 | Paleturquoise3 = 0x96CDCD00, 258 | Paleturquoise4 = 0x668B8B00, 259 | Darkslategray = 0x2F4F4F00, 260 | Darkslategray1 = 0x97FFFF00, 261 | Darkslategray2 = 0x8DEEEE00, 262 | Darkslategray3 = 0x79CDCD00, 263 | Darkslategray4 = 0x528B8B00, 264 | Cyan = 0x00FFFF00, 265 | Cyan2 = 0x00EEEE00, 266 | Cyan3 = 0x00CDCD00, 267 | Darkcyan = 0x008B8B00, 268 | Teal = 0x00808000, 269 | Mediumturquoise = 0x48D1CC00, 270 | Lightseagreen = 0x20B2AA00, 271 | Manganeseblue = 0x03A89E00, 272 | Turquoise = 0x40E0D000, 273 | Coldgrey = 0x808A8700, 274 | Turquoiseblue = 0x00C78C00, 275 | Aquamarine = 0x7FFFD400, 276 | Aquamarine2 = 0x76EEC600, 277 | Mediumaquamarine = 0x66CDAA00, 278 | Aquamarine4 = 0x458B7400, 279 | Mediumspringgreen = 0x00FA9A00, 280 | Mintcream = 0xF5FFFA00, 281 | Springgreen = 0x00FF7F00, 282 | Springgreen1 = 0x00EE7600, 283 | Springgreen2 = 0x00CD6600, 284 | Springgreen3 = 0x008B4500, 285 | Mediumseagreen = 0x3CB37100, 286 | Seagreen1 = 0x54FF9F00, 287 | Seagreen2 = 0x4EEE9400, 288 | Seagreen3 = 0x43CD8000, 289 | Seagreen = 0x2E8B5700, 290 | Emeraldgreen = 0x00C95700, 291 | Mint = 0xBDFCC900, 292 | Cobaltgreen = 0x3D914000, 293 | Honeydew = 0xF0FFF000, 294 | Honeydew2 = 0xE0EEE000, 295 | Honeydew3 = 0xC1CDC100, 296 | Honeydew4 = 0x838B8300, 297 | Darkseagreen = 0x8FBC8F00, 298 | Darkseagreen1 = 0xC1FFC100, 299 | Darkseagreen2 = 0xB4EEB400, 300 | Darkseagreen3 = 0x9BCD9B00, 301 | Darkseagreen4 = 0x698B6900, 302 | Palegreen = 0x98FB9800, 303 | Palegreen1 = 0x9AFF9A00, 304 | Lightgreen = 0x90EE9000, 305 | Palegreen3 = 0x7CCD7C00, 306 | Palegreen4 = 0x548B5400, 307 | Limegreen = 0x32CD3200, 308 | Forestgreen = 0x228B2200, 309 | Lime = 0x00FF0000, 310 | Green2 = 0x00EE0000, 311 | Green3 = 0x00CD0000, 312 | Green4 = 0x008B0000, 313 | Green = 0x00800000, 314 | Darkgreen = 0x00640000, 315 | Sapgreen = 0x30801400, 316 | Lawngreen = 0x7CFC0000, 317 | Chartreuse = 0x7FFF0000, 318 | Chartreuse2 = 0x76EE0000, 319 | Chartreuse3 = 0x66CD0000, 320 | Chartreuse4 = 0x458B0000, 321 | Greenyellow = 0xADFF2F00, 322 | Darkolivegreen1 = 0xCAFF7000, 323 | Darkolivegreen2 = 0xBCEE6800, 324 | Darkolivegreen3 = 0xA2CD5A00, 325 | Darkolivegreen4 = 0x6E8B3D00, 326 | Darkolivegreen = 0x556B2F00, 327 | Olivedrab = 0x6B8E2300, 328 | Olivedrab1 = 0xC0FF3E00, 329 | Olivedrab2 = 0xB3EE3A00, 330 | Yellowgreen = 0x9ACD3200, 331 | Olivedrab4 = 0x698B2200, 332 | Ivory = 0xFFFFF000, 333 | Ivory2 = 0xEEEEE000, 334 | Ivory3 = 0xCDCDC100, 335 | Ivory4 = 0x8B8B8300, 336 | Beige = 0xF5F5DC00, 337 | Lightyellow = 0xFFFFE000, 338 | Lightyellow2 = 0xEEEED100, 339 | Lightyellow3 = 0xCDCDB400, 340 | Lightyellow4 = 0x8B8B7A00, 341 | Lightgoldenrodyellow = 0xFAFAD200, 342 | Yellow = 0xFFFF0000, 343 | Yellow2 = 0xEEEE0000, 344 | Yellow3 = 0xCDCD0000, 345 | Yellow4 = 0x8B8B0000, 346 | Warmgrey = 0x80806900, 347 | Olive = 0x80800000, 348 | Darkkhaki = 0xBDB76B00, 349 | Khaki1 = 0xFFF68F00, 350 | Khaki2 = 0xEEE68500, 351 | Khaki3 = 0xCDC67300, 352 | Khaki4 = 0x8B864E00, 353 | Khaki = 0xF0E68C00, 354 | Palegoldenrod = 0xEEE8AA00, 355 | Lemonchiffon = 0xFFFACD00, 356 | Lemonchiffon2 = 0xEEE9BF00, 357 | Lemonchiffon3 = 0xCDC9A500, 358 | Lemonchiffon4 = 0x8B897000, 359 | Lightgoldenrod1 = 0xFFEC8B00, 360 | Lightgoldenrod2 = 0xEEDC8200, 361 | Lightgoldenrod3 = 0xCDBE7000, 362 | Lightgoldenrod4 = 0x8B814C00, 363 | Banana = 0xE3CF5700, 364 | Gold = 0xFFD70000, 365 | Gold2 = 0xEEC90000, 366 | Gold3 = 0xCDAD0000, 367 | Gold4 = 0x8B750000, 368 | Cornsilk = 0xFFF8DC00, 369 | Cornsilk2 = 0xEEE8CD00, 370 | Cornsilk3 = 0xCDC8B100, 371 | Cornsilk4 = 0x8B887800, 372 | Goldenrod = 0xDAA52000, 373 | Goldenrod1 = 0xFFC12500, 374 | Goldenrod2 = 0xEEB42200, 375 | Goldenrod3 = 0xCD9B1D00, 376 | Goldenrod4 = 0x8B691400, 377 | Darkgoldenrod = 0xB8860B00, 378 | Darkgoldenrod1 = 0xFFB90F00, 379 | Darkgoldenrod2 = 0xEEAD0E00, 380 | Darkgoldenrod3 = 0xCD950C00, 381 | Darkgoldenrod4 = 0x8B650800, 382 | Orange = 0xFFA50000, 383 | Orange2 = 0xEE9A0000, 384 | Orange3 = 0xCD850000, 385 | Orange4 = 0x8B5A0000, 386 | Floralwhite = 0xFFFAF000, 387 | Oldlace = 0xFDF5E600, 388 | Wheat = 0xF5DEB300, 389 | Wheat1 = 0xFFE7BA00, 390 | Wheat2 = 0xEED8AE00, 391 | Wheat3 = 0xCDBA9600, 392 | Wheat4 = 0x8B7E6600, 393 | Moccasin = 0xFFE4B500, 394 | Papayawhip = 0xFFEFD500, 395 | Blanchedalmond = 0xFFEBCD00, 396 | Navajowhite = 0xFFDEAD00, 397 | Navajowhite2 = 0xEECFA100, 398 | Navajowhite3 = 0xCDB38B00, 399 | Navajowhite4 = 0x8B795E00, 400 | Eggshell = 0xFCE6C900, 401 | Tan = 0xD2B48C00, 402 | Brick = 0x9C661F00, 403 | Cadmiumyellow = 0xFF991200, 404 | Antiquewhite = 0xFAEBD700, 405 | Antiquewhite1 = 0xFFEFDB00, 406 | Antiquewhite2 = 0xEEDFCC00, 407 | Antiquewhite3 = 0xCDC0B000, 408 | Antiquewhite4 = 0x8B837800, 409 | Burlywood = 0xDEB88700, 410 | Burlywood1 = 0xFFD39B00, 411 | Burlywood2 = 0xEEC59100, 412 | Burlywood3 = 0xCDAA7D00, 413 | Burlywood4 = 0x8B735500, 414 | Bisque = 0xFFE4C400, 415 | Bisque2 = 0xEED5B700, 416 | Bisque3 = 0xCDB79E00, 417 | Bisque4 = 0x8B7D6B00, 418 | Melon = 0xE3A86900, 419 | Carrot = 0xED912100, 420 | Darkorange = 0xFF8C0000, 421 | Darkorange1 = 0xFF7F0000, 422 | Darkorange2 = 0xEE760000, 423 | Darkorange3 = 0xCD660000, 424 | Darkorange4 = 0x8B450000, 425 | Tan1 = 0xFFA54F00, 426 | Tan2 = 0xEE9A4900, 427 | Peru = 0xCD853F00, 428 | Tan4 = 0x8B5A2B00, 429 | Linen = 0xFAF0E600, 430 | Peachpuff = 0xFFDAB900, 431 | Peachpuff2 = 0xEECBAD00, 432 | Peachpuff3 = 0xCDAF9500, 433 | Peachpuff4 = 0x8B776500, 434 | Seashell = 0xFFF5EE00, 435 | Seashell2 = 0xEEE5DE00, 436 | Seashell3 = 0xCDC5BF00, 437 | Seashell4 = 0x8B868200, 438 | Sandybrown = 0xF4A46000, 439 | Rawsienna = 0xC7611400, 440 | Chocolate = 0xD2691E00, 441 | Chocolate1 = 0xFF7F2400, 442 | Chocolate2 = 0xEE762100, 443 | Chocolate3 = 0xCD661D00, 444 | Ssaddlebrown = 0x8B451300, 445 | Ivoryblack = 0x29242100, 446 | Flesh = 0xFF7D4000, 447 | Cadmiumorange = 0xFF610300, 448 | Burntsienna = 0x8A360F00, 449 | Sienna = 0xA0522D00, 450 | Sienna1 = 0xFF824700, 451 | Sienna2 = 0xEE794200, 452 | Sienna3 = 0xCD683900, 453 | Sienna4 = 0x8B472600, 454 | Lightsalmon = 0xFFA07A00, 455 | Lightsalmon2 = 0xEE957200, 456 | Lightsalmon3 = 0xCD816200, 457 | Lightsalmon4 = 0x8B574200, 458 | Coral = 0xFF7F5000, 459 | Orangered = 0xFF450000, 460 | Orangered2 = 0xEE400000, 461 | Orangered3 = 0xCD370000, 462 | Orangered4 = 0x8B250000, 463 | Sepia = 0x5E261200, 464 | Darksalmon = 0xE9967A00, 465 | Salmon1 = 0xFF8C6900, 466 | Salmon2 = 0xEE826200, 467 | Salmon3 = 0xCD705400, 468 | Salmon4 = 0x8B4C3900, 469 | Coral1 = 0xFF725600, 470 | Coral2 = 0xEE6A5000, 471 | Coral3 = 0xCD5B4500, 472 | Coral4 = 0x8B3E2F00, 473 | Burntumber = 0x8A332400, 474 | Tomato = 0xFF634700, 475 | Tomato2 = 0xEE5C4200, 476 | Tomato3 = 0xCD4F3900, 477 | Tomato4 = 0x8B362600, 478 | Salmon = 0xFA807200, 479 | Mistyrose = 0xFFE4E100, 480 | Mistyrose2 = 0xEED5D200, 481 | Mistyrose3 = 0xCDB7B500, 482 | Mistyrose4 = 0x8B7D7B00, 483 | Snow = 0xFFFAFA00, 484 | Snow2 = 0xEEE9E900, 485 | Snow3 = 0xCDC9C900, 486 | Snow4 = 0x8B898900, 487 | Rosybrown = 0xBC8F8F00, 488 | Rosybrown1 = 0xFFC1C100, 489 | Rosybrown2 = 0xEEB4B400, 490 | Rosybrown3 = 0xCD9B9B00, 491 | Rosybrown4 = 0x8B696900, 492 | Lightcoral = 0xF0808000, 493 | Indianred = 0xCD5C5C00, 494 | Indianred1 = 0xFF6A6A00, 495 | Indianred2 = 0xEE636300, 496 | Indianred4 = 0x8B3A3A00, 497 | Indianred3 = 0xCD555500, 498 | Brown = 0xA52A2A00, 499 | Brown1 = 0xFF404000, 500 | Brown2 = 0xEE3B3B00, 501 | Brown3 = 0xCD333300, 502 | Brown4 = 0x8B232300, 503 | Firebrick = 0xB2222200, 504 | Firebrick1 = 0xFF303000, 505 | Firebrick2 = 0xEE2C2C00, 506 | Firebrick3 = 0xCD262600, 507 | Firebrick4 = 0x8B1A1A00, 508 | Red = 0xFF000000, 509 | Red2 = 0xEE000000, 510 | Red3 = 0xCD000000, 511 | Rarkred = 0x8B000000, 512 | Maroon = 0x80000000, 513 | SgiBeet = 0x8E388E00, 514 | SgiSlateblue = 0x7171C600, 515 | SgiLightblue = 0x7D9EC000, 516 | SgiTeal = 0x388E8E00, 517 | SgiChartreuse = 0x71C67100, 518 | SgiOlivedrab = 0x8E8E3800, 519 | SgiBrightgray = 0xC5C1AA00, 520 | SgiSalmon = 0xC6717100, 521 | SgiDarkgray = 0x55555500, 522 | SgiGray12 = 0x1E1E1E00, 523 | SgiGray16 = 0x28282800, 524 | SgiGray32 = 0x51515100, 525 | SgiGray36 = 0x5B5B5B00, 526 | SgiGray52 = 0x84848400, 527 | SgiGray56 = 0x8E8E8E00, 528 | SgiLightgray = 0xAAAAAA00, 529 | SgiGray72 = 0xB7B7B700, 530 | SgiGray76 = 0xC1C1C100, 531 | SgiGray92 = 0xEAEAEA00, 532 | SgiGray96 = 0xF4F4F400, 533 | White = 0xFFFFFF00, 534 | WhiteSmoke = 0xF5F5F500, 535 | Gainsboro = 0xDCDCDC00, 536 | Lightgrey = 0xD3D3D300, 537 | Silver = 0xC0C0C000, 538 | Darkgray = 0xA9A9A900, 539 | Gray = 0x80808000, 540 | Dimgray = 0x69696900, 541 | Black = 0x00000000, 542 | Gray99 = 0xFCFCFC00, 543 | Gray98 = 0xFAFAFA00, 544 | Gray97 = 0xF7F7F700, 545 | Gray95 = 0xF2F2F200, 546 | Gray94 = 0xF0F0F000, 547 | Gray93 = 0xEDEDED00, 548 | Gray92 = 0xEBEBEB00, 549 | Gray91 = 0xE8E8E800, 550 | Gray90 = 0xE5E5E500, 551 | Gray89 = 0xE3E3E300, 552 | Gray88 = 0xE0E0E000, 553 | Gray87 = 0xDEDEDE00, 554 | Gray86 = 0xDBDBDB00, 555 | Gray85 = 0xD9D9D900, 556 | Gray84 = 0xD6D6D600, 557 | Gray83 = 0xD4D4D400, 558 | Gray82 = 0xD1D1D100, 559 | Gray81 = 0xCFCFCF00, 560 | Gray80 = 0xCCCCCC00, 561 | Gray79 = 0xC9C9C900, 562 | Gray78 = 0xC7C7C700, 563 | Gray77 = 0xC4C4C400, 564 | Gray76 = 0xC2C2C200, 565 | Gray75 = 0xBFBFBF00, 566 | Gray74 = 0xBDBDBD00, 567 | Gray73 = 0xBABABA00, 568 | Gray72 = 0xB8B8B800, 569 | Gray71 = 0xB5B5B500, 570 | Gray70 = 0xB3B3B300, 571 | Gray69 = 0xB0B0B000, 572 | Gray68 = 0xADADAD00, 573 | Gray67 = 0xABABAB00, 574 | Gray66 = 0xA8A8A800, 575 | Gray65 = 0xA6A6A600, 576 | Gray64 = 0xA3A3A300, 577 | Gray63 = 0xA1A1A100, 578 | Gray62 = 0x9E9E9E00, 579 | Gray61 = 0x9C9C9C00, 580 | Gray60 = 0x99999900, 581 | Gray59 = 0x96969600, 582 | Gray58 = 0x94949400, 583 | Gray57 = 0x91919100, 584 | Gray56 = 0x8F8F8F00, 585 | Gray55 = 0x8C8C8C00, 586 | Gray54 = 0x8A8A8A00, 587 | Gray53 = 0x87878700, 588 | Gray52 = 0x85858500, 589 | Gray51 = 0x82828200, 590 | Gray50 = 0x7F7F7F00, 591 | Gray49 = 0x7D7D7D00, 592 | Gray48 = 0x7A7A7A00, 593 | Gray47 = 0x78787800, 594 | Gray46 = 0x75757500, 595 | Gray45 = 0x73737300, 596 | Gray44 = 0x70707000, 597 | Gray43 = 0x6E6E6E00, 598 | Gray42 = 0x6B6B6B00, 599 | Gray40 = 0x66666600, 600 | Gray39 = 0x63636300, 601 | Gray38 = 0x61616100, 602 | Gray37 = 0x5E5E5E00, 603 | Gray36 = 0x5C5C5C00, 604 | Gray35 = 0x59595900, 605 | Gray34 = 0x57575700, 606 | Gray33 = 0x54545400, 607 | Gray32 = 0x52525200, 608 | Gray31 = 0x4F4F4F00, 609 | Gray30 = 0x4D4D4D00, 610 | Gray29 = 0x4A4A4A00, 611 | Gray28 = 0x47474700, 612 | Gray27 = 0x45454500, 613 | Gray26 = 0x42424200, 614 | Gray25 = 0x40404000, 615 | Gray24 = 0x3D3D3D00, 616 | Gray23 = 0x3B3B3B00, 617 | Gray22 = 0x38383800, 618 | Gray21 = 0x36363600, 619 | Gray20 = 0x33333300, 620 | Gray19 = 0x30303000, 621 | Gray18 = 0x2E2E2E00, 622 | Gray17 = 0x2B2B2B00, 623 | Gray16 = 0x29292900, 624 | Gray15 = 0x26262600, 625 | Gray14 = 0x24242400, 626 | Gray13 = 0x21212100, 627 | Gray12 = 0x1F1F1F00, 628 | Gray11 = 0x1C1C1C00, 629 | Gray10 = 0x1A1A1A00, 630 | Gray9 = 0x17171700, 631 | Gray8 = 0x14141400, 632 | Gray7 = 0x12121200, 633 | Gray6 = 0x0F0F0F00, 634 | 635 | 636 | 637 | 638 | 639 | 640 | Gray5 = 0x0D0D0D00, 641 | Gray4 = 0x0A0A0A00, 642 | Gray3 = 0x08080800, 643 | Gray2 = 0x05050500, 644 | Gray1 = 0x03030300 645 | }; 646 | 647 | 648 | 649 | Returns the clipboard 650 | 651 | Return Value 652 | Return Value 653 | 654 | 655 | 656 | -------------------------------------------------------------------------------- /text_file.cpp: -------------------------------------------------------------------------------- 1 | #include "text_file.hpp" 2 | #include "to_utf16.hpp" 3 | #include "to_utf8.hpp" 4 | #include "cpp_highlighter.hpp" 5 | #include "full_file_name.hpp" 6 | #include 7 | #include 8 | 9 | static std::string baseName(const std::string &fileName) 10 | { 11 | auto p = fileName.rfind('/'); 12 | if (p != std::string::npos) 13 | return std::string{ begin(fileName) + p + 1, end(fileName) }; 14 | else 15 | return fileName; 16 | } 17 | 18 | static bool isCpp(const std::string &fileName) 19 | { 20 | auto p = fileName.rfind("."); 21 | std::string ext; 22 | if (p != std::string::npos) 23 | ext = fileName.substr(p); 24 | static std::string exts[] = { 25 | ".c", 26 | ".cpp", 27 | ".C", 28 | ".cc", 29 | ".c++", 30 | ".h", 31 | ".H", 32 | ".hpp", 33 | ".h++" 34 | }; 35 | return std::find(std::begin(exts), std::end(exts), ext) != std::end(exts); 36 | } 37 | 38 | TextFile::TextFile(const std::string &fileName): 39 | fileName_(getFullFileName(fileName)) 40 | { 41 | if (!fileName.empty()) 42 | setName(toUtf16(baseName(fileName))); 43 | else 44 | setName(L"Untitled"); 45 | 46 | std::ifstream f(fileName_); 47 | if (f.is_open()) 48 | while (!f.eof()) 49 | { 50 | std::string line; 51 | std::getline(f, line); 52 | buffer_.push_back(toUtf16(line)); 53 | } 54 | else 55 | buffer_.push_back(L""); 56 | if (isCpp(fileName)) 57 | highlighter_ = new CppHighlighter(this); 58 | } 59 | 60 | std::string TextFile::fileName() const 61 | { 62 | return fileName_; 63 | } 64 | 65 | void TextFile::save() 66 | { 67 | if (!fileName_.empty()) 68 | saveAs(fileName_); 69 | } 70 | 71 | void TextFile::saveAs(const std::string &fileName) 72 | { 73 | fileName_ = fileName; 74 | setName(toUtf16(baseName(fileName))); 75 | std::ofstream f(fileName_); 76 | bool first = true; 77 | for (const auto &l: buffer_) 78 | { 79 | if (first) 80 | first = false; 81 | else 82 | f << std::endl; 83 | f << toUtf8(l); 84 | } 85 | clearModified(); 86 | } 87 | 88 | 89 | std::wstring TextFile::preInsert(Coord &cursor, const std::wstring &value) 90 | { 91 | if (value.size() != 1 || value[0] != '\n') 92 | return value; 93 | else 94 | { 95 | auto &line = (*this)[cursor.y]; 96 | std::wstring spaces; 97 | int c = 0; 98 | for (auto ch: line) 99 | if (ch == L' ') 100 | ++c; 101 | else 102 | break; 103 | for (size_t x = cursor.x; x < line.size() && line[x] == ' '; ++x, --c); 104 | for (int i = 0; i < c; ++i) 105 | spaces += L' '; 106 | return L'\n' + spaces; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /text_file.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "base_text_buffer.hpp" 3 | 4 | class TextFile: public BaseTextBuffer 5 | { 6 | public: 7 | TextFile(const std::string &fileName = ""); 8 | std::string fileName() const; 9 | void save(); 10 | void saveAs(const std::string &fileName); 11 | private: 12 | std::string fileName_; 13 | std::wstring preInsert(Coord &cursor, const std::wstring &); 14 | }; 15 | -------------------------------------------------------------------------------- /text_input_event.cpp: -------------------------------------------------------------------------------- 1 | #include "text_input_event.hpp" 2 | 3 | TextInputEvent::TextInputEvent(const std::wstring &text): 4 | text_{text} 5 | {} 6 | 7 | std::wstring TextInputEvent::text() const 8 | { 9 | return text_; 10 | } 11 | -------------------------------------------------------------------------------- /text_input_event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class TextInputEvent 5 | { 6 | public: 7 | TextInputEvent(const std::wstring &); 8 | std::wstring text() const; 9 | private: 10 | std::wstring text_; 11 | }; 12 | -------------------------------------------------------------------------------- /to_utf16.cpp: -------------------------------------------------------------------------------- 1 | #include "to_utf16.hpp" 2 | #include 3 | 4 | std::wstring toUtf16(const char *value) 5 | { 6 | return toUtf16(std::string{value}); 7 | } 8 | 9 | std::wstring toUtf16(const std::string &value) 10 | { 11 | std::wstring result; 12 | wchar_t tmp = 0; 13 | for (auto ch: value) 14 | { 15 | if ((ch & 0x80) == 0) 16 | { 17 | if (tmp != 0) 18 | result += tmp; 19 | tmp = 0; 20 | result += ch; 21 | } 22 | else if ((ch & 0xe0) == 0xc0) 23 | { 24 | if (tmp != 0) 25 | result += tmp; 26 | tmp = ch & 0x1f; 27 | } 28 | else if ((ch & 0xf0) == 0xe0) 29 | tmp = ch & 0xf; 30 | else if ((ch & 0xc0) == 0x80) 31 | tmp = (tmp << 6) | (ch & 0x3f); 32 | } 33 | if (tmp != 0) 34 | result += tmp; 35 | return result; 36 | } 37 | -------------------------------------------------------------------------------- /to_utf16.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | std::wstring toUtf16(const char *); 5 | std::wstring toUtf16(const std::string &); 6 | -------------------------------------------------------------------------------- /to_utf8.cpp: -------------------------------------------------------------------------------- 1 | #include "to_utf8.hpp" 2 | #include 3 | #include 4 | 5 | std::string toUtf8(const std::wstring &value) 6 | { 7 | std::string result; 8 | for (auto ch: value) 9 | { 10 | if (ch < 0x80) 11 | result += static_cast(ch); 12 | else if (ch < 0x800) 13 | { 14 | result += static_cast(0xc0 | (ch >> 6)); 15 | result += static_cast(0x80 | (ch & 0x003f)); 16 | } 17 | else 18 | { 19 | result += static_cast(0xe0 | (ch >> 12)); 20 | result += static_cast(0x80 | ((ch >> 6) & 0x003f)); 21 | result += static_cast(0x80 | (ch & 0x003f)); 22 | } 23 | 24 | } 25 | return result; 26 | } 27 | 28 | std::string toUtf8(const wchar_t *value) 29 | { 30 | return toUtf8(std::wstring(value)); 31 | } 32 | -------------------------------------------------------------------------------- /to_utf8.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | std::string toUtf8(const std::wstring &); 5 | std::string toUtf8(const wchar_t *); 6 | -------------------------------------------------------------------------------- /undo_stack.cpp: -------------------------------------------------------------------------------- 1 | #include "undo_stack.hpp" 2 | 3 | BaseUndoCommand::~BaseUndoCommand() 4 | {} 5 | 6 | UndoStack::UndoStack(): 7 | originalState_(0) 8 | {} 9 | 10 | UndoStack::~UndoStack() 11 | { 12 | for (auto c: undoStack_) 13 | delete c; 14 | for (auto c: redoStack_) 15 | delete c; 16 | } 17 | 18 | 19 | void UndoStack::undo(Coord &cursor) 20 | { 21 | if (undoStack_.empty()) 22 | return; 23 | auto command = undoStack_.back(); 24 | undoStack_.pop_back(); 25 | redoStack_.push_back(command); 26 | command->undo(cursor); 27 | } 28 | 29 | void UndoStack::redo(Coord &cursor) 30 | { 31 | if (redoStack_.empty()) 32 | return; 33 | auto command = redoStack_.back(); 34 | redoStack_.pop_back(); 35 | undoStack_.push_back(command); 36 | command->redo(cursor); 37 | } 38 | 39 | bool UndoStack::canUndo() const 40 | { 41 | return !undoStack_.empty(); 42 | } 43 | 44 | bool UndoStack::canRedo() const 45 | { 46 | return !redoStack_.empty(); 47 | } 48 | 49 | void UndoStack::clean() 50 | { 51 | for (auto c: undoStack_) 52 | delete c; 53 | undoStack_.clear(); 54 | for (auto c: redoStack_) 55 | delete c; 56 | redoStack_.clear(); 57 | } 58 | 59 | bool UndoStack::isModified() const 60 | { 61 | return originalState_ != undoStack_.size(); 62 | } 63 | 64 | void UndoStack::clearModified() 65 | { 66 | originalState_ = undoStack_.size(); 67 | } 68 | -------------------------------------------------------------------------------- /undo_stack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "coord.hpp" 3 | #include 4 | 5 | class BaseUndoCommand 6 | { 7 | public: 8 | virtual ~BaseUndoCommand(); 9 | virtual void undo(Coord &cursor) = 0; 10 | virtual void redo(Coord &cursor) = 0; 11 | }; 12 | 13 | template 14 | class UndoCommand: public BaseUndoCommand 15 | { 16 | public: 17 | UndoCommand(Coord cursor, const RedoFunc &redoFunc, const UndoFunc &undoFunc): 18 | redoFunc_(redoFunc), 19 | undoFunc_(undoFunc), 20 | cursor_(cursor) 21 | {} 22 | virtual void undo(Coord &cursor) 23 | { 24 | Coord tmp = cursor_; 25 | undoFunc_(tmp, data_); 26 | cursor = tmp; 27 | } 28 | virtual void redo(Coord &cursor) 29 | { 30 | Coord tmp = cursor_; 31 | data_ = redoFunc_(tmp); 32 | cursor = tmp; 33 | } 34 | private: 35 | RedoFunc redoFunc_; 36 | UndoFunc undoFunc_; 37 | typedef decltype(redoFunc_(*(new Coord))) Data; 38 | Data data_; 39 | Coord cursor_; 40 | }; 41 | 42 | class UndoStack 43 | { 44 | public: 45 | UndoStack(); 46 | ~UndoStack(); 47 | template 48 | void push(Coord &cursor, const RedoFunc &redoFunc, const UndoFunc &undoFunc) 49 | { 50 | auto command = new UndoCommand{cursor, redoFunc, undoFunc}; 51 | undoStack_.push_back(command); 52 | command->redo(cursor); 53 | for (auto c: redoStack_) 54 | delete c; 55 | redoStack_.clear(); 56 | } 57 | void undo(Coord &cursor); 58 | void redo(Coord &cursor); 59 | bool canUndo() const; 60 | bool canRedo() const; 61 | void clean(); 62 | bool isModified() const; 63 | void clearModified(); 64 | public: 65 | std::vector undoStack_; 66 | std::vector redoStack_; 67 | std::vector::size_type originalState_; 68 | }; 69 | -------------------------------------------------------------------------------- /widget.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.hpp" 2 | #include "painter.hpp" 3 | #include "paint_event.hpp" 4 | #include "resize_event.hpp" 5 | #include "layout.hpp" 6 | #include "application.hpp" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | Widget::Widget(Widget *parent): 14 | parent_(parent), 15 | texture_(nullptr), 16 | width_(640), 17 | height_(480), 18 | left_(0), 19 | top_(0), 20 | needRepaint_(true), 21 | layout_(nullptr) 22 | { 23 | if (!parent_) 24 | { 25 | window_ = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width(), height(), SDL_WINDOW_RESIZABLE); 26 | renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); 27 | if (renderer_ == nullptr) 28 | throw std::runtime_error(std::string("SDL_CreateRenderer Error: ") + SDL_GetError()); 29 | Application::instance()->addWidget(this); 30 | } 31 | else 32 | parent_->addChild(this); 33 | } 34 | 35 | Widget::~Widget() 36 | { 37 | if (texture_) 38 | SDL_DestroyTexture(texture_); 39 | if (!parent_) 40 | { 41 | SDL_DestroyRenderer(renderer_); 42 | SDL_DestroyWindow(window_); 43 | Application::instance()->removeWidget(this); 44 | } 45 | else 46 | parent_->removeChild(this); 47 | } 48 | 49 | void Widget::resize(int width, int height) 50 | { 51 | width_ = width; 52 | height_ = height; 53 | if (texture_) 54 | SDL_DestroyTexture(texture_); 55 | texture_ = SDL_CreateTexture(renderer(), SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, width_, height_); 56 | 57 | ResizeEvent e; 58 | e.width = width; 59 | e.height = height; 60 | if (layout_) 61 | { 62 | layout_->resize(width, height); 63 | layout_->setLeft(0); 64 | layout_->setTop(0); 65 | } 66 | resizeEvent(e); 67 | update(); 68 | } 69 | 70 | int Widget::width() const 71 | { 72 | return width_; 73 | } 74 | 75 | void Widget::setWidth(int value) 76 | { 77 | resize(value, height()); 78 | } 79 | 80 | int Widget::height() const 81 | { 82 | return height_; 83 | } 84 | 85 | void Widget::setHeight(int value) 86 | { 87 | resize(width(), value); 88 | } 89 | 90 | int Widget::left() const 91 | { 92 | return left_; 93 | } 94 | 95 | void Widget::setLeft(int value) 96 | { 97 | left_ = value; 98 | } 99 | 100 | int Widget::top() const 101 | { 102 | return top_; 103 | } 104 | 105 | void Widget::setTop(int value) 106 | { 107 | top_ = value; 108 | } 109 | 110 | Widget *Widget::parent() const 111 | { 112 | return parent_; 113 | } 114 | 115 | Widget *Widget::ancestor() 116 | { 117 | Widget *res = this; 118 | while (res->parent()) 119 | res = res->parent(); 120 | return res; 121 | } 122 | 123 | 124 | void Widget::setFocus() 125 | { 126 | Application::instance()->setFocusWidget(this); 127 | } 128 | 129 | void Widget::clearFocus() 130 | { 131 | if (hasFocus()) 132 | Application::instance()->clearFocus(); 133 | } 134 | 135 | bool Widget::hasFocus() 136 | { 137 | return Application::instance()->focusWidget() == this; 138 | } 139 | 140 | const std::vector &Widget::children() const 141 | { 142 | return children_; 143 | } 144 | 145 | Uint32 Widget::windowId() const 146 | { 147 | return SDL_GetWindowID(window_); 148 | } 149 | 150 | SDL_Renderer *Widget::renderer() 151 | { 152 | return ancestor()->renderer_; 153 | } 154 | 155 | int Widget::gLeft() const 156 | { 157 | int res = 0; 158 | auto w = this; 159 | while (w) 160 | { 161 | res += w->left(); 162 | w = w->parent(); 163 | } 164 | return res; 165 | } 166 | 167 | int Widget::gTop() const 168 | { 169 | int res = 0; 170 | auto w = this; 171 | while (w) 172 | { 173 | res += w->top(); 174 | w = w->parent(); 175 | } 176 | return res; 177 | } 178 | 179 | void Widget::update() 180 | { 181 | ancestor()->needRepaint_ = true; 182 | } 183 | 184 | void Widget::setLayout(Layout *value) 185 | { 186 | layout_ = value; 187 | ancestor()->resize(width_, height_); 188 | } 189 | 190 | Layout *Widget::layout() 191 | { 192 | return layout_; 193 | } 194 | 195 | const Layout *Widget::layout() const 196 | { 197 | return layout_; 198 | } 199 | 200 | int Widget::maxHeight() const 201 | { 202 | return std::numeric_limits::max(); 203 | } 204 | 205 | int Widget::minHeight() const 206 | { 207 | return 0; 208 | } 209 | 210 | int Widget::maxWidth() const 211 | { 212 | return std::numeric_limits::max(); 213 | } 214 | 215 | int Widget::minWidth() const 216 | { 217 | return 0; 218 | } 219 | 220 | bool Widget::keyPressEvent(KeyEvent &) 221 | { 222 | return false; 223 | } 224 | 225 | bool Widget::keyReleaseEvent(KeyEvent &) 226 | { 227 | return false; 228 | } 229 | 230 | bool Widget::textInputEvent(TextInputEvent &) 231 | { 232 | return false; 233 | } 234 | 235 | bool Widget::mouseDoubleClickEvent(MouseEvent &) 236 | { 237 | return false; 238 | } 239 | 240 | bool Widget::mouseMoveEvent(MouseEvent &) 241 | { 242 | return false; 243 | } 244 | 245 | bool Widget::mousePressEvent(MouseEvent &) 246 | { 247 | return false; 248 | } 249 | 250 | bool Widget::mouseReleaseEvent(MouseEvent &) 251 | { 252 | return false; 253 | } 254 | 255 | void Widget::paintEvent(PaintEvent &) 256 | { 257 | } 258 | 259 | void Widget::resizeEvent(ResizeEvent &event) 260 | { 261 | std::cout << " resizeEvent: " << event.width << "x" << event.height << std::endl; 262 | } 263 | 264 | void Widget::addChild(Widget *w) 265 | { 266 | children_.push_back(w); 267 | } 268 | 269 | void Widget::removeChild(Widget *w) 270 | { 271 | children_.erase(std::remove(begin(children_), end(children_), w), end(children_)); 272 | } 273 | 274 | void Widget::updateWithoutRedraw() 275 | { 276 | SDL_Rect r; 277 | r.x = gLeft(); 278 | r.y = gTop(); 279 | r.w = width(); 280 | r.h = height(); 281 | SDL_SetRenderTarget(renderer(), nullptr); 282 | SDL_RenderCopy(renderer(), texture_, nullptr, &r); 283 | for (auto child: children()) 284 | child->updateWithoutRedraw(); 285 | if (!parent_) 286 | SDL_RenderPresent(renderer_); 287 | } 288 | 289 | void Widget::internalPaint(PaintEvent &event) 290 | { 291 | SDL_SetRenderTarget(renderer(), texture_); 292 | paintEvent(event); 293 | SDL_Rect r; 294 | r.x = gLeft(); 295 | r.y = gTop(); 296 | r.w = width(); 297 | r.h = height(); 298 | SDL_SetRenderTarget(renderer(), nullptr); 299 | SDL_RenderCopy(renderer(), texture_, nullptr, &r); 300 | for (auto child: children()) 301 | child->internalPaint(event); 302 | needRepaint_ = false; 303 | if (!parent_) 304 | SDL_RenderPresent(renderer_); 305 | } 306 | 307 | bool Widget::needRepaint() const 308 | { 309 | return needRepaint_; 310 | } 311 | -------------------------------------------------------------------------------- /widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "paint_device.hpp" 3 | #include "layoutable.hpp" 4 | #include 5 | #include 6 | 7 | class KeyEvent; 8 | class TextInputEvent; 9 | class MouseEvent; 10 | struct ResizeEvent; 11 | class PaintEvent; 12 | class Layout; 13 | struct SDL_Window; 14 | struct SDL_Renderer; 15 | 16 | class Widget: public PaintDevice, public Layoutable 17 | { 18 | friend class Application; 19 | public: 20 | Widget(Widget *parent = nullptr); 21 | virtual ~Widget(); 22 | virtual void resize(int width, int height); 23 | int width() const; 24 | void setWidth(int); 25 | int height() const; 26 | void setHeight(int); 27 | int left() const; 28 | virtual void setLeft(int); 29 | int top() const; 30 | virtual void setTop(int); 31 | Widget *parent() const; 32 | Widget *ancestor(); 33 | void setFocus(); 34 | void clearFocus(); 35 | bool hasFocus(); 36 | const std::vector &children() const; 37 | Uint32 windowId() const; 38 | virtual SDL_Renderer *renderer(); 39 | virtual int gLeft() const; 40 | virtual int gTop() const; 41 | void update(); 42 | void setLayout(Layout *); 43 | Layout *layout(); 44 | const Layout *layout() const; 45 | virtual int maxHeight() const; 46 | virtual int minHeight() const; 47 | virtual int maxWidth() const; 48 | virtual int minWidth() const; 49 | protected: 50 | // some of following events handlers return bool, true means event handled and does not require handling from the parent object 51 | virtual bool keyPressEvent(KeyEvent &); 52 | virtual bool keyReleaseEvent(KeyEvent &); 53 | virtual bool textInputEvent(TextInputEvent &); 54 | virtual bool mouseDoubleClickEvent(MouseEvent &); 55 | virtual bool mouseMoveEvent(MouseEvent &); 56 | virtual bool mousePressEvent(MouseEvent &); 57 | virtual bool mouseReleaseEvent(MouseEvent &); 58 | virtual void paintEvent(PaintEvent &); 59 | virtual void resizeEvent(ResizeEvent &); 60 | private: 61 | SDL_Window *window_; 62 | Widget *parent_; 63 | std::vector children_; 64 | SDL_Renderer *renderer_; 65 | SDL_Texture *texture_; 66 | int width_; 67 | int height_; 68 | int left_; 69 | int top_; 70 | bool needRepaint_; 71 | Layout *layout_; 72 | void addChild(Widget *); 73 | void removeChild(Widget *); 74 | void internalPaint(PaintEvent &); 75 | void updateWithoutRedraw(); 76 | Widget(const Widget &); 77 | Widget &operator=(const Widget &); 78 | bool needRepaint() const; 79 | }; 80 | --------------------------------------------------------------------------------