Ladybird/AppKit: Port TaskManager window to Swift

This is just a direct port of the Objective-C++ code to Swift 6.
A future patch should probably update it to actually use SwiftUI.
This commit is contained in:
Andrew Kaster 2024-07-16 05:33:39 -06:00 committed by Andrew Kaster
parent 4066ce2c7e
commit 68ce5f8290
Notes: github-actions[bot] 2024-07-21 21:56:43 +00:00
14 changed files with 313 additions and 15 deletions

View file

@ -50,7 +50,7 @@ runs:
shell: bash shell: bash
run: | run: |
set -e set -e
sudo xcode-select --switch /Applications/Xcode_15.4.app sudo xcode-select --switch /Applications/Xcode_16.0.app
brew update brew update
brew install autoconf autoconf-archive automake coreutils bash ffmpeg ninja wabt ccache unzip qt llvm@18 nasm brew install autoconf autoconf-archive automake coreutils bash ffmpeg ninja wabt ccache unzip qt llvm@18 nasm

View file

@ -138,6 +138,16 @@ jobs:
working-directory: ${{ github.workspace }}/Build working-directory: ${{ github.workspace }}/Build
run: cmake --build . run: cmake --build .
- name: Enable the AppKit chrome with Swift files
if: ${{ inputs.os_name == 'macOS' && inputs.fuzzer == 'NO_FUZZ' }}
working-directory: ${{ github.workspace }}
run: cmake -B Build -DENABLE_QT=OFF -DENABLE_SWIFT=ON
- name: Build the AppKit chrome with Swift files
if: ${{ inputs.os_name == 'macOS' && inputs.fuzzer == 'NO_FUZZ' }}
working-directory: ${{ github.workspace }}/Build
run: cmake --build .
- name: Save Caches - name: Save Caches
uses: ./.github/actions/cache-save uses: ./.github/actions/cache-save
with: with:

View file

@ -36,15 +36,20 @@ include(lagom_install_options)
if (ENABLE_ADDRESS_SANITIZER) if (ENABLE_ADDRESS_SANITIZER)
add_cxx_compile_options(-fsanitize=address -fno-omit-frame-pointer) add_cxx_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_cxx_link_options(-fsanitize=address) add_cxx_link_options(-fsanitize=address)
add_swift_compile_options(-sanitize=address)
add_swift_link_options(-sanitize=address)
endif() endif()
if (ENABLE_MEMORY_SANITIZER) if (ENABLE_MEMORY_SANITIZER)
add_cxx_compile_options(-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer) add_cxx_compile_options(-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer)
add_cxx_link_options(-fsanitize=memory -fsanitize-memory-track-origins) add_cxx_link_options(-fsanitize=memory -fsanitize-memory-track-origins)
add_swift_compile_options(-sanitize=memory)
add_swift_link_options(-sanitize=memory)
endif() endif()
if (ENABLE_UNDEFINED_SANITIZER) if (ENABLE_UNDEFINED_SANITIZER)
add_cxx_compile_options(-fsanitize=undefined -fno-omit-frame-pointer) add_cxx_compile_options(-fsanitize=undefined -fno-omit-frame-pointer)
add_swift_compile_options(-sanitize=address)
if (UNDEFINED_BEHAVIOR_IS_FATAL) if (UNDEFINED_BEHAVIOR_IS_FATAL)
add_cxx_compile_options(-fno-sanitize-recover=undefined) add_cxx_compile_options(-fno-sanitize-recover=undefined)
endif() endif()
@ -52,6 +57,7 @@ if (ENABLE_UNDEFINED_SANITIZER)
add_cxx_compile_options(-fno-sanitize=function) add_cxx_compile_options(-fno-sanitize=function)
endif() endif()
add_cxx_link_options(-fsanitize=undefined) add_cxx_link_options(-fsanitize=undefined)
add_swift_link_options(-sanitize=address)
endif() endif()
if (HAIKU) if (HAIKU)

View file

@ -11,7 +11,16 @@
#import <UI/LadybirdWebView.h> #import <UI/LadybirdWebView.h>
#import <UI/Tab.h> #import <UI/Tab.h>
#import <UI/TabController.h> #import <UI/TabController.h>
#import <UI/TaskManagerController.h>
#if defined(LADYBIRD_USE_SWIFT)
// FIXME: Report this codegen error to Apple
# define StyleMask NSWindowStyleMask
# import <Ladybird-Swift.h>
# undef StyleMask
#else
# import <UI/TaskManagerController.h>
#endif
#import <Utilities/Conversions.h> #import <Utilities/Conversions.h>
#if !__has_feature(objc_arc) #if !__has_feature(objc_arc)
@ -237,7 +246,7 @@
return; return;
} }
self.task_manager_controller = [[TaskManagerController alloc] init:self]; self.task_manager_controller = [[TaskManagerController alloc] initWithDelegate:self];
[self.task_manager_controller showWindow:nil]; [self.task_manager_controller showWindow:nil];
} }

View file

@ -1,6 +1,5 @@
add_executable(ladybird MACOSX_BUNDLE add_library(ladybird_impl STATIC
${LADYBIRD_SOURCES} ${LADYBIRD_SOURCES}
main.mm
Application/Application.mm Application/Application.mm
Application/ApplicationBridge.cpp Application/ApplicationBridge.cpp
Application/ApplicationDelegate.mm Application/ApplicationDelegate.mm
@ -14,14 +13,52 @@ add_executable(ladybird MACOSX_BUNDLE
UI/SearchPanel.mm UI/SearchPanel.mm
UI/Tab.mm UI/Tab.mm
UI/TabController.mm UI/TabController.mm
UI/TaskManager.mm
UI/TaskManagerController.mm
Utilities/Conversions.mm Utilities/Conversions.mm
) )
target_include_directories(ladybird PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(ladybird_impl PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(ladybird PRIVATE "-framework Cocoa -framework UniformTypeIdentifiers" LibUnicode)
target_compile_options(ladybird PRIVATE target_compile_options(ladybird_impl PRIVATE "SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -std=c++23 -cxx-interoperability-mode=default>")
-fobjc-arc target_compile_options(ladybird_impl PUBLIC
-Wno-deprecated-anon-enum-enum-conversion # Required for CGImageCreate $<$<COMPILE_LANGUAGE:CXX>:-fobjc-arc>
$<$<COMPILE_LANGUAGE:CXX>:-Wno-deprecated-anon-enum-enum-conversion> # Required for CGImageCreate
) )
target_compile_features(ladybird_impl PUBLIC cxx_std_23)
if (ENABLE_SWIFT)
enable_language(Swift)
if (CMAKE_Swift_COMPILER_VERSION VERSION_LESS 6.0)
message(FATAL_ERROR
"Swift 6.0 or newer is required to parse AK C++ headers in C++23 mode"
)
endif()
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
message(FATAL_ERROR
"Swift files must use Clang that was bundled with swiftc"
)
endif()
include(../cmake/GenerateSwiftHeader.cmake)
target_sources(ladybird_impl PRIVATE
UI/TaskManager.swift
UI/TaskManagerController.swift
)
target_compile_definitions(ladybird_impl PUBLIC LADYBIRD_USE_SWIFT)
set_target_properties(ladybird_impl PROPERTIES Swift_MODULE_NAME "SwiftLadybird")
get_target_property(LADYBIRD_NATIVE_DIRS ladybird_impl INCLUDE_DIRECTORIES)
_swift_generate_cxx_header(ladybird_impl "Ladybird-Swift.h"
SEARCH_PATHS ${LADYBIRD_NATIVE_DIRS}
)
else()
target_sources(ladybird_impl PRIVATE
UI/TaskManager.mm
UI/TaskManagerController.mm
)
endif()
add_executable(ladybird MACOSX_BUNDLE
main.mm
)
target_link_libraries(ladybird PRIVATE "-framework Cocoa -framework UniformTypeIdentifiers" LibUnicode ladybird_impl)
create_ladybird_bundle(ladybird) create_ladybird_bundle(ladybird)

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
import Foundation
import Ladybird.WebView
import Ladybird.WebViewApplication
import SwiftUI
public class TaskManager: NSWindow {
private let WINDOW_WIDTH: CGFloat = 600
private let WINDOW_HEIGHT: CGFloat = 400
var web_view: LadybirdWebView
private var timer: Timer?
init() {
let tab_rect = NSApplication.shared.keyWindow!.frame
let position_x = tab_rect.origin.x + (tab_rect.size.width - WINDOW_WIDTH) / 2
let position_y = tab_rect.origin.y + (tab_rect.size.height - WINDOW_HEIGHT) / 2
let window_rect = NSMakeRect(position_x, position_y, WINDOW_WIDTH, WINDOW_HEIGHT)
let style_mask = NSWindow.StyleMask.init(arrayLiteral: [
NSWindow.StyleMask.titled, NSWindow.StyleMask.closable, NSWindow.StyleMask.miniaturizable,
NSWindow.StyleMask.resizable,
])
self.web_view = LadybirdWebView.init(nil)
super.init(
contentRect: window_rect, styleMask: style_mask, backing: NSWindow.BackingStoreType.buffered,
defer: false)
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in
if let strong_self = self {
strong_self.updateStatistics()
}
}
self.web_view.postsBoundsChangedNotifications = true
let scroll_view = NSScrollView()
scroll_view.hasVerticalScroller = true
scroll_view.hasHorizontalScroller = true
scroll_view.lineScroll = 24
scroll_view.contentView = self.web_view
scroll_view.documentView = NSView()
self.contentView = scroll_view
self.title = "Task Manager"
self.setIsVisible(true)
self.updateStatistics()
}
func updateStatistics() {
WebView.Application.the().update_process_statistics();
self.web_view.loadHTML(WebView.Application.the().generate_process_statistics_html().__bytes_as_string_viewUnsafe());
}
}

View file

@ -16,6 +16,6 @@
@interface TaskManagerController : NSWindowController @interface TaskManagerController : NSWindowController
- (instancetype)init:(id<TaskManagerDelegate>)delegate; - (instancetype)initWithDelegate:(id<TaskManagerDelegate>)delegate;
@end @end

View file

@ -20,7 +20,7 @@
@implementation TaskManagerController @implementation TaskManagerController
- (instancetype)init:(id<TaskManagerDelegate>)delegate - (instancetype)initWithDelegate:(id<TaskManagerDelegate>)delegate
{ {
if (self = [super init]) { if (self = [super init]) {
self.delegate = delegate; self.delegate = delegate;

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
import Foundation
import SwiftUI
@objc
public protocol TaskManagerDelegate where Self: NSObject {
func onTaskManagerClosed()
}
public class TaskManagerController: NSWindowController, NSWindowDelegate {
private weak var delegate: TaskManagerDelegate?
@objc
public convenience init(delegate: TaskManagerDelegate) {
self.init()
self.delegate = delegate
}
@IBAction public override func showWindow(_ sender: Any?) {
self.window = TaskManager.init()
self.window!.delegate = self
self.window!.makeKeyAndOrderFront(sender)
}
public func windowWillClose(_ sender: Notification) {
self.delegate?.onTaskManagerClosed()
}
public func windowDidResize(_ sender: Notification) {
guard self.window != nil else { return }
if !self.window!.inLiveResize {
self.taskManager().web_view.handleResize()
}
}
public func windowDidChangeBackingProperties(_ sender: Notification) {
self.taskManager().web_view.handleDevicePixelRatioChange()
}
private func taskManager() -> TaskManager {
return self.window as! TaskManager
}
}

View file

@ -0,0 +1,14 @@
module Ladybird [system] {
requires cplusplus
requires objc_arc
explicit module WebView {
header "UI/LadybirdWebView.h"
export *
}
explicit module WebViewApplication {
header "../../Userland/Libraries/LibWebView/Application.h"
export *
}
}

View file

@ -0,0 +1,85 @@
# This source file is part of the Swift open source project
#
# Copyright (c) 2023 Apple Inc. and the Swift project authors.
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# Generate the bridging header from Swift to C++
#
# target: the name of the target to generate headers for.
# This target must build swift source files.
# header: the name of the header file to generate.
#
# NOTE: This logic will eventually be unstreamed into CMake.
function(_swift_generate_cxx_header target header)
if(NOT TARGET ${target})
message(FATAL_ERROR "Target ${target} not defined.")
endif()
if(NOT DEFINED CMAKE_Swift_COMPILER)
message(WARNING "Swift not enabled in project. Cannot generate headers for Swift files.")
return()
endif()
cmake_parse_arguments(ARG "" "" "SEARCH_PATHS;MODULE_NAME;CXX_STD_VERSION" ${ARGN})
if(NOT ARG_MODULE_NAME)
set(target_module_name $<TARGET_PROPERTY:${target},Swift_MODULE_NAME>)
set(ARG_MODULE_NAME $<IF:$<BOOL:${target_module_name}>,${target_module_name},${target}>)
endif()
if(ARG_SEARCH_PATHS)
list(TRANSFORM ARG_SEARCH_PATHS PREPEND "-I")
endif()
if(APPLE)
set(SDK_FLAGS "-sdk" "${CMAKE_OSX_SYSROOT}")
elseif(WIN32)
set(SDK_FLAGS "-sdk" "$ENV{SDKROOT}")
elseif(DEFINED ${CMAKE_SYSROOT})
set(SDK_FLAGS "-sdk" "${CMAKE_SYSROOT}")
endif()
cmake_path(APPEND CMAKE_CURRENT_BINARY_DIR include
OUTPUT_VARIABLE base_path)
cmake_path(APPEND base_path ${header}
OUTPUT_VARIABLE header_path)
if (NOT ARG_CXX_STD_VERSION)
set(ARG_CXX_STD_VERSION "14")
get_target_property(CxxFeatures ${target} COMPILE_FEATURES)
list(FILTER CxxFeatures INCLUDE REGEX "cxx_std_[0-9]+$")
if (NOT "${CxxFeatures}" STREQUAL "")
string(SUBSTRING ${CxxFeatures} 8 2 ARG_CXX_STD_VERSION)
endif()
endif()
set(CXX_STD_FLAGS -Xcc -std=c++${ARG_CXX_STD_VERSION})
set(_AllSources $<TARGET_PROPERTY:${target},SOURCES>)
set(_SwiftSources $<FILTER:${_AllSources},INCLUDE,\\.swift$>)
add_custom_command(OUTPUT ${header_path}
DEPENDS ${_SwiftSources}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND
${CMAKE_Swift_COMPILER} -frontend -typecheck
${ARG_SEARCH_PATHS}
${_SwiftSources}
${SDK_FLAGS}
${CXX_STD_FLAGS}
-module-name "${ARG_MODULE_NAME}"
-cxx-interoperability-mode=default
-emit-clang-header-path ${header_path}
COMMENT
"Generating '${header_path}'"
COMMAND_EXPAND_LISTS)
# Added to public interface for dependees to find.
target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${base_path}>)
# Added to the target to ensure target rebuilds if header changes and is used
# by sources in the target.
target_sources(${target} PRIVATE ${header_path})
endfunction()

View file

@ -21,6 +21,22 @@ macro(add_cxx_link_options)
add_link_options($<$<LINK_LANGUAGE:C,CXX>:${args}>) add_link_options($<$<LINK_LANGUAGE:C,CXX>:${args}>)
endmacro() endmacro()
macro(add_swift_compile_options)
set(args "")
foreach(arg ${ARGN})
string(APPEND args ${arg}$<SEMICOLON>)
endforeach()
add_compile_options($<$<COMPILE_LANGUAGE:Swift>:${args}>)
endmacro()
macro(add_swift_link_options)
set(args "")
foreach(arg ${ARGN})
string(APPEND args ${arg}$<SEMICOLON>)
endforeach()
add_link_options($<$<LINK_LANGUAGE:Swift>:${args}>)
endmacro()
if (MSVC) if (MSVC)
add_cxx_compile_options(/W4) add_cxx_compile_options(/W4)
# do not warn about unused function # do not warn about unused function

View file

@ -28,3 +28,4 @@ serenity_option(ENABLE_CLANG_PLUGINS OFF CACHE BOOL "Enable building with the Cl
serenity_option(ENABLE_CLANG_PLUGINS_INVALID_FUNCTION_MEMBERS OFF CACHE BOOL "Enable detecting invalid function types as members of GC-allocated objects") serenity_option(ENABLE_CLANG_PLUGINS_INVALID_FUNCTION_MEMBERS OFF CACHE BOOL "Enable detecting invalid function types as members of GC-allocated objects")
serenity_option(ENABLE_GUI_TARGETS ON CACHE BOOL "Enable building GUI targets") serenity_option(ENABLE_GUI_TARGETS ON CACHE BOOL "Enable building GUI targets")
serenity_option(ENABLE_SWIFT OFF CACHE BOOL "Enable building Swift files")

View file

@ -10,6 +10,12 @@
#include <LibWebView/Process.h> #include <LibWebView/Process.h>
#include <LibWebView/ProcessManager.h> #include <LibWebView/ProcessManager.h>
#ifdef __swift__
# include <swift/bridging>
#else
# define SWIFT_IMMORTAL_REFERENCE
#endif
namespace WebView { namespace WebView {
class Application { class Application {
@ -46,6 +52,6 @@ private:
Core::EventLoop m_event_loop; Core::EventLoop m_event_loop;
ProcessManager m_process_manager; ProcessManager m_process_manager;
bool m_in_shutdown { false }; bool m_in_shutdown { false };
}; } SWIFT_IMMORTAL_REFERENCE;
} }