add a simple text editor

This commit is contained in:
Chee Yee
2023-02-23 04:16:49 -08:00
parent 1cfeece53f
commit e519343164
20 changed files with 302 additions and 725 deletions
+1 -2
View File
@@ -33,8 +33,6 @@ add_executable(ezremote_client
source/http/iis.cpp
source/http/nginx.cpp
source/http/npxserve.cpp
source/xt_editor/editor.cpp
source/xt_editor/getline.c
source/actions.cpp
source/config.cpp
source/fs.cpp
@@ -59,6 +57,7 @@ add_self(ezremote_client)
add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.02" 32 0)
target_link_libraries(ezremote_client
dbglogger
c
c++
png
+5
View File
@@ -115,3 +115,8 @@ STR_ENABLE_RPI=RPI
STR_ENABLE_RPI_FTP_SMB_MSG=This option enables Remote Package Installation. This requires a HTTP Server setup on the same host sharing the same folder with anonymous access.
STR_ENABLE_RPI_WEBDAV_MSG=This option enables Remote Package Installation. This requires the Server with anonymous access that does not need username/password.
STR_FILES=Files
STR_EDITOR=Editor
STR_SAVE=Save
STR_MAX_EDIT_FILE_SIZE_MSG=Cannot edit files bigger than 32KB
STR_DELETE_LINE=Delete Selected Line
STR_INSERT_LINE=Insert Below Selected Line
+3 -1
View File
@@ -44,9 +44,11 @@ enum ACTIONS
ACTION_LOCAL_CUT,
ACTION_LOCAL_COPY,
ACTION_LOCAL_PASTE,
ACTION_LOCAL_EDIT,
ACTION_REMOTE_CUT,
ACTION_REMOTE_COPY,
ACTION_REMOTE_PASTE
ACTION_REMOTE_PASTE,
ACTION_REMOTE_EDIT
};
enum OverWriteType
+4
View File
@@ -27,6 +27,7 @@ std::map<std::string, RemoteSettings> site_settings;
PackageUrlInfo install_pkg_url;
char favorite_urls[MAX_FAVORITE_URLS][512];
bool auto_delete_tmp_pkg;
int max_edit_file_size;
RemoteClient *remoteclient;
namespace CONFIG
@@ -91,6 +92,9 @@ namespace CONFIG
auto_delete_tmp_pkg = ReadBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, true);
WriteBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, auto_delete_tmp_pkg);
max_edit_file_size = ReadInt(CONFIG_GLOBAL, CONFIG_MAX_EDIT_FILE_SIZE, MAX_EDIT_FILE_SIZE);
WriteInt(CONFIG_GLOBAL, CONFIG_MAX_EDIT_FILE_SIZE, max_edit_file_size);
for (int i = 0; i < sites.size(); i++)
{
RemoteSettings setting;
+5
View File
@@ -12,6 +12,7 @@
#define DATA_PATH "/data/" APP_ID
#define CONFIG_INI_FILE DATA_PATH "/config.ini"
#define COOKIE_FILE DATA_PATH "/cookies.txt"
#define TMP_EDITOR_FILE DATA_PATH "/tmp_editor.txt"
#define CONFIG_GLOBAL "Global"
@@ -28,6 +29,7 @@
#define CONFIG_FAVORITE_URLS "favorite_urls"
#define MAX_FAVORITE_URLS 30
#define CONFIG_MAX_EDIT_FILE_SIZE "max_edit_file_size"
#define CONFIG_LAST_SITE "last_site"
#define CONFIG_AUTO_DELETE_TMP_PKG "auto_delete_tmp_pkg"
@@ -42,6 +44,8 @@
#define HTTP_SERVER_NGINX "Nginx"
#define HTTP_SERVER_NPX_SERVE "Serve"
#define MAX_EDIT_FILE_SIZE 262144
struct RemoteSettings
{
char site_name[32];
@@ -76,6 +80,7 @@ extern RemoteClient *remoteclient;
extern PackageUrlInfo install_pkg_url;
extern char favorite_urls[MAX_FAVORITE_URLS][512];
extern bool auto_delete_tmp_pkg;
extern int max_edit_file_size;
namespace CONFIG
{
+61
View File
@@ -150,6 +150,67 @@ namespace FS
return data;
}
bool LoadText(std::vector<std::string> *lines, const std::string &path)
{
FILE *fd = fopen(path.c_str(), "r");
if (fd == nullptr)
return false;
lines->clear();
char buffer[1024];
short bytes_read;
std::vector<char> line = std::vector<char>(0);
do
{
bytes_read = fread(buffer, sizeof(char), 1024, fd);
if (bytes_read < 0)
{
fclose(fd);
return false;
}
for (short i = 0; i < bytes_read; i++)
{
if (buffer[i] != '\r' && buffer[i] != '\n')
{
line.push_back(buffer[i]);
}
else
{
lines->push_back(std::string(line.data(), line.size()));
line = std::vector<char>(0);
if (buffer[i] == '\r' && buffer[i+1] == '\n')
i++;
}
}
} while (bytes_read == 1024);
if (line.size()>0)
lines->push_back(std::string(line.data(), line.size()));
fclose(fd);
return true;
}
bool SaveText(std::vector<std::string> *lines, const std::string &path)
{
FILE *fd = OpenRW(path);
if (fd == nullptr)
return false;
char nl[1] = {'\n'};
for (int i=0; i < lines->size(); i++)
{
Write(fd, lines->at(i).c_str(), lines->at(i).length());
Write(fd, nl, 1);
}
fclose(fd);
return true;
}
void Save(const std::string &path, const void *data, uint32_t size)
{
FILE *fd = fopen(path.c_str(), "w+");
+3
View File
@@ -51,6 +51,9 @@ namespace FS
int Write(FILE *f, const void *buffer, uint32_t size);
std::vector<char> Load(const std::string &path);
bool LoadText(std::vector<std::string> *lines, const std::string &path);
bool SaveText(std::vector<std::string> *lines, const std::string &path);
void Save(const std::string &path, const void *data, uint32_t size);
std::vector<std::string> ListFiles(const std::string &path);
+1 -1
View File
@@ -23,7 +23,7 @@ namespace GUI
{
ImGui_ImplSDL2_ProcessEvent(&event);
}
GImGui->GcCompactAll = true;
ImGui_ImplSDLRenderer_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
+5 -5
View File
@@ -10,9 +10,9 @@
#include "ime_dialog.h"
static int ime_dialog_running = 0;
static uint16_t inputTextBuffer[512+1];
static uint8_t storebuffer[512];
static char initial_ime_text[512];
static uint16_t inputTextBuffer[1024+1];
static uint8_t storebuffer[1024];
static char initial_ime_text[1024];
static int max_text_length;
static void utf16_to_utf8(const uint16_t *src, uint8_t *dst)
@@ -83,7 +83,7 @@ namespace Dialog
uint16_t title[100];
if ((initialTextBuffer && strlen(initialTextBuffer) > 511) || (Title && strlen(Title) > 99))
if ((initialTextBuffer && strlen(initialTextBuffer) > 1023) || (Title && strlen(Title) > 99))
{
ime_dialog_running = 0;
return -1;
@@ -95,7 +95,7 @@ namespace Dialog
if (initialTextBuffer)
{
snprintf(initial_ime_text, 511, "%s", initialTextBuffer);
snprintf(initial_ime_text, 1023, "%s", initialTextBuffer);
}
// converts the multibyte string src to a wide-character string starting at dest.
+6
View File
@@ -129,6 +129,12 @@ char lang_strings[LANG_STRINGS_NUM][LANG_STR_SIZE] = {
"This option enables Remote Package Installation. "
"This requires the Server with anonymous access that does not need username/password.", // STR_ENABLE_RPI_WEBDAV_MSG
"Files", // STR_FILES
"Editor", // STR_EDITOR
"Save", // STR_SAVE
"Cannot edit files bigger than", // STR_MAX_EDIT_FILE_SIZE_MSG
"Delete Selected Line", // STR_DELETE_LINE
"Insert Below Selected Line", // STR_INSERT_LINE
"Modified", // STR_MODIFIED
};
bool needs_extended_font = false;
+8 -2
View File
@@ -120,7 +120,13 @@
FUNC(STR_ENABLE_RPI) \
FUNC(STR_ENABLE_RPI_FTP_SMB_MSG) \
FUNC(STR_ENABLE_RPI_WEBDAV_MSG) \
FUNC(STR_FILES)
FUNC(STR_FILES) \
FUNC(STR_EDITOR) \
FUNC(STR_SAVE) \
FUNC(STR_MAX_EDIT_FILE_SIZE_MSG) \
FUNC(STR_DELETE_LINE) \
FUNC(STR_INSERT_LINE) \
FUNC(STR_MODIFIED)
#define GET_VALUE(x) x,
#define GET_STRING(x) #x,
@@ -130,7 +136,7 @@ enum
FOREACH_STR(GET_VALUE)
};
#define LANG_STRINGS_NUM 117
#define LANG_STRINGS_NUM 123
#define LANG_ID_SIZE 64
#define LANG_STR_SIZE 384
extern char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE];
+8 -4
View File
@@ -11,7 +11,7 @@
#include <orbis/Pad.h>
#include <orbis/AudioOut.h>
#include <orbis/Net.h>
// #include <dbglogger.h>
#include <dbglogger.h>
#include "imgui.h"
#include "SDL2/SDL.h"
@@ -118,6 +118,10 @@ void InitImgui()
0xF56F, 0xF56F, // compress
0xF0F6, 0xF0F6, // properties
0xF112, 0xF112, // cancel
0xF0DA, 0xF0DA, // arrow right
0x0031, 0x0031, // 1
0x004C, 0x004C, // L
0x0052, 0x0052, // R
0,
};
@@ -240,7 +244,7 @@ void InitImgui()
colors[ImGuiCol_PlotHistogramHovered] = panelHoverColor;
colors[ImGuiCol_ModalWindowDimBg] = bgColorBlur;
colors[ImGuiCol_DragDropTarget] = bgColor;
colors[ImGuiCol_NavHighlight] = bgColor;
colors[ImGuiCol_NavHighlight] = titleColor;
colors[ImGuiCol_Tab] = bgColor;
colors[ImGuiCol_TabActive] = panelActiveColor;
colors[ImGuiCol_TabUnfocused] = bgColor;
@@ -257,8 +261,8 @@ static void terminate()
int main()
{
// dbglogger_init();
// dbglogger_log("If you see this you've set up dbglogger correctly.");
dbglogger_init();
dbglogger_log("If you see this you've set up dbglogger correctly.");
int rc;
// No buffering
setvbuf(stdout, NULL, _IONBF, 0);
+2 -1
View File
@@ -17,7 +17,8 @@ enum RemoteActions
REMOTE_ACTION_DOWNLOAD = 64,
REMOTE_ACTION_UPLOAD = 128,
REMOTE_ACTION_INSTALL = 256,
REMOTE_ACTION_ALL = 511
REMOTE_ACTION_EDIT = 512,
REMOTE_ACTION_ALL = 1023
};
enum ClientType
+188 -3
View File
@@ -64,8 +64,18 @@ bool select_url_inprogress = false;
int favorite_url_idx = 0;
char extract_zip_folder[256];
char zip_file_path[384];
char label[256];
// Editor variables
std::vector<std::string> edit_buffer;
bool editor_inprogress = false;
char edit_line[1024];
int edit_line_num = 0;
char label[256];
bool editor_modified = false;
char edit_file[256];
int edit_line_to_select = -1;
// Overwrite dialog variables
bool dont_prompt_overwrite = false;
bool dont_prompt_overwrite_cb = false;
int confirm_transfer_state = -1;
@@ -789,7 +799,7 @@ namespace Windows
{
ImGui::SetNextWindowPos(ImVec2(1330, 300));
}
ImGui::SetNextWindowSizeConstraints(ImVec2(230, 150), ImVec2(230, 550), NULL, NULL);
ImGui::SetNextWindowSizeConstraints(ImVec2(230, 150), ImVec2(230, 600), NULL, NULL);
if (ImGui::BeginPopupModal(lang_strings[STR_ACTIONS], NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::PushID("Select All##settings");
@@ -922,6 +932,46 @@ namespace Windows
ImGui::PopID();
ImGui::Separator();
ImGui::PushID("Edit##settings");
flags = ImGuiSelectableFlags_None;
if (remote_browser_selected && remoteclient != nullptr && !(remoteclient->SupportedActions() & REMOTE_ACTION_EDIT))
{
flags = ImGuiSelectableFlags_Disabled;
}
if (ImGui::Selectable(lang_strings[STR_EDIT], false, flags | ImGuiSelectableFlags_DontClosePopups, ImVec2(220, 0)))
{
bool can_edit = true;
if (local_browser_selected)
{
if (selected_local_file.file_size > max_edit_file_size)
can_edit = false;
else
{
snprintf(edit_file, 255, "%s", selected_local_file.path);
FS::LoadText(&edit_buffer, selected_local_file.path);
}
}
else
{
if (selected_remote_file.file_size > max_edit_file_size)
can_edit = false;
else if (remoteclient != nullptr && remoteclient->Get(TMP_EDITOR_FILE, selected_remote_file.path))
{
snprintf(edit_file, 255, "%s", selected_remote_file.path);
FS::LoadText(&edit_buffer, TMP_EDITOR_FILE);
}
}
if (can_edit)
editor_inprogress = true;
else
sprintf(status_message, "%s %d", lang_strings[STR_MAX_EDIT_FILE_SIZE_MSG], max_edit_file_size);
editor_modified = false;
SetModalMode(false);
ImGui::CloseCurrentPopup();
}
ImGui::PopID();
ImGui::Separator();
if (local_browser_selected)
{
ImGui::PushID("Extract##settings");
@@ -1329,6 +1379,131 @@ namespace Windows
}
}
void ShowEditorDialog()
{
if (editor_inprogress)
{
ImGuiIO &io = ImGui::GetIO();
(void)io;
ImGuiStyle *style = &ImGui::GetStyle();
ImVec4 *colors = style->Colors;
SetModalMode(true);
ImGui::OpenPopup(lang_strings[STR_EDITOR]);
ImGui::SetNextWindowPos(ImVec2(320, 115));
ImGui::SetNextWindowSizeConstraints(ImVec2(1280, 80), ImVec2(1280, 850), NULL, NULL);
if (ImGui::BeginPopupModal(lang_strings[STR_EDITOR], NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImVec2 cur_pos = ImGui::GetCursorPos();
char id[128];
sprintf(id, "%s##editor", lang_strings[STR_CANCEL]);
if (ImGui::Button(id, ImVec2(635, 0)))
{
editor_inprogress = false;
SetModalMode(false);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
sprintf(id, "%s##editor", lang_strings[STR_SAVE]);
if (ImGui::Button(id, ImVec2(635, 0)))
{
bool local_browser_selected = saved_selected_browser & LOCAL_BROWSER;
bool remote_browser_selected = saved_selected_browser & REMOTE_BROWSER;
if (local_browser_selected)
{
FS::SaveText(&edit_buffer, selected_local_file.path);
selected_action = ACTION_REFRESH_LOCAL_FILES;
}
else
{
FS::SaveText(&edit_buffer, TMP_EDITOR_FILE);
if (remoteclient != nullptr)
{
remoteclient->Put(TMP_EDITOR_FILE, selected_remote_file.path);
selected_action = ACTION_REFRESH_REMOTE_FILES;
}
}
editor_inprogress = false;
SetModalMode(false);
ImGui::CloseCurrentPopup();
}
ImGui::Separator();
ImGui::BeginChild("Editor##ChildWindow", ImVec2(1275, 680));
int j = 0;
static int insert_item = -1;
for (std::vector<std::string>::iterator it = edit_buffer.begin(); it != edit_buffer.end(); it++)
{
ImGui::Text("%s", ICON_FA_CARET_RIGHT);
ImGui::SameLine();
sprintf(id, "%d##editor", j);
ImGui::PushID(id);
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f));
if (ImGui::Selectable(it->c_str(), false, ImGuiSelectableFlags_DontClosePopups, ImVec2(1275, 0)))
{
edit_line_num = j;
snprintf(edit_line, 1023, "%s", it->c_str());
ResetImeCallbacks();
ime_single_field = edit_line;
ime_field_size = 1023;
ime_after_update = AfterEditorCallback;
ime_callback = SingleValueImeCallback;
Dialog::initImeDialog(lang_strings[STR_EDIT], edit_line, 1023, ORBIS_TYPE_BASIC_LATIN, 420, 290);
gui_mode = GUI_MODE_IME;
}
ImGui::PopStyleVar();
ImGui::PopID();
if ((gui_mode != GUI_MODE_IME && j == edit_line_num) || edit_line_to_select == j)
{
SetNavFocusHere();
edit_line_num = -1;
edit_line_to_select = -1;
}
if (ImGui::IsItemHovered())
{
if (ImGui::CalcTextSize(it->c_str()).x > 1275)
{
ImGui::BeginTooltip();
ImGui::Text("%s", it->c_str());
ImGui::EndTooltip();
}
}
if (ImGui::IsItemFocused())
{
if (ImGui::IsKeyPressed(ImGuiKey_GamepadR1, false))
{
insert_item = j;
editor_modified = true;
}
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadL1, false))
{
edit_buffer.erase(it--);
editor_modified = true;
edit_line_to_select = j;
}
}
j++;
}
if (insert_item > -1)
{
if (insert_item == edit_buffer.size() - 1)
edit_buffer.push_back(std::string());
else
edit_buffer.insert(edit_buffer.begin() + insert_item + 1, std::string());
}
insert_item = -1;
ImGui::EndChild();
ImGui::Text("%s%s", (editor_modified? "**" : ""), edit_file);
ImGui::Separator();
ImGui::Text("L1 - %s R1 - %s", lang_strings[STR_DELETE_LINE], lang_strings[STR_INSERT_LINE]);
ImGui::EndPopup();
}
}
}
void MainWindow()
{
Windows::SetupWindow();
@@ -1346,6 +1521,7 @@ namespace Windows
ShowProgressDialog();
ShowActionsDialog();
ShowFavoriteUrlsDialog();
ShowEditorDialog();
}
ImGui::End();
}
@@ -1626,7 +1802,6 @@ namespace Windows
{
ime_callback(ime_result);
}
if (ime_after_update != nullptr)
{
ime_after_update(ime_result);
@@ -1754,4 +1929,14 @@ namespace Windows
remote_settings->http_port = atoi(txt_http_port);
}
}
void AfterEditorCallback(int ime_result)
{
if (ime_result == IME_DIALOG_RESULT_FINISHED)
{
std::string str = std::string(edit_line);
edit_buffer[edit_line_num] = str;
editor_modified = true;
}
}
}
+2
View File
@@ -41,6 +41,7 @@ extern ACTIONS action_to_take;
extern bool file_transfering;
extern char extract_zip_folder[];
extern char zip_file_path[];
extern std::vector<std::string> edit_buffer;
static ImVector<ImRect> s_GroupPanelLabelStack;
@@ -208,6 +209,7 @@ namespace Windows
void AfterZipFileCallback(int ime_result);
void AferServerChangeCallback(int ime_result);
void AfterHttpPortChangeCallback(int ime_result);
void AfterEditorCallback(int ime_result);
}
#endif
-40
View File
@@ -1,40 +0,0 @@
#ifndef ARRAY_H
#define ARRAY_H
#define ARRAY_INITIAL_CAPACITY 256
// note: An array whose length can dynamically change at run-time
template <typename T>
struct Array {
T& operator[](int Index) {
return Data[Index];
}
T* Data;
size_t Capacity;
size_t Index;
};
template <typename T>
inline Array<T> array_init(size_t Capacity = ARRAY_INITIAL_CAPACITY) {
Array<T> Result;
Result.Data = (T*)malloc(Capacity * sizeof(T));
Result.Capacity = Capacity;
Result.Index = 0;
return Result;
}
template <typename T>
inline void array_add(Array<T>* _Array, T Value) {
if (_Array->Index >= _Array->Capacity) {
_Array->Capacity *= 2;
_Array->Data = (T*)realloc(_Array->Data, _Array->Capacity * sizeof(T));
}
_Array->Data[_Array->Index] = Value;
_Array->Index++;
}
#endif
-385
View File
@@ -1,385 +0,0 @@
#include "xt_editor/types.h"
#include "xt_editor/editor.h"
struct EditorRow {
char* Chars;
size_t Size;
};
struct EditorState {
int CPosX;
int CPosY;
int Rows;
int Columns;
EditorRow* Row;
int RowCount;
int CharCount;
bool IsFileDirty;
char* FileName;
};
enum CursorStyle_ {
CursorStyle_Block,
CursorStyle_Block_Outline,
CursorStyle_Line,
CursorStyle_Underline,
};
typedef int CursorStyle;
struct EditorConfig {
CursorStyle Style;
bool LineBlink;
};
static EditorState State;
static EditorConfig Config;
static bool IsInitialized = false;
static int TextStart = 7;
static char LeftBuffer[16];
static float BlinkStart = 0;
static float BlinkEnd = 0;
#ifdef BUILD_WIN32
#include "editor_input_win32.cpp"
#else
void Editor_HandleInput()
{};
#endif
void Editor_OpenFile(char* Filename) {
State.FileName = Filename;
State.IsFileDirty = false;
FILE* File = fopen(Filename, "r");
if (!File)
return;
char* Line = 0;
size_t LineCapacity = 0;
ssize_t LineLength;
while ((LineLength = getline(&Line, &LineCapacity, File)) != -1) {
while (LineLength > 0 && (Line[LineLength - 1] == '\n' || Line[LineLength - 1] == '\r')) {
LineLength--;
Editor_AppendRow(Line, LineLength);
State.CharCount += LineLength;
}
}
free(Line);
fclose(File);
}
void Editor_Init() {
State.CPosX = 0;
State.CPosY = 0;
ImVec2 WindowSize = ImGui::GetWindowContentRegionMax();
float FontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x;
ImVec2 CharAdvance = ImVec2(FontSize, ImGui::GetTextLineHeightWithSpacing() * 1.0f);
State.Rows = WindowSize.y / CharAdvance.y;
State.Columns = WindowSize.x / CharAdvance.x;
State.Row = (EditorRow*)malloc(sizeof(EditorRow));
Editor_OpenFile("../src/editor.cpp");
IsInitialized = true;
Config.Style = CursorStyle_Block;
Config.LineBlink = true;
}
// https://en.wikipedia.org/wiki/UTF-8
// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code)
static int UTF8CharLength(char c) {
if ((c & 0xFE) == 0xFC)
return 6;
if ((c & 0xFC) == 0xF8)
return 5;
if ((c & 0xF8) == 0xF0)
return 4;
else if ((c & 0xF0) == 0xE0)
return 3;
else if ((c & 0xE0) == 0xC0)
return 2;
return 1;
}
int Editor_GetCharacterIndexByCursor(int X, int Y) {
int Index = 0;
int Column = 0;
EditorRow* Line = &State.Row[Y];
if (Line == 0)
return 0;
for (; Index < Line->Size && Column < X;) {
Index += UTF8CharLength(Line->Chars[Index]);
++Column;
}
return Index;
}
void Editor_RenderRows(ImVec2 WindowSize, ImVec2 Pos) {
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyle().Colors[ImGuiCol_FrameBg]);
//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
ImGui::BeginChild("Editor", WindowSize, true);
ImGui::PushAllowKeyboardFocus(true);
// Handle input here or else we can't grab the childs input
Editor_HandleInput();
if (State.CPosX < 0)
State.CPosX = 0;
if (State.CPosY < 0)
State.CPosY = 0;
static float FontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; // Get the size of the tallest char
ImVec2 CharAdvance = ImVec2(FontSize, ImGui::GetTextLineHeightWithSpacing() * 1.0f);
bool Focused = ImGui::IsWindowFocused();
float ScrollX = ImGui::GetScrollX();
float ScrollY = ImGui::GetScrollY();
int LineNum = (int)floor(ScrollY / CharAdvance.y);
int LineMax = Maximum(0, Minimum(State.RowCount - 1, LineNum + (int)floor((ScrollY + WindowSize.y) / CharAdvance.y)));
int ActualTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, LeftBuffer, nullptr, nullptr).x + TextStart;
ImDrawList* Draw = ImGui::GetWindowDrawList();
while (LineNum <= LineMax) {
ImVec2 LineStartPos = ImVec2(Pos.x, Pos.y + LineNum * CharAdvance.y);
ImVec2 TextPos = ImVec2(LineStartPos.x + ActualTextStart, LineStartPos.y);
snprintf(LeftBuffer, 16, "%*d ", (TextStart - 1), LineNum + 1);
int LineNumWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, LeftBuffer, nullptr, nullptr).x;
Draw->AddText(ImVec2(LineStartPos.x, LineStartPos.y), IM_COL32(255, 255, 255, 255), LeftBuffer);
ImVec2 Start = ImVec2(LineStartPos.x + ScrollX, LineStartPos.y);
ImVec2 End = ImVec2(Start.x + ScrollX + WindowSize.x, Start.y + CharAdvance.y);
Draw->AddRectFilled(Start, End, 0x141414);
Draw->AddRect(Start, End, 0x40808080, 1.0f);
EditorRow* Row = &State.Row[LineNum];
size_t Len = Row->Size;
if (Len > State.Columns) {
Len = State.Columns;
}
if (Config.Style != (CursorStyle_Line))
Draw->AddText(TextPos, IM_COL32(255, 255, 255, 255), Row->Chars);
// Draw the cursor
if (State.CPosY == LineNum && Focused) {
float CursorWidth = CharAdvance.x;
if (Config.Style == CursorStyle_Line || Config.Style == CursorStyle_Underline)
CursorWidth = 1.f;
int Index = Editor_GetCharacterIndexByCursor(State.CPosX, State.CPosY);
int ScaledCurX = (Index * CharAdvance.x);
int ScaledCurY = (State.CPosY * CharAdvance.y);
ImVec2 TextStartPos = ImVec2(Pos.x + ActualTextStart, Pos.y);
ImVec2 CursorStart, CursorEnd;
if (Config.Style == CursorStyle_Underline) { // We are doing underline style
CursorStart = ImVec2(TextStartPos.x + ScaledCurX, ((ScaledCurY + TextStartPos.y + CharAdvance.y) - CursorWidth) - 1);
CursorEnd = ImVec2(TextStartPos.x + ScaledCurX + CharAdvance.x, (ScaledCurY + TextStartPos.y + CharAdvance.y) - 1);
} else {
CursorStart = ImVec2(TextStartPos.x + ScaledCurX, ScaledCurY + TextStartPos.y);
CursorEnd = ImVec2(TextStartPos.x + ScaledCurX + CursorWidth, ScaledCurY + TextStartPos.y + CharAdvance.y);
}
BlinkEnd++;
float Elapsed = (BlinkEnd - BlinkStart);
static int OldCPosX = 0;
static int OldCPosY = 0;
if ((OldCPosX != State.CPosX || OldCPosY != State.CPosY) || Config.LineBlink == false) {
// Constantly render the cursor if we're in motion
(Config.Style == CursorStyle_Block_Outline) ? Draw->AddRect(CursorStart, CursorEnd, 0xffffffff, 1.0f) : Draw->AddRectFilled(CursorStart, CursorEnd, 0xffffffff);
// Draw the char of text at the cursors location in the opposite color
char* Char = (char*)malloc(sizeof(char) * 1);
Char[0] = Row->Chars[Index];
Draw->AddText(CursorStart, IM_COL32(0, 0, 0, 255), Char);
} else {
// Blink the cursor rendering
static float InitStart = 108;
if (Elapsed > InitStart) {
(Config.Style == CursorStyle_Block_Outline) ? Draw->AddRect(CursorStart, CursorEnd, 0xffffffff, 1.0f) : Draw->AddRectFilled(CursorStart, CursorEnd, 0xffffffff);
// Draw the char of text at the cursors location in the opposite color
char* Char = (char*)malloc(sizeof(char) * 1);
Char[0] = Row->Chars[Index];
Draw->AddText(CursorStart, IM_COL32(0, 0, 0, 255), Char);
if (Elapsed > (InitStart + 40))
BlinkStart = BlinkEnd;
}
}
OldCPosX = State.CPosX;
OldCPosY = State.CPosY;
}
// AddRect doesn't allow for a transparent rectangle so we need to write the character again :/
// When we use CursorStyle_Line the original text gets overwritten in a similar way.
if (Config.Style == CursorStyle_Block_Outline || Config.Style == CursorStyle_Line)
Draw->AddText(TextPos, IM_COL32(255, 255, 255, 255), Row->Chars);
{
// DEBUG DRAW: Cursor position and index
int Index = Editor_GetCharacterIndexByCursor(State.CPosX, State.CPosY);
char Temp[64];
sprintf(Temp, "%d, %d, %d", State.CPosX, State.CPosY, Index);
Draw->AddText(ImVec2(Pos.x + 550, Pos.y), IM_COL32(255, 255, 255, 255), Temp);
}
LineNum++;
}
while (LineNum >= LineMax) {
ImVec2 LineStartPos = ImVec2(Pos.x, Pos.y + LineNum * CharAdvance.y);
ImVec2 TextPos = ImVec2(LineStartPos.x + CharAdvance.x * (TextStart + 1), LineStartPos.y);
snprintf(LeftBuffer, 16, "%*s ", (TextStart - 1), "~");
int LineNumWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, LeftBuffer, nullptr, nullptr).x;
Draw->AddText(ImVec2(LineStartPos.x, LineStartPos.y), IM_COL32(255, 255, 255, 255), LeftBuffer);
ImVec2 Start = ImVec2(LineStartPos.x + ScrollX, LineStartPos.y);
ImVec2 End = ImVec2(Start.x + ScrollX + WindowSize.x, Start.y + CharAdvance.y);
Draw->AddRectFilled(Start, End, 0x141414);
Draw->AddRect(Start, End, 0x40808080, 1.0f);
LineNum++;
if (LineNum >= (LineMax)) break; // Impose some limit so that we don't render infinitly
}
ImGui::PopAllowKeyboardFocus();
ImGui::EndChild();
//ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::Text("xt editor -- \"%s\"%s %2s %dL %dC %8s (%d, %d)", State.FileName, (State.IsFileDirty) ? "*" : "", "", State.RowCount, State.CharCount, "", State.CPosX, State.CPosY);
}
void Editor_AppendRow(char* String, size_t Length) {
State.Row = (EditorRow*)realloc(State.Row, sizeof(EditorRow) * (State.RowCount + 1));
int At = State.RowCount;
State.Row[At].Size = Length;
State.Row[At].Chars = (char*)malloc(Length + 1);
memcpy(State.Row[At].Chars, String, Length);
State.Row[At].Chars[Length] = 0;
State.RowCount++;
}
bool StringsMatch(char* A, char* B) {
while (*A && *B) {
if (*A != *B){
return false;
}
++A;
++B;
}
if (*A != *B){
return false;
} else {
return true;
}
}
int StringLength(const char* String) {
int Count = 0;
while (*String++) {
++Count;
}
return Count;
}
char* StringCopy(const char* String) {
char* Result = (char*)malloc(sizeof(char) * (StringLength(String) + 1));
memcpy(Result, String, sizeof(char) * (StringLength(String) + 1));
return Result;
}
void Editor_Render() {
ImGui::Begin("xt Demo Panel", nullptr, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar);
ImGui::SetWindowSize(ImVec2(1280, 720), ImGuiCond_FirstUseEver);
if (!IsInitialized) {
Editor_Init();
}
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Quit", "Alt-F4, CTRL-Q")) {
exit(0);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View")) {
if (ImGui::MenuItem("Cursor Blink", NULL, &Config.LineBlink)) {
//ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
//ImGui::PopItemFlag();
}
{
const char* Items[] = { "Block", "Block Outline", "Line", "Underline" };
static char* CurrentItem = NULL;
ImGuiComboFlags flags = ImGuiComboFlags_NoArrowButton;
ImGuiStyle& Style = ImGui::GetStyle();
ImGui::Text("Cursor Style");
ImGui::SameLine(0, Style.ItemInnerSpacing.x);
ImGui::PushItemWidth(ImGui::CalcItemWidth() - Style.ItemInnerSpacing.x- ImGui::GetFrameHeight());
if (ImGui::BeginCombo("##custom combo", CurrentItem, ImGuiComboFlags_NoArrowButton)) {
for (int n = 0; n < IM_ARRAYSIZE(Items); n++) {
bool is_selected = (CurrentItem == Items[n]);
if (ImGui::Selectable(Items[n], is_selected)) {
CurrentItem = StringCopy(Items[n]);
if (StringsMatch(CurrentItem, "Block")) {
Config.Style = CursorStyle_Block;
}
else if (StringsMatch(CurrentItem, "Block Outline")) {
Config.Style = CursorStyle_Block_Outline;
}
else if (StringsMatch(CurrentItem, "Line")) {
Config.Style = CursorStyle_Line;
}
else if (StringsMatch(CurrentItem, "Underline")) {
Config.Style = CursorStyle_Underline;
}
}
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
ImVec2 WindowSize = ImGui::GetWindowContentRegionMax();
WindowSize.y -= 215;
ImVec2 Pos = ImGui::GetCursorScreenPos();
Editor_RenderRows(WindowSize, Pos);
ImGui::End();
}
-10
View File
@@ -1,10 +0,0 @@
#ifndef EDITOR_H
#define EDITOR_H
#include "imgui.h"
void Editor_Init();
void Editor_AppendRow(char* String, size_t Length);
void Editor_RenderRows(ImVec2 WindowSize, ImVec2 Pos);
void Editor_Render();
#endif
-56
View File
@@ -1,56 +0,0 @@
// getline() is written to POSIX spec so that it can be used on Windows platforms
// This is taken from a stackexchange answer, link has since been lost.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
typedef intptr_t ssize_t;
ssize_t getline(char** lineptr, size_t* n, FILE* stream) {
size_t pos;
int c;
if (lineptr == NULL || stream == NULL || n == NULL) {
errno = EINVAL;
return -1;
}
c = getc(stream);
if (c == EOF) {
return -1;
}
if (*lineptr == NULL) {
*lineptr = (char*)malloc(128);
if (*lineptr == NULL) {
return -1;
}
*n = 128;
}
pos = 0;
while (c != EOF) {
if (pos + 1 >= *n) {
size_t new_size = *n + (*n >> 2);
if (new_size < 128) {
new_size = 128;
}
char* new_ptr = (char*)realloc(*lineptr, new_size);
if (new_ptr == NULL) {
return -1;
}
*n = new_size;
*lineptr = new_ptr;
}
((unsigned char*)(*lineptr))[pos++] = c;
if (c == '\n') {
break;
}
c = getc(stream);
}
(*lineptr)[pos] = '\0';
return pos;
}
-215
View File
@@ -1,215 +0,0 @@
#ifndef TYPES_H
#define TYPES_H
#include <float.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#ifdef BUILD_WIN32 // MSVC
#define API_EXPORT extern "C" __declspec(dllexport)
#define API_IMPORT extern "C" __declspec(dllimport)
#elif BUILD_LINUX | BUILD_MACOS // GCC or Clang
#define API_EXPORT extern "C" __attribute__((visibility("default")))
#define API_IMPORT
#endif
#define global static
typedef unsigned int uint;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef int32 bool32;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef float real32;
typedef double real64;
typedef int8 s8;
typedef int16 s16;
typedef int32 s32;
typedef int64 s64;
typedef bool32 b32;
typedef uint8 u8;
typedef uint16 u16;
typedef uint32 u32;
typedef uint64 u64;
typedef real32 f32;
typedef real64 f64;
typedef uintptr_t umm;
typedef intptr_t smm;
#define S32Min ((s32)0x80000000)
#define S32Max ((s32)0x7fffffff)
#define U16Max 65535
#define U32Max ((u32)-1)
#define U64Max ((u64)-1)
#define F32Max FLT_MAX
#define F32Min -FLT_MAX
#define Minimum(A, B) ((A < B) ? (A) : (B))
#define Maximum(A, B) ((A > B) ? (A) : (B))
#define For(Value) For_e((Value), ArrayCount(Value))
#define For_e(Value, End) For_se((Value), 0, (End))
#define For_se(Value, Start, End) for (int (Value) = (Start); (Value) < (End); ++(Value))
// todo(jax): Should these always be 64-bit?
#define Kilobytes(Value) (((uint64)Value) * 1024LL)
#define Megabytes(Value) (Kilobytes((uint64)Value) * 1024LL)
#define Gigabytes(Value) (Megabytes((uint64)Value) * 1024LL)
#define Terabytes(Value) (Gigabytes((uint64)Value) * 1024LL)
#define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0]))
// todo(jax): swap, min, max ... macros???
#define AlignPow2(Value, Alignment) ((Value + ((Alignment) - 1)) & ~((Alignment) - 1))
#define Align4(Value) ((Value + 3) & ~3)
#define Align8(Value) ((Value + 7) & ~7)
#define Align16(Value) ((Value + 15) & ~15)
#define Stringize(x) PrimitiveStringize(x)
#define PrimitiveStringize(x) #x
inline b32 IsPow2(u32 Value) {
return ((Value & ~(Value - 1)) == Value);
}
// ANSI Color Codes
#define BLACK "\33[0;30m"
#define RED "\33[0;31m"
#define GREEN "\33[0;32m"
#define YELLOW "\33[0;33m"
#define BLUE "\33[0;34m"
#define MAGENTA "\33[0;35m"
#define CYAN "\33[0;36m"
#define WHITE "\33[0;37m"
#define LIGHT_GRAY "\33[0;37m"
#define DARK_GRAY "\33[1;30m"
#define BOLD_BLACK "\33[1;30m"
#define BOLD_RED "\33[1;31m"
#define BOLD_GREEN "\33[1;32m"
#define BOLD_YELLOW "\33[1;33m"
#define BOLD_BLUE "\33[1;34m"
#define BOLD_MAGENTA "\33[1;35m"
#define BOLD_CYAN "\33[1;36m"
#define BOLD_WHITE "\33[1;37m"
#define RESET "\33[0m"
// note(jax): Platform-independent way to perform an assertion.
// Flat out writes to zero memory to crash the program.
// todo(jax): Create some sort of assert function that creates a message box
// like in previous engines I've worked on!
#if ENGINE_SLOW
#define Assert(Expression) if (!(Expression)) { *(int*)0=0; }
#else
#define Assert(Expression)
#endif
#define InvalidCodePath Assert(!"InvalidCodePath")
// A structure that encapsulates a non-terminated buffer
struct string {
u8* Data;
umm Count;
};
inline u32 SafeTruncateU32(u64 Value) {
// todo(jax): Defines for min/max values
Assert(Value <= 0xFFFFFFFF);
u32 Result = (u32)Value;
return Result;
}
inline u16 SafeTruncateU16(u32 Value) {
// todo(jax): Defines for min/max values
Assert(Value <= 0xFFFF);
u16 Result = (u16)Value;
return Result;
}
inline u8 SafeTruncateU8(u64 Value) {
Assert(Value <= 0xFF);
u8 Result = (u8)Value;
return Result;
}
inline s32 SafeTruncateS32(s64 Value) {
if (Value >> 63) {
b32 IsSafeOperation = !(!(Value >> 32) && 0xffffffff);
if (!IsSafeOperation) {
printf("SafeTruncateS32: Performing unsafe truncation on '%lld'\n", Value);
}
return (s32)Value;
} else {
b32 IsSafeOperation = !((Value >> 32) && 0xffffffff);
if (!IsSafeOperation) {
printf("SafeTruncateS32: Performing unsafe truncation on '%lld'\n", Value);
}
return (s32)Value;
}
}
inline s16 SafeTruncateS16(s32 Value) {
if (Value >> 31) {
b32 IsSafeOperation = !(!(Value >> 16) && 0xffff);
if (!IsSafeOperation) {
printf("SafeTruncateS16: Performing unsafe truncation on '%d'\n", Value);
}
return (s16)Value;
} else {
b32 IsSafeOperation = !((Value >> 16) && 0xffff);
if (!IsSafeOperation) {
printf("SafeTruncateS16: Performing unsafe truncation on '%d'\n", Value);
}
return (s16)Value;
}
}
inline s8 SafeTruncateS8(s16 Value) {
if (Value >> 15) {
b32 IsSafeOperation = !(!(Value >> 8) && 0xff);
if (!IsSafeOperation) {
printf("SafeTruncateS8: Performing unsafe truncation on '%d'\n", Value);
}
return (s8)Value;
} else {
b32 IsSafeOperation = !((Value >> 8) && 0xff);
if (!IsSafeOperation) {
printf("SafeTruncateS8: Performing unsafe truncation on '%d'\n", Value);
}
return (s8)Value;
}
}
//
// note: Scalar operations
//
// todo(jax): These will eventually go into mathlib
inline real32 Square(real32 A) {
real32 Result = A*A;
return Result;
}
inline real32 Lerp(real32 A, real32 t, real32 B){
real32 Result = (1.0f - t)*A + t*B;
return Result;
}
#endif