Files
ladybird/UI/AppKit/Utilities/Conversions.mm
Andreas Kling 1802a05bc2 UI/AppKit: Show rich autocomplete suggestion rows
Render AutocompleteSuggestion rows with section headers, favicons,
titles, and secondary text in the child-window popup instead of just
plain strings.

Move the AppKit popup and inline completion onto the shared suggestion
model, and share the base64 PNG decoding helper with the application
menu icon loading path.
2026-04-16 21:01:28 +02:00

184 lines
5.4 KiB
Plaintext

/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Base64.h>
#include <LibGfx/ImageFormats/PNGWriter.h>
#import <Utilities/Conversions.h>
namespace Ladybird {
String ns_string_to_string(NSString* string)
{
auto const* utf8 = [string UTF8String];
return MUST(String::from_utf8({ utf8, strlen(utf8) }));
}
NSString* string_to_ns_string(StringView string)
{
return [[NSString alloc] initWithData:string_to_ns_data(string) encoding:NSUTF8StringEncoding];
}
Utf16String ns_string_to_utf16_string(NSString* string)
{
// FIXME: There isn't a great way to get the underlying UTF-16 data from an NSString. There is [NSString getCharacters],
// but that requires pre-allocated a separate buffer to hold the code units. We could add a macOS-only factory
// to UTF-16 string to handle that, but it's tricky with inline and ASCII storage.
auto const* utf8 = [string UTF8String];
return Utf16String::from_utf8({ utf8, strlen(utf8) });
}
NSString* utf16_string_to_ns_string(Utf16View const& string)
{
if (string.has_ascii_storage())
return string_to_ns_string(string.bytes());
return [[NSString alloc] initWithCharacters:reinterpret_cast<unichar const*>(string.utf16_span().data())
length:string.length_in_code_units()];
}
ByteString ns_string_to_byte_string(NSString* string)
{
auto const* utf8 = [string UTF8String];
return ByteString(utf8, strlen(utf8));
}
ByteString ns_data_to_string(NSData* data)
{
return { reinterpret_cast<char const*>([data bytes]), [data length] };
}
NSData* string_to_ns_data(StringView string)
{
return [NSData dataWithBytes:string.characters_without_null_termination() length:string.length()];
}
NSImage* image_from_base64_png(StringView string, NSSize size)
{
auto decoded = decode_base64(string);
if (decoded.is_error())
return nil;
auto* data = [NSData dataWithBytes:decoded.value().data()
length:decoded.value().size()];
auto* image = [[NSImage alloc] initWithData:data];
[image setSize:size];
return image;
}
NSDictionary* deserialize_json_to_dictionary(StringView json)
{
auto* ns_json = string_to_ns_string(json);
auto* json_data = [ns_json dataUsingEncoding:NSUTF8StringEncoding];
NSError* error = nil;
NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData:json_data
options:0
error:&error];
if (!dictionary) {
NSLog(@"Error deserializing DOM tree: %@", error);
}
return dictionary;
}
Gfx::IntRect ns_rect_to_gfx_rect(NSRect rect)
{
return {
static_cast<int>(rect.origin.x),
static_cast<int>(rect.origin.y),
static_cast<int>(rect.size.width),
static_cast<int>(rect.size.height),
};
}
NSRect gfx_rect_to_ns_rect(Gfx::IntRect rect)
{
return NSMakeRect(
static_cast<CGFloat>(rect.x()),
static_cast<CGFloat>(rect.y()),
static_cast<CGFloat>(rect.width()),
static_cast<CGFloat>(rect.height()));
}
Gfx::IntSize ns_size_to_gfx_size(NSSize size)
{
return {
static_cast<int>(size.width),
static_cast<int>(size.height),
};
}
NSSize gfx_size_to_ns_size(Gfx::IntSize size)
{
return NSMakeSize(
static_cast<CGFloat>(size.width()),
static_cast<CGFloat>(size.height()));
}
Gfx::IntPoint ns_point_to_gfx_point(NSPoint point)
{
return {
static_cast<int>(point.x),
static_cast<int>(point.y),
};
}
NSPoint gfx_point_to_ns_point(Gfx::IntPoint point)
{
return NSMakePoint(
static_cast<CGFloat>(point.x()),
static_cast<CGFloat>(point.y()));
}
Gfx::Color ns_color_to_gfx_color(NSColor* color)
{
auto rgb_color = [color colorUsingColorSpace:NSColorSpace.genericRGBColorSpace];
if (rgb_color != nil)
return {
static_cast<u8>([rgb_color redComponent] * 255),
static_cast<u8>([rgb_color greenComponent] * 255),
static_cast<u8>([rgb_color blueComponent] * 255),
static_cast<u8>([rgb_color alphaComponent] * 255)
};
return {};
}
NSColor* gfx_color_to_ns_color(Gfx::Color color)
{
return [NSColor colorWithRed:(color.red() / 255.f)
green:(color.green() / 255.f)
blue:(color.blue() / 255.f)
alpha:(color.alpha() / 255.f)];
}
Gfx::IntPoint compute_origin_relative_to_window(NSWindow* window, Gfx::IntPoint position)
{
// The origin of the NSWindow is its bottom-left corner, relative to the bottom-left of the screen. We must convert
// window positions sent to/from WebContent between this origin and the window's and screen's top-left corners.
auto screen_frame = Ladybird::ns_rect_to_gfx_rect([[window screen] frame]);
auto window_frame = Ladybird::ns_rect_to_gfx_rect([window frame]);
position.set_y(screen_frame.height() - window_frame.height() - position.y());
return position;
}
NSImage* gfx_bitmap_to_ns_image(Gfx::Bitmap const& bitmap)
{
auto png = Gfx::PNGWriter::encode(bitmap);
if (png.is_error())
return nullptr;
auto* data = [NSData dataWithBytes:png.value().data()
length:png.value().size()];
return [[NSImage alloc] initWithData:data];
}
}