Transitioning text editor to SDL

This commit is contained in:
Krzosa Karol
2024-07-26 17:08:14 +02:00
parent 27b322bf25
commit 7803bc87a6
5 changed files with 928 additions and 0 deletions

149
src/platform/font.cpp Normal file
View File

@@ -0,0 +1,149 @@
struct Glyph {
Vec2 size;
Vec2 offset;
float xadvance;
float left_side_bearing;
Rect2 atlas_bounding_box;
};
struct Font {
Array<Glyph> glyphs;
uint32_t first_char, last_char;
uint32_t texture_id;
float size;
float descent;
float ascent;
float line_gap;
Rect2 white_texture_bounding_box;
};
struct Atlas {
uint8_t *bitmap;
Vec2I size;
Vec2 inverse_size;
int xcursor;
int ycursor;
int32_t biggest_height;
Rect2 white_texture_bounding_box;
uint32_t texture_id;
};
Atlas CreateAtlas(Allocator allocator, Vec2I size) {
Atlas result = {};
result.size = size;
result.inverse_size.x = 1.f / (float)result.size.x;
result.inverse_size.y = 1.f / (float)result.size.y;
result.bitmap = AllocArray(allocator, uint8_t, size.x * size.y);
// Add a whitebox first for rectangle rendering
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
uint8_t *dst = result.bitmap + x + y * result.size.x;
*dst = 0xff;
}
}
result.white_texture_bounding_box = {2.f * result.inverse_size.x, 2.f / (float)result.size.y, 14.f * result.inverse_size.x, 14.f / (float)result.size.y};
result.xcursor += 16;
result.biggest_height += 16;
return result;
}
Rect2 PackBitmap(Atlas *atlas, uint8_t *bitmap, int width, int height) {
// Packing into a texture atlas
// @Inefficient The algorithm is a simplest thing I had in mind, first we advance
// through the atlas in X packing consecutive glyphs. After we get to the end of the row
// we advance to the next row by the Y size of the biggest packed glyph. If we get to the
// end of atlas and fail to pack everything the app panics.
int spacing = 4;
if (atlas->xcursor + width > atlas->size.x) {
if (atlas->ycursor + height < atlas->size.y) {
atlas->xcursor = 0;
atlas->ycursor += atlas->biggest_height + spacing;
} else {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Font loading error!", "Error while packing a font into atlas. Atlas size for this font scale is a bit too small", NULL);
}
}
uint8_t *src = bitmap;
for (int y = atlas->ycursor; y < atlas->ycursor + height; y += 1) {
for (int x = atlas->xcursor; x < atlas->xcursor + width; x += 1) {
uint8_t *dst = atlas->bitmap + x + y * atlas->size.x;
*dst = *src++;
}
}
Vec2 size = {(float)width * atlas->inverse_size.x, (float)height * atlas->inverse_size.y};
Vec2 pos = {(float)atlas->xcursor * atlas->inverse_size.x, (float)atlas->ycursor * atlas->inverse_size.y};
Rect2 result = {pos.x, pos.y, pos.x + size.x, pos.y + size.y};
atlas->xcursor += width + spacing;
if (height > atlas->biggest_height) {
atlas->biggest_height = height;
}
return result;
}
Font CreateFont(Atlas *atlas, int32_t size, String path) {
Scratch scratch;
Font result = {};
String file = ReadFile(scratch, path);
if (file.len == 0) {
return result;
}
stbtt_fontinfo stb_font;
int success = stbtt_InitFont(&stb_font, (const unsigned char *)file.data, 0);
if (!success) {
return result;
}
float scale = stbtt_ScaleForPixelHeight(&stb_font, (float)size);
float em_scale = stbtt_ScaleForMappingEmToPixels(&stb_font, (float)size);
int ascent, descent, line_gap;
stbtt_GetFontVMetrics(&stb_font, &ascent, &descent, &line_gap);
result.ascent = (float)ascent * scale;
result.descent = (float)descent * scale;
result.line_gap = (float)line_gap * scale;
result.size = (float)size;
result.first_char = ' ';
result.last_char = '~';
result.white_texture_bounding_box = atlas->white_texture_bounding_box;
for (uint32_t ascii_symbol = result.first_char; ascii_symbol <= result.last_char; ascii_symbol++) {
int width, height, xoff, yoff;
uint8_t *bitmap = (uint8_t *)stbtt_GetCodepointBitmap(&stb_font, 0, scale, ascii_symbol, &width, &height, &xoff, &yoff);
defer { stbtt_FreeBitmap(bitmap, 0); };
int xadvance, left_side_bearing;
stbtt_GetCodepointHMetrics(&stb_font, ascii_symbol, &xadvance, &left_side_bearing);
Glyph glyph = {};
glyph.atlas_bounding_box = PackBitmap(atlas, bitmap, width, height);
glyph.size = {(float)width, (float)height};
glyph.offset = {(float)xoff, (float)yoff};
glyph.xadvance = (float)xadvance * scale;
glyph.left_side_bearing = (float)left_side_bearing * scale;
Add(&result.glyphs, glyph);
}
return result;
}
Glyph *GetGlyph(Font *font, uint32_t codepoint) {
bool is_in_range = codepoint >= font->first_char && codepoint <= font->last_char;
if (is_in_range) {
uint32_t index = codepoint - font->first_char;
return &font->glyphs[index];
} else {
uint32_t index = '?' - font->first_char;
return &font->glyphs[index];
}
}

155
src/platform/platform.cpp Normal file
View File

@@ -0,0 +1,155 @@
#define BASIC_IMPL
#include "basic/basic.h"
#include "basic/filesystem.h"
#include "SDL3/SDL.h"
#include "external/glad/glad.h"
#include "external/stb_truetype.h"
#include "external/stb_truetype.c"
#include "basic/math_int.cpp"
#include "basic/math.cpp"
#include "font.cpp"
#include "render_opengl.cpp"
bool AppIsRunning = true;
bool WaitForEvents = false;
int64_t FrameID;
void ProcessSDLEvent(SDL_Event *event) {
switch (event->type) {
case SDL_EVENT_QUIT: AppIsRunning = false; return;
case SDL_EVENT_KEY_DOWN: {
SDL_KeyboardEvent &key = event->key;
bool shift = key.mod & SDL_KMOD_SHIFT;
bool ctrl = key.mod & SDL_KMOD_CTRL;
bool alt = key.mod & SDL_KMOD_ALT;
bool super = key.mod & SDL_KMOD_GUI;
if (key.key == SDLK_F5) {
AppIsRunning = false;
return;
}
} break;
case SDL_EVENT_KEY_UP: {
SDL_KeyboardEvent &key = event->key;
bool shift = key.mod & SDL_KMOD_SHIFT;
bool ctrl = key.mod & SDL_KMOD_CTRL;
bool alt = key.mod & SDL_KMOD_ALT;
bool super = key.mod & SDL_KMOD_GUI;
} break;
case SDL_EVENT_TEXT_INPUT: {
} break;
case SDL_EVENT_MOUSE_MOTION: {
} break;
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
} break;
case SDL_EVENT_MOUSE_BUTTON_UP: {
} break;
case SDL_EVENT_MOUSE_WHEEL: {
} break;
}
}
void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
printf("%s", message);
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL error", message, NULL);
}
}
#if _WIN32
int WinMain(void *hInstance, void *hPrevInstance, const char *lpCmdLine, int nShowCmd)
#else
int main()
#endif
{
InitScratch();
if (SDL_Init(SDL_INIT_VIDEO) == -1) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
return 1;
}
const char *glsl_version = "#version 450";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY;
SDL_Window *window = SDL_CreateWindow("Text editor", 1280, 720, window_flags);
if (window == NULL) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window!", SDL_GetError(), NULL);
return 1;
}
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
SDL_GL_SetSwapInterval(1); // Enable vsync
SDL_ShowWindow(window);
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't load opengl!", SDL_GetError(), NULL);
return 1;
}
SDL_GL_SetSwapInterval(1); // vsync
glDebugMessageCallback(&GLDebugCallback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
{
Scratch scratch;
Atlas atlas = CreateAtlas(scratch, {1024, 1024});
MainFont = CreateFont(&atlas, 16, "C:\\Windows\\Fonts\\consola.ttf");
{
glCreateTextures(GL_TEXTURE_2D, 1, &atlas.texture_id);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureStorage2D(atlas.texture_id, 1, GL_R8, (GLsizei)atlas.size.x, (GLsizei)atlas.size.y);
glTextureSubImage2D(atlas.texture_id, 0, 0, 0, (GLsizei)atlas.size.x, (GLsizei)atlas.size.y, GL_RED, GL_UNSIGNED_BYTE, atlas.bitmap);
}
MainFont.texture_id = atlas.texture_id;
}
InitRender();
while (AppIsRunning) {
FrameID += 1;
int window_x, window_y;
SDL_GetWindowSize(window, &window_x, &window_y);
Vec2 window_size = {(float)window_x, (float)window_y};
BeginFrameRender(window_size);
SDL_Event event;
if (WaitForEvents) {
SDL_WaitEvent(&event);
ProcessSDLEvent(&event);
}
while (SDL_PollEvent(&event)) {
ProcessSDLEvent(&event);
}
{
Scratch scratch;
uint64_t ms = SDL_GetTicks();
double time = (double)ms / 1000.0;
String string = Format(scratch, "%d %f %f", (int)FrameID, time, (time / (double)FrameID));
DrawString(&MainFont, ToString16(scratch, string), {0, 0}, {255, 0, 0, 255});
}
EndFrameRender({0, 0, 0, 1});
SDL_GL_SwapWindow(window);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

View File

@@ -0,0 +1,301 @@
struct Shader {
uint32_t pipeline;
uint32_t fshader;
uint32_t vshader;
};
Shader CreateShader(char *glsl_vshader, char *glsl_fshader);
struct Vertex2D {
Vec2 pos;
Vec2 tex;
Color color;
};
struct VertexNode2D {
VertexNode2D *next;
int count;
Vertex2D vertices[1024 * 64];
};
struct VertexList2D {
VertexNode2D *first;
VertexNode2D *last;
};
VertexList2D Vertices;
int64_t TotalVertexCount;
unsigned VBO, VAO;
Shader Shader2D;
Arena RenderArena;
Vec2 WindowSize;
Font MainFont;
Int FontLineSpacing;
Int FontCharSpacing;
void BeginFrameRender(Vec2 window_size) {
Clear(&RenderArena);
TotalVertexCount = 0;
Vertices.first = NULL;
Vertices.last = NULL;
WindowSize = window_size;
}
void EndFrameRender(Color color) {
glEnable(GL_BLEND);
glEnable(GL_SCISSOR_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glViewport(0, 0, (GLsizei)WindowSize.x, (GLsizei)WindowSize.y);
glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Default draw using the font texture
glBindProgramPipeline(Shader2D.pipeline);
float xinverse = 1.f / (WindowSize.x / 2.f);
float yinverse = 1.f / (WindowSize.y / 2.f);
glProgramUniform2f(Shader2D.vshader, 0, xinverse, yinverse);
for (VertexNode2D *it = Vertices.first; it; it = it->next) {
glNamedBufferSubData(VBO, 0, it->count * sizeof(Vertex2D), it->vertices);
glBindVertexArray(VAO);
GLint s_texture = 0; // texture unit that sampler2D will use in GLSL code
glBindTextureUnit(s_texture, MainFont.texture_id);
glDrawArrays(GL_TRIANGLES, 0, it->count);
}
}
void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
printf("%s", message);
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL error", message, NULL);
}
}
void InitRender() {
InitArena(&RenderArena);
glDebugMessageCallback(&GLDebugCallback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glCreateBuffers(1, &VBO);
glNamedBufferStorage(VBO, Lengthof(VertexNode2D::vertices) * sizeof(Vertex2D), 0, GL_DYNAMIC_STORAGE_BIT);
glCreateVertexArrays(1, &VAO);
GLint vbuf_index = 0;
glVertexArrayVertexBuffer(VAO, vbuf_index, VBO, 0, sizeof(struct Vertex2D));
GLint a_pos = 0;
glVertexArrayAttribFormat(VAO, a_pos, 2, GL_FLOAT, GL_FALSE, offsetof(struct Vertex2D, pos));
glVertexArrayAttribBinding(VAO, a_pos, vbuf_index);
glEnableVertexArrayAttrib(VAO, a_pos);
GLint a_tex = 1;
glVertexArrayAttribFormat(VAO, a_tex, 2, GL_FLOAT, GL_FALSE, offsetof(struct Vertex2D, tex));
glVertexArrayAttribBinding(VAO, a_tex, vbuf_index);
glEnableVertexArrayAttrib(VAO, a_tex);
GLint a_color = 2;
glVertexArrayAttribFormat(VAO, a_color, 4, GL_UNSIGNED_BYTE, GL_FALSE, offsetof(struct Vertex2D, color));
glVertexArrayAttribBinding(VAO, a_color, vbuf_index);
glEnableVertexArrayAttrib(VAO, a_color);
char *glsl_vshader = R"==(
#version 450 core
layout(location=0) uniform vec2 U_InvHalfScreenSize;
layout(location=0) in vec2 VPos;
layout(location=1) in vec2 VTex;
layout(location=2) in vec4 VColor;
out gl_PerVertex { vec4 gl_Position; }; // required because of ARB_separate_shader_objects
out vec2 FUV;
out vec4 FColor;
void main() {
vec2 pos = VPos * U_InvHalfScreenSize;
pos.y = 2 - pos.y; // invert y axis
pos -= vec2(1, 1); // convert to 0,1 range
gl_Position = vec4(pos, 0, 1);
FUV = VTex;
FColor = VColor / 255.0;
}
)==";
char *glsl_fshader = R"==(
#version 450 core
in vec2 FUV;
in vec4 FColor;
layout (binding=0) uniform sampler2D S_Texture;
layout (location=0) out vec4 OutColor;
void main() {
vec4 c = FColor;
c.a *= texture(S_Texture, FUV).r;
OutColor = c;
}
)==";
Shader2D = CreateShader(glsl_vshader, glsl_fshader);
}
Shader CreateShader(char *glsl_vshader, char *glsl_fshader) {
Shader result = {};
result.vshader = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &glsl_vshader);
result.fshader = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &glsl_fshader);
GLint linked;
glGetProgramiv(result.vshader, GL_LINK_STATUS, &linked);
if (!linked) {
char message[1024];
glGetProgramInfoLog(result.vshader, sizeof(message), NULL, message);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to create vertex shader!", message, NULL);
}
glGetProgramiv(result.fshader, GL_LINK_STATUS, &linked);
if (!linked) {
char message[1024];
glGetProgramInfoLog(result.fshader, sizeof(message), NULL, message);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to create fragment shader!", message, NULL);
}
glGenProgramPipelines(1, &result.pipeline);
glUseProgramStages(result.pipeline, GL_VERTEX_SHADER_BIT, result.vshader);
glUseProgramStages(result.pipeline, GL_FRAGMENT_SHADER_BIT, result.fshader);
return result;
}
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
do { \
(n)->next = 0; \
if ((f) == 0) { \
(f) = (l) = (n); \
} else { \
(l) = (l)->next = (n); \
} \
} while (0)
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
Vertex2D *AllocVertex2D(Allocator allocator, VertexList2D *list, int count) {
VertexNode2D *node = list->last;
if (node == 0 || node->count + count > Lengthof(node->vertices)) {
node = AllocType(allocator, VertexNode2D);
SLL_QUEUE_ADD(list->first, list->last, node);
}
TotalVertexCount += count;
Vertex2D *result = node->vertices + node->count;
node->count += count;
return result;
}
void PushVertex2D(Allocator allocator, VertexList2D *list, Vertex2D *vertices, int count) {
Vertex2D *result = AllocVertex2D(allocator, list, count);
for (int i = 0; i < count; i += 1) result[i] = vertices[i];
}
void PushQuad2D(Allocator arena, VertexList2D *list, Rect2 rect, Rect2 tex, Color color, float rotation = 0.f, Vec2 rotation_point = {}) {
Vertex2D *v = AllocVertex2D(arena, list, 6);
v[0] = {
{rect.min.x, rect.max.y},
{ tex.min.x, tex.max.y},
color
};
v[1] = {
{rect.max.x, rect.max.y},
{ tex.max.x, tex.max.y},
color
};
v[2] = {
{rect.min.x, rect.min.y},
{ tex.min.x, tex.min.y},
color
};
v[3] = {
{rect.min.x, rect.min.y},
{ tex.min.x, tex.min.y},
color
};
v[4] = {
{rect.max.x, rect.max.y},
{ tex.max.x, tex.max.y},
color
};
v[5] = {
{rect.max.x, rect.min.y},
{ tex.max.x, tex.min.y},
color
};
if (rotation != 0.f) {
float s = sinf(rotation);
float c = cosf(rotation);
for (int i = 0; i < 6; i += 1) {
v[i].pos -= rotation_point;
v[i].pos = {v[i].pos.x * c + v[i].pos.y * (-s), v[i].pos.x * s + v[i].pos.y * c};
v[i].pos += rotation_point;
}
}
}
void DrawRect(Rect2 rect, Color color) {
PushQuad2D(RenderArena, &Vertices, rect, MainFont.white_texture_bounding_box, color);
}
Vec2 DrawString(Font *font, String16 string, Vec2 pos, Color color, bool draw = true) {
pos.y += font->ascent;
Vec2 original_pos = pos;
For(string) {
Glyph *g = GetGlyph(font, it);
Rect2 rect = Rect2FromSize(pos + g->offset, g->size);
if (draw && it != '\n' && it != ' ' && it != '\t') {
PushQuad2D(RenderArena, &Vertices, rect, g->atlas_bounding_box, color);
}
pos.x += g->xadvance;
}
Vec2 result = {pos.x - original_pos.x, font->size};
return result;
}
Vec2 GetStringSize(Font *font, String16 string) {
return DrawString(font, string, {}, {}, false);
}
Int GetCharSpacing(Font *font) {
Glyph *g = GetGlyph(font, '_');
if (g->xadvance) return (Int)g->xadvance;
return (Int)g->size.x;
}
Int GetLineSpacing(Font *font) {
Int result = (Int)(font->ascent - font->descent + font->line_gap);
return result;
}
void BeginScissor(Rect2 rect) {
glScissor((GLint)rect.min.x, (GLint)rect.min.y, (GLsizei)(rect.max.x - rect.min.x), (GLsizei)(rect.max.y - rect.min.y));
}
void BeginScissor(Rect2I rect) {
glScissor((GLint)rect.min.x, (GLint)rect.min.y, (GLsizei)(rect.max.x - rect.min.x), (GLsizei)(rect.max.y - rect.min.y));
}
void EndScissor() {
glScissor(0, 0, (GLsizei)WindowSize.x, (GLsizei)WindowSize.y);
}
Rect2 GetScreenRectF() {
Rect2 result = {0, 0, WindowSize.x, WindowSize.y};
return result;
}
Rect2I GetScreenRectI() {
Rect2I result = {0, 0, (Int)WindowSize.x, (Int)WindowSize.y};
return result;
}

View File

@@ -0,0 +1,309 @@
#include <math.h>
#define BASIC_IMPL
#include "basic/basic.h"
#include "basic/filesystem.h"
#include "basic/string16.cpp"
#include "basic/math_int.cpp"
#include "basic/math.cpp"
#include "profiler/profiler.cpp"
#include "SDL3/SDL.h"
#include "external/glad/glad.h"
#include "external/stb_truetype.h"
#include "external/stb_truetype.c"
#include "platform/font.cpp"
#include "platform/render_opengl.cpp"
#include "text_editor.h"
#include "buffer_helpers.cpp"
#include "buffer.cpp"
#include "buffer_multi_cursor.cpp"
#include "buffer_history.cpp"
#include "buffer_fuzzy_search.cpp"
#include "buffer_test_load.cpp"
#include "management.cpp"
#include "window.cpp"
#include "colors.cpp"
#include "window_draw.cpp"
#include "lua.hpp"
#include "lua_api.cpp"
bool AppIsRunning = true;
bool WaitForEvents = false;
void ProcessSDLEvent(SDL_Event *event) {
switch (event->type) {
case SDL_EVENT_QUIT: AppIsRunning = false; return;
case SDL_EVENT_KEY_DOWN: {
SDL_KeyboardEvent &key = event->key;
bool shift = key.mod & SDL_KMOD_SHIFT;
bool ctrl = key.mod & SDL_KMOD_CTRL;
bool alt = key.mod & SDL_KMOD_ALT;
bool super = key.mod & SDL_KMOD_GUI;
if (key.key == SDLK_F5) {
AppIsRunning = false;
return;
}
} break;
case SDL_EVENT_KEY_UP: {
SDL_KeyboardEvent &key = event->key;
bool shift = key.mod & SDL_KMOD_SHIFT;
bool ctrl = key.mod & SDL_KMOD_CTRL;
bool alt = key.mod & SDL_KMOD_ALT;
bool super = key.mod & SDL_KMOD_GUI;
} break;
case SDL_EVENT_TEXT_INPUT: {
} break;
case SDL_EVENT_MOUSE_MOTION: {
} break;
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
} break;
case SDL_EVENT_MOUSE_BUTTON_UP: {
} break;
case SDL_EVENT_MOUSE_WHEEL: {
} break;
}
}
#if _WIN32
int WinMain(void *hInstance, void *hPrevInstance, const char *lpCmdLine, int nShowCmd)
#else
int main()
#endif
{
BeginProfiler();
BeginProfileScope("main");
InitScratch();
InitArena(&Perm);
WorkingDir = GetWorkingDir(Perm);
if (SDL_Init(SDL_INIT_VIDEO) == -1) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
return 1;
}
const char *glsl_version = "#version 450";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY;
SDL_Window *window = SDL_CreateWindow("Text editor", 1280, 720, window_flags);
if (window == NULL) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window!", SDL_GetError(), NULL);
return 1;
}
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
SDL_GL_SetSwapInterval(1); // Enable vsync
SDL_ShowWindow(window);
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't load opengl!", SDL_GetError(), NULL);
return 1;
}
SDL_GL_SetSwapInterval(1); // vsync
InitRender();
InitLua();
{
Scratch scratch;
Atlas atlas = CreateAtlas(scratch, {1024, 1024});
MainFont = CreateFont(&atlas, 16, "C:\\Windows\\Fonts\\consola.ttf");
{
glCreateTextures(GL_TEXTURE_2D, 1, &atlas.texture_id);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureStorage2D(atlas.texture_id, 1, GL_R8, (GLsizei)atlas.size.x, (GLsizei)atlas.size.y);
glTextureSubImage2D(atlas.texture_id, 0, 0, 0, (GLsizei)atlas.size.x, (GLsizei)atlas.size.y, GL_RED, GL_UNSIGNED_BYTE, atlas.bitmap);
}
MainFont.texture_id = atlas.texture_id;
FontCharSpacing = GetCharSpacing(&MainFont);
FontLineSpacing = GetLineSpacing(&MainFont);
}
Allocator sys_allocator = GetSystemAllocator();
{
Buffer *buffer = CreateBuffer(sys_allocator, "*scratch*");
}
{
Window *w = CreateWindow();
Buffer *b = CreateBuffer(sys_allocator, "*load_text_a*");
View *v = CreateView(b->id);
LoadTextA(b);
AddView(w, v->id);
}
SetActiveWindow({1});
{
Window *w = CreateWindow();
Buffer *b = CreateBuffer(sys_allocator, "*load_unicode*");
View *v = CreateView(b->id);
LoadUnicode(b);
AddView(w, v->id);
}
{
Window *w = CreateWindow();
Buffer *b = GetBuffer({0});
View *v = CreateView(b->id);
AddView(w, v->id);
}
{
Window *w = CreateWindow();
w->draw_scrollbar = false;
w->draw_line_numbers = false;
w->dont_save_in_active_window_history = true;
w->deactivate_on_escape = true;
Buffer *b = CreateBuffer(sys_allocator, "*infobar*");
b->no_history = true;
View *v = CreateView(b->id);
AddView(w, v->id);
InfoBarWindowID = w->id;
}
{
Window *w = CreateWindow();
w->draw_scrollbar = false;
w->draw_line_numbers = false;
w->visible = false;
w->fuzzy_search = true;
w->execute_line = true;
w->invisible_when_inactive = true;
w->dont_save_in_active_window_history = true;
w->deactivate_on_escape = true;
Buffer *b = CreateBuffer(sys_allocator, "*commands*");
View *v = CreateView(b->id);
AddView(w, v->id);
CommandWindowID = w->id;
// Command_EvalLua(v, L"open \"./\"");
}
{
Window *w = CreateWindow();
w->draw_scrollbar = false;
w->draw_line_numbers = false;
w->visible = false;
w->dont_save_in_active_window_history = true;
w->invisible_when_inactive = true;
w->deactivate_on_escape = true;
Buffer *b = CreateBuffer(sys_allocator, "*search*");
View *v = CreateView(b->id);
AddView(w, v->id);
SearchWindowID = w->id;
}
while (AppIsRunning) {
FrameID += 1;
int window_x, window_y;
SDL_GetWindowSize(window, &window_x, &window_y);
Vec2 window_size = {(float)window_x, (float)window_y};
BeginFrameRender(window_size);
Rect2I screen_rect = GetScreenRectI();
Rect2I infobar_rect = CutBottom(&screen_rect, (Int)FontLineSpacing);
float line_numbers_size = GetStringSize(&MainFont, L"1234567891").x;
{
int i = 5;
if (Windows[i].visible) {
Rect2I rect = CutBottom(&screen_rect, FontLineSpacing);
Windows[i].total_rect = rect;
Windows[i].document_rect = Windows[i].total_rect;
}
}
{
int i = 0;
Windows[i].total_rect = CutLeft(&screen_rect, (Int)((double)GetSize(screen_rect).x * 0.33));
Windows[i].document_rect = Windows[i].total_rect;
if (Windows[i].draw_scrollbar) Windows[i].scrollbar_rect = CutRight(&Windows[i].document_rect, 10);
if (Windows[i].draw_line_numbers) Windows[i].line_numbers_rect = CutLeft(&Windows[i].document_rect, (Int)line_numbers_size);
}
{
int i = 1;
Windows[i].total_rect = CutLeft(&screen_rect, (Int)((double)GetSize(screen_rect).x * 0.5));
Windows[i].document_rect = Windows[i].total_rect;
if (Windows[i].draw_scrollbar) Windows[i].scrollbar_rect = CutRight(&Windows[i].document_rect, 10);
if (Windows[i].draw_line_numbers) Windows[i].line_numbers_rect = CutLeft(&Windows[i].document_rect, (Int)line_numbers_size);
}
{
int i = 2;
Windows[i].total_rect = CutLeft(&screen_rect, (Int)((double)GetSize(screen_rect).x * 1.0));
Windows[i].document_rect = Windows[i].total_rect;
if (Windows[i].draw_scrollbar) Windows[i].scrollbar_rect = CutRight(&Windows[i].document_rect, 10);
if (Windows[i].draw_line_numbers) Windows[i].line_numbers_rect = CutLeft(&Windows[i].document_rect, (Int)line_numbers_size);
}
{
int i = 3;
Windows[i].total_rect = infobar_rect;
Windows[i].document_rect = Windows[i].total_rect;
}
{
int i = 4;
Rect2 screen_rect = GetScreenRectF();
Vec2 size = GetSize(screen_rect);
CutTop(&screen_rect, size.y * 0.05f);
CutLeft(&screen_rect, size.x * 0.2f);
CutRight(&screen_rect, size.x * 0.2f);
Rect2 r = CutTop(&screen_rect, FontLineSpacing * 30.f);
Windows[i].z = 1;
Windows[i].total_rect = ToRect2I(r);
Windows[i].document_rect = Windows[i].total_rect;
}
SDL_Event event;
if (WaitForEvents) {
SDL_WaitEvent(&event);
ProcessSDLEvent(&event);
}
while (SDL_PollEvent(&event)) {
ProcessSDLEvent(&event);
}
Scratch scratch;
Array<Int> order = GetWindowZOrder(scratch);
For(IterateInReverse(&order)) {
Window &window = Windows[it];
if (window.visible) {
// HandleWindowBindings(&window);
DrawWindow(window);
}
}
// {
// Scratch scratch;
// uint64_t ms = SDL_GetTicks();
// double time = (double)ms / 1000.0;
// String string = Format(scratch, "%d %f %f", (int)FrameID, time, (time / (double)FrameID));
// DrawString(&MainFont, ToString16(scratch, string), {0, 0}, {255, 0, 0, 255});
// }
EndFrameRender(ColorBackground);
SDL_GL_SwapWindow(window);
}
SDL_DestroyWindow(window);
SDL_Quit();
EndProfileScope();
EndProfiler();
return 0;
}

14
src/text_editor/todo.txt Normal file
View File

@@ -0,0 +1,14 @@
- Save file (utf16->utf8)
- resize windows
- file dock on left side
- We can actually combine this with command window and lua, it's just going to be a buffer of
- open "asd/asd/asd/asd"
- Ctrl + F
- word completion
- Colored strings
- move off raylib
- Adjust text position a little bit down?
- proper double click that works on laptop
- font cache and on demand unicode loads