Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,21 @@ jobs:
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install nsis -y --no-progress
$installed = $false
for ($attempt = 1; $attempt -le 3; $attempt++) {
choco install nsis -y --no-progress
if ($LASTEXITCODE -eq 0) {
$installed = $true
break
}
if ($attempt -lt 3) {
Write-Host "NSIS installation attempt $attempt failed; retrying after 15 seconds."
Start-Sleep -Seconds 15
}
}
if (-not $installed) {
throw 'NSIS installation failed after 3 attempts.'
}
$env:Path = "C:\Program Files (x86)\NSIS;$env:Path"
if (-not (Get-Command makensis.exe -ErrorAction SilentlyContinue)) {
throw 'NSIS compiler was not installed.'
Expand Down
94 changes: 85 additions & 9 deletions src/features/ui/platform/macos/ui_macos.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "axyne/layout_metrics.h"
#include "axyne/syntax.h"
#include "axyne/completion.h"
#include "axyne/memory_usage.h"
#include "Scintilla.h"
#include "../../editor_document.h"
#include "../../editor_actions.h"
Expand Down Expand Up @@ -1481,6 +1482,7 @@ @interface AxyneWorkspaceView : NSView <NSMenuItemValidation, AxyneProblemsViewD
AxynePaletteController _palette;
AxynePaletteOverlay *_paletteOverlay;
NSTextField *_paletteField;
NSTimer *_statusTimer;
NSTimer *_paletteTimer;
}
@end
Expand Down Expand Up @@ -2287,6 +2289,12 @@ - (instancetype)initWithFrame:(NSRect)frame
return self;
}

- (void)statusTimerTick:(NSTimer *)timer
{
(void)timer;
[self setNeedsDisplay:YES];
}

- (void)updateChromeTitles
{
BOOL reference = axyne_macos_reference_surfaces(&_preferences.theme);
Expand Down Expand Up @@ -5226,6 +5234,11 @@ - (BOOL)requireEditorFor:(NSString *)action
- (void)viewDidMoveToWindow
{
[super viewDidMoveToWindow];
[_statusTimer invalidate];
_statusTimer = nil;
if ([self window] != nil)
_statusTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self
selector:@selector(statusTimerTick:) userInfo:nil repeats:YES];
[self loadScintillaView];
[self applyPreferences];
[self loadActiveDocument];
Expand Down Expand Up @@ -7134,16 +7147,77 @@ - (void)drawRect:(NSRect)dirtyRect
[self drawLabel:status at:NSMakePoint(12, statusTop + 5) size:11
color:_lastExitFailed ? axyne_preference_color(0xe5a445) :
(_hasExitStatus ? axyne_preference_color(0xa3c98a) : muted) family:@"SF Pro Text"];
if (![self isEmptyState]) {
/* No editor position in the empty state, so the whole editing
* summary (line/column, indent style, encoding) stays blank. */
NSInteger caret = [self sendEditorMessage:SCI_GETCURRENTPOS wParam:0 lParam:0];
NSInteger line = [self sendEditorMessage:SCI_LINEFROMPOSITION wParam:caret lParam:0] + 1;
NSInteger column = [self sendEditorMessage:SCI_GETCOLUMN wParam:caret lParam:0] + 1;
NSString *editing = [NSString stringWithFormat:@"줄 %ld, 열 %ld %@: %u UTF-8",
{
AxyneDocument *document = [self isEmptyState] ? NULL : [self activeDocument];
NSInteger line = 1, column = 1, eol = SC_EOL_LF, codepage = SC_CP_UTF8;
AxyneLanguageId language = document != NULL && document->path != NULL
? axyne_language_for_path(document->path) : AXYNE_LANGUAGE_NONE;
const char *languageName = language != AXYNE_LANGUAGE_NONE
? axyne_language_name(language) : "Plain Text";
const char *toolchain = _buildTarget.platform == AXYNE_PLATFORM_WINDOWS ? "MSVC" : "Clang";
char target[96];
AxyneMemoryUsage memory;
char gauge[64];
NSString *encoding;
NSString *eolName;
NSString *metadata;
NSFont *statusFont = [NSFont fontWithName:@"SF Pro Text" size:11];
NSDictionary *statusAttributes;
CGFloat memoryWidth, metadataWidth, barWidth = 96;
CGFloat cursorX;
CGFloat fillWidth;

if (document != NULL && _editorView != nil) {
NSInteger caret = [self sendEditorMessage:SCI_GETCURRENTPOS wParam:0 lParam:0];
line = [self sendEditorMessage:SCI_LINEFROMPOSITION wParam:caret lParam:0] + 1;
column = [self sendEditorMessage:SCI_GETCOLUMN wParam:caret lParam:0] + 1;
eol = [self sendEditorMessage:SCI_GETEOLMODE wParam:0 lParam:0];
codepage = [self sendEditorMessage:SCI_GETCODEPAGE wParam:0 lParam:0];
}
if (_runtimesDiscovered && language != AXYNE_LANGUAGE_NONE && document != NULL &&
document->path != NULL) {
AxyneLanguagePlan plan;
char message[256];
memset(&plan, 0, sizeof(plan));
if (axyne_language_resolve_runner(language, &_runtimes, &_buildTarget,
document->path, NULL, &plan, message, sizeof(message)) == AXYNE_STATUS_OK &&
plan.toolchain_name != NULL && plan.toolchain_name[0] != '\0')
toolchain = plan.toolchain_name;
axyne_language_plan_free(&plan);
}
(void)snprintf(target, sizeof(target), "%s %s %s", toolchain,
axyne_architecture_name(_buildTarget.platform, _buildTarget.architecture),
axyne_configuration_name(_buildTarget.configuration));
encoding = codepage == SC_CP_UTF8 ? @"UTF-8" :
[NSString stringWithFormat:@"CP%ld", (long)codepage];
eolName = eol == SC_EOL_CRLF ? @"CRLF" : (eol == SC_EOL_CR ? @"CR" : @"LF");
if (axyne_memory_usage_sample(&memory, NULL) != AXYNE_STATUS_OK)
memset(&memory, 0, sizeof(memory));
(void)axyne_memory_format_gauge(memory.resident_bytes,
AXYNE_MEMORY_BUDGET_BYTES, gauge, sizeof(gauge));
metadata = [NSString stringWithFormat:@"줄 %ld, 열 %ld %@: %u %@ %@ %s %s",
(long)line, (long)column, _preferences.editor.insert_spaces ? @"공백" : @"탭",
_preferences.editor.tab_width];
[self drawLabel:editing at:NSMakePoint(MAX(180, width - 320), statusTop + 5)
_preferences.editor.tab_width, encoding, eolName, languageName, target];
statusAttributes = @{NSFontAttributeName: statusFont != nil ? statusFont : [NSFont systemFontOfSize:11]};
memoryWidth = [[NSString stringWithUTF8String:gauge] sizeWithAttributes:statusAttributes].width;
cursorX = width - 12 - barWidth;
metadata = [self label:metadata fittingWidth:MAX(0, cursorX - 18 - 180)
size:11 family:@"SF Pro Text"];
metadataWidth = [metadata sizeWithAttributes:statusAttributes].width;
[[axyne_preference_color(0x343942) colorWithAlphaComponent:1.0] setFill];
NSRectFill(NSMakeRect(cursorX, statusTop + 8, barWidth, 8));
fillWidth = MIN(barWidth, barWidth * (CGFloat)memory.resident_bytes /
(CGFloat)AXYNE_MEMORY_BUDGET_BYTES);
if (fillWidth > 0) {
[axyne_preference_color(axyne_memory_usage_level(memory.resident_bytes) ==
AXYNE_MEMORY_LEVEL_NORMAL ? 0xa66bf0 : 0xe5a445) setFill];
NSRectFill(NSMakeRect(cursorX, statusTop + 8, fillWidth, 8));
}
cursorX -= 10 + memoryWidth;
[self drawLabel:[NSString stringWithUTF8String:gauge]
at:NSMakePoint(cursorX, statusTop + 5) size:11 color:text family:@"SF Pro Text"];
cursorX -= 18 + metadataWidth;
[self drawLabel:metadata at:NSMakePoint(MAX(180, cursorX), statusTop + 5)
size:11 color:text family:@"SF Pro Text"];
}
if (_editorView == nil)
Expand All @@ -7152,6 +7226,8 @@ - (void)drawRect:(NSRect)dirtyRect
}
- (void)dealloc
{
[_statusTimer invalidate];
_statusTimer = nil;
if (_popup != nil) {
[_popup setDelegate:nil];
[_popup close];
Expand Down
106 changes: 96 additions & 10 deletions src/features/ui/platform/windows/ui_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "axyne/layout_metrics.h"
#include "axyne/syntax.h"
#include "axyne/completion.h"
#include "axyne/memory_usage.h"
#include "axyne/document.h"
#include "axyne/empty_state.h"
#include "axyne/search.h"
Expand Down Expand Up @@ -140,6 +141,7 @@ enum {
AXYNE_GIT_MESSAGE = 5030, /* commit message EDIT control id */
AXYNE_GIT_MODE = 5031,
AXYNE_GIT_REFRESH_TIMER = 5120,
AXYNE_STATUS_TIMER = 5123,
AXYNE_GIT_REFRESH_DELAY = 300,
AXYNE_GIT_GRAPH_STEP = 200, /* commits added per "더 불러오기" */
AXYNE_GIT_HSCROLL_STEP = 48 /* graph pixels per horizontal wheel notch */
Expand Down Expand Up @@ -8987,6 +8989,7 @@ static void axyne_paint_shell(HWND window, AxyneWindowState *state)
int status_top = height - AXYNE_STATUS;
int bottom_top = status_top - axyne_panel_height(state);
int editor_top = AXYNE_TOP_MENU + AXYNE_TOOLBAR + AXYNE_TABS;
int status_metadata_left = max(axyne_sidebar_width(state) + 8, width - 620);

/* Paint the shell as one frame while sidebar child controls are moved.
* This prevents the class background from flashing during tab switches. */
Expand Down Expand Up @@ -9204,20 +9207,97 @@ static void axyne_paint_shell(HWND window, AxyneWindowState *state)
if (state->lsp_status[0] != '\0') {
wchar_t *lsp_status = axyne_wide(state->lsp_status);
if (lsp_status != NULL) {
RECT rect = {axyne_sidebar_size(state) + 8, status_top, width - 250, height};
RECT rect = {axyne_sidebar_size(state) + 8, status_top,
max(axyne_sidebar_size(state) + 8, status_metadata_left - 8), height};
axyne_text_rect(dc, state->ui_font, AXYNE_MUTED, rect, lsp_status, DT_LEFT);
free(lsp_status);
}
}
if (state->editor != NULL && axyne_active_visible(state) != NULL) {
wchar_t position[96];
LRESULT caret = SendMessageA(state->editor, SCI_GETCURRENTPOS, 0, 0);
LRESULT line = SendMessageA(state->editor, SCI_LINEFROMPOSITION, (WPARAM)caret, 0);
LRESULT column = SendMessageA(state->editor, SCI_GETCOLUMN, (WPARAM)caret, 0);
RECT rect = {width - 246, status_top, width - 12, height};
(void)swprintf_s(position, 96, L"줄 %lld, 열 %lld UTF-8",
(long long)line + 1, (long long)column + 1);
axyne_text_rect(dc, state->ui_font, AXYNE_MUTED, rect, position, DT_RIGHT);
{
AxyneDocument *document = axyne_active_visible(state);
AxyneLanguageId language = axyne_active_language(state);
const char *language_name = language != AXYNE_LANGUAGE_NONE
? axyne_language_name(language) : "Plain Text";
const char *toolchain = "MSVC";
char target[96], gauge[64];
wchar_t metadata[384], gauge_wide[96], language_wide[64], target_wide[96], encoding[32];
AxyneMemoryUsage memory;
LRESULT line = 0, column = 0, eol = SC_EOL_LF, codepage = SC_CP_UTF8;
const wchar_t *eol_name = L"LF";
int bar_width = 96;
int bar_left = width - 12 - bar_width;
int metadata_right;
int metadata_left;
RECT rect;
if (state->editor != NULL && document != NULL) {
LRESULT caret = SendMessageA(state->editor, SCI_GETCURRENTPOS, 0, 0);
line = SendMessageA(state->editor, SCI_LINEFROMPOSITION, (WPARAM)caret, 0);
column = SendMessageA(state->editor, SCI_GETCOLUMN, (WPARAM)caret, 0);
eol = SendMessageA(state->editor, SCI_GETEOLMODE, 0, 0);
codepage = SendMessageA(state->editor, SCI_GETCODEPAGE, 0, 0);
}
if (eol == SC_EOL_CRLF) eol_name = L"CRLF";
else if (eol == SC_EOL_CR) eol_name = L"CR";
if (state->runtimes_discovered && language != AXYNE_LANGUAGE_NONE &&
document != NULL && document->path != NULL) {
AxyneLanguagePlan plan;
char message[256];
memset(&plan, 0, sizeof(plan));
if (axyne_language_resolve_runner(language, &state->runtimes,
&state->build_target, document->path, NULL, &plan,
message, sizeof(message)) == AXYNE_STATUS_OK &&
plan.toolchain_name != NULL && plan.toolchain_name[0] != '\0')
toolchain = plan.toolchain_name;
axyne_language_plan_free(&plan);
}
(void)snprintf(target, sizeof(target), "%s %s %s", toolchain,
axyne_architecture_name(state->build_target.platform,
state->build_target.architecture),
axyne_configuration_name(state->build_target.configuration));
memset(&memory, 0, sizeof(memory));
(void)axyne_memory_usage_sample(&memory, NULL);
(void)axyne_memory_format_gauge(memory.resident_bytes,
AXYNE_MEMORY_BUDGET_BYTES, gauge, sizeof(gauge));
(void)swprintf_s(language_wide, 64, L"%hs", language_name);
(void)swprintf_s(target_wide, 96, L"%hs", target);
(void)swprintf_s(gauge_wide, 96, L"%hs", gauge);
if (codepage == SC_CP_UTF8)
(void)swprintf_s(encoding, 32, L"UTF-8");
else
(void)swprintf_s(encoding, 32, L"CP%lld", (long long)codepage);
(void)swprintf_s(metadata, 384,
L"줄 %lld, 열 %lld %s: %u %s %s %s %s",
(long long)line + 1, (long long)column + 1,
state->preferences.editor.insert_spaces ? L"공백" : L"탭",
state->preferences.editor.tab_width,
encoding,
eol_name, language_wide, target_wide);
metadata_right = bar_left - 16 - axyne_measure_text(state->font_small, gauge_wide);
metadata_left = max(status_metadata_left,
metadata_right - axyne_measure_text(state->font_small, metadata) - 4);
rect.left = metadata_left;
rect.top = status_top;
rect.right = metadata_right;
rect.bottom = height;
axyne_text_rect(dc, state->font_small, AXYNE_TEXT, rect, metadata, DT_RIGHT);
rect.left = metadata_right + 4;
rect.right = bar_left - 4;
axyne_text_rect(dc, state->font_small, AXYNE_TEXT, rect, gauge_wide, DT_RIGHT);
axyne_round_fill(dc, bar_left, status_top + 8, width - 12, status_top + 16,
4, axyne_blend(AXYNE_STATUS_BG, AXYNE_TEXT, 20),
axyne_blend(AXYNE_STATUS_BG, AXYNE_TEXT, 20));
{
int fill = (int)((double)bar_width * (double)memory.resident_bytes /
(double)AXYNE_MEMORY_BUDGET_BYTES);
if (fill > bar_width) fill = bar_width;
if (fill > 0)
axyne_round_fill(dc, bar_left, status_top + 8, bar_left + fill,
status_top + 16, 4,
axyne_memory_usage_level(memory.resident_bytes) ==
AXYNE_MEMORY_LEVEL_NORMAL ? AXYNE_ACCENT : AXYNE_RUN_TEXT,
axyne_memory_usage_level(memory.resident_bytes) ==
AXYNE_MEMORY_LEVEL_NORMAL ? AXYNE_ACCENT : AXYNE_RUN_TEXT);
}
}

if (axyne_empty_state(state))
Expand Down Expand Up @@ -9281,6 +9361,7 @@ static LRESULT CALLBACK axyne_window_proc(HWND window, UINT message,
FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, FIXED_PITCH | FF_MODERN, L"Cascadia Mono");
state->main_window = window;
(void)SetTimer(window, AXYNE_STATUS_TIMER, 2000, NULL);
state->problem_selected = -1;
axyne_outline_init(&state->outline);
axyne_open_scintilla(state, window, instance);
Expand Down Expand Up @@ -9371,6 +9452,10 @@ static LRESULT CALLBACK axyne_window_proc(HWND window, UINT message,
axyne_git_panel_refresh(window, state);
return 0;
}
if (state != NULL && w_param == AXYNE_STATUS_TIMER) {
InvalidateRect(window, NULL, FALSE);
return 0;
}
break;
case AXYNE_WM_PALETTE_CLOSE:
if (state != NULL && state->palette.active &&
Expand Down Expand Up @@ -10107,6 +10192,7 @@ static LRESULT CALLBACK axyne_window_proc(HWND window, UINT message,
/* WM_PAINT covers the complete client area. */
return 1;
case WM_DESTROY:
KillTimer(window, AXYNE_STATUS_TIMER);
if (state != NULL) axyne_cancel_outline_timer(state);
/* Child controls still exist during the parent's WM_DESTROY. Their
* handles are already invalid by WM_NCDESTROY, so release every tab's
Expand Down
Loading