[0.1.26] macOS phase 2: folder picker, the Trash, the temporary folder, copying images
This commit is contained in:
parent
12d0f30fc4
commit
5571c21d0b
@ -57,14 +57,15 @@ see [Screen capture](#screen-capture).
|
|||||||
what search needs from every file, is still loading: a line beside the count says so). **Size**
|
what search needs from every file, is still loading: a line beside the count says so). **Size**
|
||||||
sets the thumbnails' size. Click a thumbnail to open it in the Capture window.
|
sets the thumbnails' size. Click a thumbnail to open it in the Capture window.
|
||||||
- **Delete:** hover a thumbnail and click the trash button in its corner. You are asked whether to
|
- **Delete:** hover a thumbnail and click the trash button in its corner. You are asked whether to
|
||||||
move the file to the Recycle Bin or to delete it permanently, overwriting it with random bytes
|
move the file to the Recycle Bin (the Trash on macOS, a `deleted` folder in the library on Linux)
|
||||||
|
or to delete it permanently, overwriting it with random bytes
|
||||||
first; "Don't ask again" keeps the choice until the app restarts. Only the `.mcap` goes: the
|
first; "Don't ask again" keeps the choice until the app restarts. Only the `.mcap` goes: the
|
||||||
`.snagx` it was converted from is untouched.
|
`.snagx` it was converted from is untouched.
|
||||||
- **Copy:** click a thumbnail to select it (it is outlined, and shown in the Capture window), then
|
- **Copy:** click a thumbnail to select it (it is outlined, and shown in the Capture window), then
|
||||||
**Ctrl-C** copies its image, as the Capture window shows it (annotations and all; a video's
|
**Ctrl-C** copies its image, as the Capture window shows it (annotations and all; a video's
|
||||||
frame), and **Ctrl-Shift-C** its full path. Neither acts while you type in the search box. They
|
frame), and **Ctrl-Shift-C** its full path. Neither acts while you type in the search box. They
|
||||||
are `copy_image` and `copy_path` in `[hotkeys]` in `settings.config`. Copying images is Windows
|
are `copy_image` and `copy_path` in `[hotkeys]` in `settings.config` (**Cmd** for Ctrl on macOS).
|
||||||
only so far.
|
Copying images works on Windows and macOS, not yet on Linux.
|
||||||
- **Ctrl-F** puts the keyboard in the search box, its text selected (`focus_search` in `[hotkeys]`).
|
- **Ctrl-F** puts the keyboard in the search box, its text selected (`focus_search` in `[hotkeys]`).
|
||||||
- **Files added or deleted outside the app** (in Explorer, by another program) appear in the grid
|
- **Files added or deleted outside the app** (in Explorer, by another program) appear in the grid
|
||||||
or leave it on their own, folders added, renamed or deleted too: the library is watched.
|
or leave it on their own, folders added, renamed or deleted too: the library is watched.
|
||||||
@ -245,7 +246,7 @@ register the key, the Event Log says so; choose another, such as `Ctrl-Shift-S
|
|||||||
|
|
||||||
**Import** (above the grid) brings a folder of screenshots, photos and videos into the library:
|
**Import** (above the grid) brings a folder of screenshots, photos and videos into the library:
|
||||||
|
|
||||||
1. **Import from:** the folder, typed or chosen with **Browse...**. Everything under it is looked
|
1. **Import from:** the folder, typed or chosen with **Browse...** (Windows and macOS). Everything under it is looked
|
||||||
at, its folders too (not hidden ones, such as `.thumbnails`).
|
at, its folders too (not hidden ones, such as `.thumbnails`).
|
||||||
2. **Into library folder:** the folder in the library it goes into, such as `SnagitLibraryWork` or
|
2. **Into library folder:** the folder in the library it goes into, such as `SnagitLibraryWork` or
|
||||||
`Phone`. It starts as the name of the folder imported from, or the library folder that folder went
|
`Phone`. It starts as the name of the folder imported from, or the library folder that folder went
|
||||||
|
|||||||
@ -49,7 +49,7 @@ APP_NAME :: "mcap";
|
|||||||
APP_FOLDER_NAME :: "mcap";
|
APP_FOLDER_NAME :: "mcap";
|
||||||
GIT_BRANCH :: "check";
|
GIT_BRANCH :: "check";
|
||||||
GIT_HASH :: "-";
|
GIT_HASH :: "-";
|
||||||
VERSION :: "0.1.25";
|
VERSION :: "0.1.26";
|
||||||
BUILD_DATE :: "-";
|
BUILD_DATE :: "-";
|
||||||
DEBUG :: true;
|
DEBUG :: true;
|
||||||
COMPILE_TESTS :: true;
|
COMPILE_TESTS :: true;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ APP_NAME :: "mcap";
|
|||||||
APP_FOLDER_NAME :: "mcap";
|
APP_FOLDER_NAME :: "mcap";
|
||||||
APP_BUNDLE_IDENTIFIER :: "com.example.mcap";
|
APP_BUNDLE_IDENTIFIER :: "com.example.mcap";
|
||||||
|
|
||||||
VERSION :: "0.1.25";
|
VERSION :: "0.1.26";
|
||||||
// Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated
|
// Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated
|
||||||
// builds never write over the ones you run by hand.
|
// builds never write over the ones you run by hand.
|
||||||
build_directory := "bin";
|
build_directory := "bin";
|
||||||
|
|||||||
@ -1038,9 +1038,9 @@ import_window :: () { using ImGui;
|
|||||||
import_source_changed();
|
import_source_changed();
|
||||||
}
|
}
|
||||||
SameLine();
|
SameLine();
|
||||||
#if OS == .WINDOWS {
|
#if OS == .WINDOWS || OS == .MACOS {
|
||||||
if Button("Browse...") {
|
if Button("Browse...") {
|
||||||
chosen := windows_choose_folder("Choose a folder to import");
|
chosen := platform_choose_folder("Choose a folder to import");
|
||||||
if chosen {
|
if chosen {
|
||||||
count := min(chosen.count, importer.source_buffer.count - 1);
|
count := min(chosen.count, importer.source_buffer.count - 1);
|
||||||
memcpy(importer.source_buffer.data, chosen.data, count);
|
memcpy(importer.source_buffer.data, chosen.data, count);
|
||||||
|
|||||||
@ -183,7 +183,7 @@ open_file_list_folders :: () {
|
|||||||
array_reset_keeping_memory(*open_file.folders);
|
array_reset_keeping_memory(*open_file.folders);
|
||||||
visit_files(viewer_folder(), false, *open_file.folders, (info: *File_Visit_Info, folders: *[..] string) {
|
visit_files(viewer_folder(), false, *open_file.folders, (info: *File_Visit_Info, folders: *[..] string) {
|
||||||
if !info.is_directory || !import_name_is_valid(info.short_name) return; // not hidden ones (.thumbnails)
|
if !info.is_directory || !import_name_is_valid(info.short_name) return; // not hidden ones (.thumbnails)
|
||||||
#if OS != .WINDOWS if info.short_name == "deleted" return; // where deleted captures go there
|
#if OS == .LINUX if info.short_name == "deleted" return; // where deleted captures go there
|
||||||
array_add(folders, copy_string(info.short_name,, context.default_allocator));
|
array_add(folders, copy_string(info.short_name,, context.default_allocator));
|
||||||
}, visit_files = false, visit_directories = true);
|
}, visit_files = false, visit_directories = true);
|
||||||
quick_sort(open_file.folders, compare);
|
quick_sort(open_file.folders, compare);
|
||||||
@ -359,7 +359,7 @@ open_file_copy_path :: () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open_file_copy_image :: () {
|
open_file_copy_image :: () {
|
||||||
#if OS != .WINDOWS {
|
#if OS == .LINUX {
|
||||||
toast_show(.Error, "Copying images is not available on this system yet");
|
toast_show(.Error, "Copying images is not available on this system yet");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -237,7 +237,7 @@ screen_capture_out_path :: (name: string) -> path: string, error: string {
|
|||||||
return .{}, false, "not available on this system yet";
|
return .{}, false, "not available on this system yet";
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_copy_image_to_clipboard :: (pixels: [] u8, width: s32, height: s32) -> bool {
|
#if OS == .LINUX platform_copy_image_to_clipboard :: (pixels: [] u8, width: s32, height: s32) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -190,8 +190,7 @@ screen_record_leftover_date :: (path: string) -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
screen_record_folder :: () -> string {
|
screen_record_folder :: () -> string {
|
||||||
#if OS == .WINDOWS return tprint("%/%", windows_expand_environment("%TEMP%"), APP_FOLDER_NAME);
|
return tprint("%/%", platform_temporary_folder(), APP_FOLDER_NAME);
|
||||||
else return tprint("/tmp/%", APP_FOLDER_NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OS == .WINDOWS {
|
#if OS == .WINDOWS {
|
||||||
|
|||||||
@ -653,13 +653,13 @@ viewer_list_files :: () -> [..] Listed_File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Whether path, a file or folder, is where the library keeps captures: under it, and not hidden or
|
// Whether path, a file or folder, is where the library keeps captures: under it, and not hidden or
|
||||||
// in the deleted folder viewer_delete_capture moves files to off Windows.
|
// in the deleted folder viewer_delete_capture moves files to on Linux.
|
||||||
viewer_is_library_path :: (path: string) -> bool {
|
viewer_is_library_path :: (path: string) -> bool {
|
||||||
folder := viewer_folder();
|
folder := viewer_folder();
|
||||||
if path.count <= folder.count + 1 || !begins_with(path, folder) || path[folder.count] != #char "/" return false;
|
if path.count <= folder.count + 1 || !begins_with(path, folder) || path[folder.count] != #char "/" return false;
|
||||||
relative := advance(path, folder.count + 1);
|
relative := advance(path, folder.count + 1);
|
||||||
if begins_with(relative, ".") || contains(relative, "/.") return false;
|
if begins_with(relative, ".") || contains(relative, "/.") return false;
|
||||||
#if OS != .WINDOWS if relative == "deleted" || begins_with(relative, "deleted/") return false;
|
#if OS == .LINUX if relative == "deleted" || begins_with(relative, "deleted/") return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,13 +837,13 @@ viewer_mark_removed :: (index: s32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Viewer_Delete_Mode :: enum u8 {
|
Viewer_Delete_Mode :: enum u8 {
|
||||||
RECYCLE; // to the Recycle Bin, or the deleted folder elsewhere: can be undone
|
RECYCLE; // to the Recycle Bin, the Trash, or the deleted folder on Linux: can be undone
|
||||||
OVERWRITE; // overwritten with random bytes, renamed, then deleted for good
|
OVERWRITE; // overwritten with random bytes, renamed, then deleted for good
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes the capture's file, then takes it out of the grid. RECYCLE: to the Recycle Bin on Windows
|
// Deletes the capture's file, then takes it out of the grid. RECYCLE: to the Recycle Bin on Windows
|
||||||
// (Windows asks first when the drive has none and it would be deleted for good), elsewhere into a
|
// (Windows asks first when the drive has none and it would be deleted for good), the Trash on macOS,
|
||||||
// deleted folder beside it. OVERWRITE: viewer_overwrite_and_delete. False, with why, when the file
|
// and on Linux into a deleted folder in the library. OVERWRITE: viewer_overwrite_and_delete. False, with why, when the file
|
||||||
// is still there.
|
// is still there.
|
||||||
viewer_delete_capture :: (index: s32, mode: Viewer_Delete_Mode) -> ok: bool, error: string {
|
viewer_delete_capture :: (index: s32, mode: Viewer_Delete_Mode) -> ok: bool, error: string {
|
||||||
capture := *viewer.captures[index];
|
capture := *viewer.captures[index];
|
||||||
@ -860,6 +860,9 @@ viewer_delete_capture :: (index: s32, mode: Viewer_Delete_Mode) -> ok: bool, err
|
|||||||
result := SHFileOperationW(*operation);
|
result := SHFileOperationW(*operation);
|
||||||
if operation.fAnyOperationsAborted return false, "cancelled";
|
if operation.fAnyOperationsAborted return false, "cancelled";
|
||||||
if result != 0 return false, tprint("the shell could not delete it (error 0x%)", formatInt(result, base = 16));
|
if result != 0 return false, tprint("the shell could not delete it (error 0x%)", formatInt(result, base = 16));
|
||||||
|
} else #if OS == .MACOS {
|
||||||
|
ok, error := macos_move_to_trash(capture.path);
|
||||||
|
if !ok return false, tprint("it could not be moved to the Trash: %", error);
|
||||||
} else {
|
} else {
|
||||||
folder := tprint("%/deleted", viewer_folder());
|
folder := tprint("%/deleted", viewer_folder());
|
||||||
make_directory_if_it_does_not_exist(folder);
|
make_directory_if_it_does_not_exist(folder);
|
||||||
@ -921,6 +924,7 @@ viewer_delete_now :: (index: s32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if OS == .WINDOWS VIEWER_TRASH_NAME :: "Recycle Bin";
|
#if OS == .WINDOWS VIEWER_TRASH_NAME :: "Recycle Bin";
|
||||||
|
else #if OS == .MACOS VIEWER_TRASH_NAME :: "Trash";
|
||||||
else VIEWER_TRASH_NAME :: "deleted folder";
|
else VIEWER_TRASH_NAME :: "deleted folder";
|
||||||
|
|
||||||
// The confirmation for viewer.delete_index, as a modal popup.
|
// The confirmation for viewer.delete_index, as a modal popup.
|
||||||
@ -1274,9 +1278,7 @@ viewer_open_proc :: (thread: *Thread) -> s64 {
|
|||||||
exporting := viewer.open_folder.count > 0;
|
exporting := viewer.open_folder.count > 0;
|
||||||
folder := viewer.open_folder;
|
folder := viewer.open_folder;
|
||||||
if !exporting {
|
if !exporting {
|
||||||
#if OS == .WINDOWS base := windows_expand_environment("%TEMP%");
|
folder = tprint("%/%", platform_temporary_folder(), APP_FOLDER_NAME);
|
||||||
else base := "/tmp";
|
|
||||||
folder = tprint("%/%", base, APP_FOLDER_NAME);
|
|
||||||
}
|
}
|
||||||
make_directory_if_it_does_not_exist(folder, recursive = true);
|
make_directory_if_it_does_not_exist(folder, recursive = true);
|
||||||
if !is_directory(folder) { viewer.open_error = copy_string(tprint("% is not a folder that can be written to", folder)); return 0; }
|
if !is_directory(folder) { viewer.open_error = copy_string(tprint("% is not a folder that can be written to", folder)); return 0; }
|
||||||
@ -1399,10 +1401,10 @@ viewer_export_popup :: () { using ImGui;
|
|||||||
if custom {
|
if custom {
|
||||||
SetNextItemWidth(GetFontSize() * 30);
|
SetNextItemWidth(GetFontSize() * 30);
|
||||||
InputTextWithHint("##folder", "The folder to export to", viewer.export_buffer.data, viewer.export_buffer.count);
|
InputTextWithHint("##folder", "The folder to export to", viewer.export_buffer.data, viewer.export_buffer.count);
|
||||||
#if OS == .WINDOWS {
|
#if OS == .WINDOWS || OS == .MACOS {
|
||||||
SameLine();
|
SameLine();
|
||||||
if Button("Browse...") {
|
if Button("Browse...") {
|
||||||
chosen := windows_choose_folder("Choose a folder to export to");
|
chosen := platform_choose_folder("Choose a folder to export to");
|
||||||
if chosen viewer_export_set_buffer(chosen);
|
if chosen viewer_export_set_buffer(chosen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1464,7 +1466,7 @@ viewer_copy_image :: () {
|
|||||||
index := viewer_selected();
|
index := viewer_selected();
|
||||||
if index < 0 { toast_show(.Error, "Select a capture to copy its image"); return; }
|
if index < 0 { toast_show(.Error, "Select a capture to copy its image"); return; }
|
||||||
capture := *viewer.captures[index];
|
capture := *viewer.captures[index];
|
||||||
#if OS != .WINDOWS {
|
#if OS == .LINUX {
|
||||||
toast_show(.Error, "Copying images is not available on this system yet");
|
toast_show(.Error, "Copying images is not available on this system yet");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,13 @@ platform_get_path_config_data :: inline () -> string {
|
|||||||
return XDG.get_path(.CONFIG_HOME);
|
return XDG.get_path(.CONFIG_HOME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// $TMPDIR, else /tmp, without a separator at the end. Temp.
|
||||||
|
platform_temporary_folder :: () -> string {
|
||||||
|
folder := posix_getenv("TMPDIR");
|
||||||
|
if !folder return "/tmp";
|
||||||
|
return trim_right(copy_string(folder,, temp), "/");
|
||||||
|
}
|
||||||
|
|
||||||
// A tag such as ja-JP from the locale variables, or "". Temp.
|
// A tag such as ja-JP from the locale variables, or "". Temp.
|
||||||
platform_preferred_language :: () -> string {
|
platform_preferred_language :: () -> string {
|
||||||
for string.["LC_ALL", "LC_MESSAGES", "LANG"] {
|
for string.["LC_ALL", "LC_MESSAGES", "LANG"] {
|
||||||
|
|||||||
@ -70,6 +70,93 @@ platform_cjk_font_candidates :: () -> [] Cjk_Font {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- folders, the Trash and the clipboard
|
||||||
|
|
||||||
|
// The user's own temporary folder, NSTemporaryDirectory() (under /var/folders, cleared by macOS),
|
||||||
|
// not /tmp, which every user shares. No separator at the end. Temp.
|
||||||
|
platform_temporary_folder :: () -> string {
|
||||||
|
pool := objc_new(NSAutoreleasePool);
|
||||||
|
defer NSAutoreleasePool.drain(pool);
|
||||||
|
folder := copy_string(to_string(NSTemporaryDirectory()),, temp); // the NSString goes with the pool
|
||||||
|
return trim_right(folder, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asks for a folder with the system's open panel, over the app's window: its path (temp), or ""
|
||||||
|
// when none was chosen. Main thread; it runs modally until the panel is closed.
|
||||||
|
platform_choose_folder :: (title: string) -> string {
|
||||||
|
pool := objc_new(NSAutoreleasePool);
|
||||||
|
defer NSAutoreleasePool.drain(pool);
|
||||||
|
panel := NSOpenPanel.openPanel();
|
||||||
|
if !panel return "";
|
||||||
|
func_bool :: #type (*void, Selector, BOOL) -> void #c_call;
|
||||||
|
func_string :: #type (*void, Selector, *NSString) -> void #c_call;
|
||||||
|
(cast(func_bool) objc_msgSend)(panel, selector("setCanChooseDirectories:"), YES);
|
||||||
|
(cast(func_bool) objc_msgSend)(panel, selector("setCanChooseFiles:"), NO);
|
||||||
|
(cast(func_bool) objc_msgSend)(panel, selector("setAllowsMultipleSelection:"), NO);
|
||||||
|
(cast(func_bool) objc_msgSend)(panel, selector("setCanCreateDirectories:"), YES);
|
||||||
|
(cast(func_string) objc_msgSend)(panel, selector("setMessage:"), NSString.getTempString(title));
|
||||||
|
(cast(func_string) objc_msgSend)(panel, selector("setPrompt:"), NSString.getTempString("Choose"));
|
||||||
|
save_panel := cast(*NSSavePanel) panel;
|
||||||
|
if NSSavePanel.runModal(save_panel) != NSModalResponseOK return "";
|
||||||
|
url := NSSavePanel.URL(save_panel);
|
||||||
|
if !url return "";
|
||||||
|
return copy_string(to_string(NSURL.path(url)),, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves a file to the Trash, where Finder's Put Back returns it to where it was. Error is "" or why not.
|
||||||
|
macos_move_to_trash :: (path: string) -> ok: bool, error: string {
|
||||||
|
pool := objc_new(NSAutoreleasePool);
|
||||||
|
defer NSAutoreleasePool.drain(pool);
|
||||||
|
url := NSURL.fileURLWithPath(NSString.getTempString(path));
|
||||||
|
error: *NSError;
|
||||||
|
func :: #type (*void, Selector, *NSURL, **NSURL, **NSError) -> BOOL #c_call;
|
||||||
|
if (cast(func) objc_msgSend)(NSFileManager.defaultManager(), selector("trashItemAtURL:resultingItemURL:error:"), url, null, *error) == YES return true, "";
|
||||||
|
why := ifx error then copy_string(to_string(NSError.localizedDescription(error)),, temp) else "no reason given";
|
||||||
|
return false, why;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Puts RGBA pixels, top row first, on the clipboard as PNG and TIFF, in sRGB and opaque, as the
|
||||||
|
// Windows version makes them. Every app that pastes images takes one of the two.
|
||||||
|
platform_copy_image_to_clipboard :: (pixels: [] u8, width: s32, height: s32) -> bool {
|
||||||
|
if width <= 0 || height <= 0 || pixels.count < cast(s64) width * height * 4 return false;
|
||||||
|
pool := objc_new(NSAutoreleasePool);
|
||||||
|
defer NSAutoreleasePool.drain(pool);
|
||||||
|
|
||||||
|
// AppKit allocates the pixels (planes null) and frees them with the rep.
|
||||||
|
rep := NSBitmapImageRep.initWithBitmapDataPlanes(objc_alloc(NSBitmapImageRep), null, width, height, 8, 4, YES, NO, NSDeviceRGBColorSpace, cast(s64) width * 4, 32);
|
||||||
|
if !rep return false;
|
||||||
|
autorelease(rep);
|
||||||
|
out := NSBitmapImageRep.bitmapData(rep);
|
||||||
|
if !out return false;
|
||||||
|
memcpy(out, pixels.data, cast(s64) width * height * 4);
|
||||||
|
for 0..cast(s64) width * height - 1 out[it * 4 + 3] = 255;
|
||||||
|
|
||||||
|
// Tagged sRGB, as the pixels are, so a paste doesn't shift their colors on a wide-gamut screen.
|
||||||
|
func_object :: #type (*void, Selector) -> *NSObject #c_call;
|
||||||
|
func_retag :: #type (*void, Selector, *NSObject) -> *NSBitmapImageRep #c_call;
|
||||||
|
srgb := (cast(func_object) objc_msgSend)(objc_getClass("NSColorSpace"), selector("sRGBColorSpace"));
|
||||||
|
tagged := (cast(func_retag) objc_msgSend)(rep, selector("bitmapImageRepByRetaggingWithColorSpace:"), srgb);
|
||||||
|
if tagged rep = tagged;
|
||||||
|
|
||||||
|
NSBitmapImageFileTypePNG :: 4;
|
||||||
|
func_png :: #type (*void, Selector, NSUInteger, *NSObject) -> *NSData #c_call;
|
||||||
|
properties := (cast(func_object) objc_msgSend)(objc_getClass("NSDictionary"), selector("dictionary"));
|
||||||
|
png := (cast(func_png) objc_msgSend)(rep, selector("representationUsingType:properties:"), NSBitmapImageFileTypePNG, properties);
|
||||||
|
tiff := cast(*NSData) (cast(func_object) objc_msgSend)(rep, selector("TIFFRepresentation"));
|
||||||
|
if !png && !tiff return false;
|
||||||
|
|
||||||
|
pasteboard := NSPasteboard.generalPasteboard();
|
||||||
|
NSPasteboard.clearContents(pasteboard);
|
||||||
|
func_set :: #type (*void, Selector, *NSData, NSPasteboardType) -> BOOL #c_call;
|
||||||
|
set := false;
|
||||||
|
if png set = (cast(func_set) objc_msgSend)(pasteboard, selector("setData:forType:"), png, NSPasteboardTypePNG) == YES || set;
|
||||||
|
if tiff set = (cast(func_set) objc_msgSend)(pasteboard, selector("setData:forType:"), tiff, NSPasteboardTypeTIFF) == YES || set;
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSTemporaryDirectory :: () -> *NSString #foreign macos_foundation;
|
||||||
|
macos_foundation :: #library,system "Foundation"; // the Objective_C module keeps its own to itself
|
||||||
|
|
||||||
Mach :: #import "macOS"; // task_info
|
Mach :: #import "macOS"; // task_info
|
||||||
|
|
||||||
#import "Objective_C";
|
#import "Objective_C";
|
||||||
|
|||||||
@ -846,6 +846,13 @@ os_log_error :: () {
|
|||||||
error_code := GetLastError();
|
error_code := GetLastError();
|
||||||
log_error(" > GetLastError code: %, %", error_code, get_error_string(error_code));
|
log_error(" > GetLastError code: %, %", error_code, get_error_string(error_code));
|
||||||
}
|
}
|
||||||
|
// %TEMP%, without a separator at the end. Temp.
|
||||||
|
platform_temporary_folder :: () -> string {
|
||||||
|
return trim_right(windows_expand_environment("%TEMP%"), "/\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_choose_folder :: (title: string) -> string { return windows_choose_folder(title); }
|
||||||
|
|
||||||
// Asks for a folder with Explorer's own picker, over the app's window: its path (temp), or "" when
|
// Asks for a folder with Explorer's own picker, over the app's window: its path (temp), or "" when
|
||||||
// none was chosen.
|
// none was chosen.
|
||||||
windows_choose_folder :: (title: string) -> string {
|
windows_choose_folder :: (title: string) -> string {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user