duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cocoa_progress_callback.mm (6951B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "cocoa_progress_callback.h"
      5 
      6 #include "common/cocoa_tools.h"
      7 #include "common/log.h"
      8 
      9 Log_SetChannel(CocoaProgressCallback);
     10 
     11 CocoaProgressCallback::CocoaProgressCallback() : ProgressCallback()
     12 {
     13   Create();
     14 }
     15 
     16 CocoaProgressCallback::~CocoaProgressCallback()
     17 {
     18   Destroy();
     19 }
     20 
     21 void CocoaProgressCallback::PushState()
     22 {
     23   ProgressCallback::PushState();
     24 }
     25 
     26 void CocoaProgressCallback::PopState()
     27 {
     28   ProgressCallback::PopState();
     29   UpdateProgress();
     30 }
     31 
     32 void CocoaProgressCallback::SetCancellable(bool cancellable)
     33 {
     34   ProgressCallback::SetCancellable(cancellable);
     35 }
     36 
     37 void CocoaProgressCallback::SetTitle(const std::string_view title)
     38 {
     39   @autoreleasepool {
     40     dispatch_async(dispatch_get_main_queue(), [this, title = [CocoaTools::StringViewToNSString(title) retain]]() {
     41       [m_window setTitle:title];
     42       [title release];
     43     });
     44   }
     45 }
     46 
     47 void CocoaProgressCallback::SetStatusText(const std::string_view text)
     48 {
     49   ProgressCallback::SetStatusText(text);
     50   @autoreleasepool {
     51     dispatch_async(dispatch_get_main_queue(), [this, text = [CocoaTools::StringViewToNSString(text) retain]]() {
     52       [m_status setStringValue:text];
     53       [text release];
     54     });
     55   }
     56 }
     57 
     58 void CocoaProgressCallback::SetProgressRange(u32 range)
     59 {
     60   ProgressCallback::SetProgressRange(range);
     61   UpdateProgress();
     62 }
     63 
     64 void CocoaProgressCallback::SetProgressValue(u32 value)
     65 {
     66   ProgressCallback::SetProgressValue(value);
     67   UpdateProgress();
     68 }
     69 
     70 bool CocoaProgressCallback::Create()
     71 {
     72   @autoreleasepool
     73   {
     74     const NSRect window_rect =
     75       NSMakeRect(0.0f, 0.0f, static_cast<float>(WINDOW_WIDTH), static_cast<float>(WINDOW_HEIGHT));
     76     constexpr NSWindowStyleMask style = NSWindowStyleMaskTitled;
     77     m_window = [[NSWindow alloc] initWithContentRect:window_rect
     78                                            styleMask:style
     79                                              backing:NSBackingStoreBuffered
     80                                                defer:NO];
     81 
     82     NSView* m_view;
     83     m_view = [[NSView alloc] init];
     84     [m_window setContentView:m_view];
     85 
     86     int x = WINDOW_MARGIN;
     87     int y = WINDOW_HEIGHT - WINDOW_MARGIN;
     88 
     89     y -= 16 + SUBWINDOW_PADDING;
     90     m_status = [NSTextField labelWithString:@"Initializing..."];
     91     [m_status setFrame:NSMakeRect(x, y, SUBWINDOW_WIDTH, 16)];
     92     [m_view addSubview:m_status];
     93 
     94     y -= 16 + SUBWINDOW_PADDING;
     95     m_progress = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(x, y, SUBWINDOW_WIDTH, 16)];
     96     [m_progress setMinValue:0];
     97     [m_progress setMaxValue:100];
     98     [m_progress setDoubleValue:0];
     99     [m_progress setIndeterminate:NO];
    100     [m_view addSubview:m_progress];
    101 
    102     y -= 170 + SUBWINDOW_PADDING;
    103     m_text_scroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(x, y, SUBWINDOW_WIDTH, 170)];
    104     [m_text_scroll setBorderType:NSBezelBorder];
    105     [m_text_scroll setHasVerticalScroller:YES];
    106     [m_text_scroll setHasHorizontalScroller:NO];
    107 
    108     const NSSize content_size = [m_text_scroll contentSize];
    109     m_text = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, content_size.width, content_size.height)];
    110     [m_text setMinSize:NSMakeSize(0, content_size.height)];
    111     [m_text setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
    112     [m_text setVerticallyResizable:YES];
    113     [m_text setHorizontallyResizable:NO];
    114     [m_text setAutoresizingMask:NSViewWidthSizable];
    115     [m_text setUsesAdaptiveColorMappingForDarkAppearance:YES];
    116     [[m_text textContainer] setContainerSize:NSMakeSize(content_size.width, FLT_MAX)];
    117     [[m_text textContainer] setWidthTracksTextView:YES];
    118     [m_text_scroll setDocumentView:m_text];
    119     [m_view addSubview:m_text_scroll];
    120 
    121     [m_window center];
    122     [m_window setIsVisible:TRUE];
    123     [m_window makeKeyAndOrderFront:nil];
    124     [m_window setReleasedWhenClosed:NO];
    125   }
    126 
    127   return true;
    128 }
    129 
    130 void CocoaProgressCallback::Destroy()
    131 {
    132   if (m_window == nil)
    133     return;
    134 
    135   [m_window close];
    136 
    137   m_text = nil;
    138   m_progress = nil;
    139   m_status = nil;
    140 
    141   [m_view release];
    142   m_view = nil;
    143 
    144   [m_window release];
    145   m_window = nil;
    146 }
    147 
    148 void CocoaProgressCallback::UpdateProgress()
    149 {
    150   const float percent = (static_cast<float>(m_progress_value) / static_cast<float>(m_progress_range)) * 100.0f;
    151   dispatch_async(dispatch_get_main_queue(), [this, percent]() {
    152     [m_progress setDoubleValue:percent];
    153   });
    154 }
    155 
    156 void CocoaProgressCallback::DisplayError(const std::string_view message)
    157 {
    158   ERROR_LOG(message);
    159   AppendMessage(message);
    160 }
    161 
    162 void CocoaProgressCallback::DisplayWarning(const std::string_view message)
    163 {
    164   WARNING_LOG(message);
    165   AppendMessage(message);
    166 }
    167 
    168 void CocoaProgressCallback::DisplayInformation(const std::string_view message)
    169 {
    170   INFO_LOG(message);
    171   AppendMessage(message);
    172 }
    173 
    174 void CocoaProgressCallback::AppendMessage(const std::string_view message)
    175 {
    176   @autoreleasepool
    177   {
    178     NSString* nsmessage = [[CocoaTools::StringViewToNSString(message) stringByAppendingString:@"\n"] retain];
    179     dispatch_async(dispatch_get_main_queue(), [this, nsmessage]() {
    180       @autoreleasepool
    181       {
    182         NSAttributedString* attr = [[[NSAttributedString alloc] initWithString:nsmessage] autorelease];
    183         [[m_text textStorage] appendAttributedString:attr];
    184         [m_text scrollRangeToVisible:NSMakeRange([[m_text string] length], 0)];
    185         [nsmessage release];
    186       }
    187     });
    188   }
    189 }
    190 
    191 void CocoaProgressCallback::DisplayDebugMessage(const std::string_view message)
    192 {
    193   DEV_LOG(message);
    194 }
    195 
    196 void CocoaProgressCallback::ModalError(const std::string_view message)
    197 {
    198   if (![NSThread isMainThread])
    199   {
    200     dispatch_sync(dispatch_get_main_queue(), [this, message]() { ModalError(message); });
    201     return;
    202   }
    203 
    204   @autoreleasepool
    205   {
    206     NSAlert* alert = [[[NSAlert alloc] init] autorelease];
    207     [alert setMessageText:CocoaTools::StringViewToNSString(message)];
    208     [alert setAlertStyle:NSAlertStyleCritical];
    209     [alert runModal];
    210   }
    211 }
    212 
    213 bool CocoaProgressCallback::ModalConfirmation(const std::string_view message)
    214 {
    215   if (![NSThread isMainThread])
    216   {
    217     bool result;
    218     dispatch_sync(dispatch_get_main_queue(), [this, message, &result]() { result = ModalConfirmation(message); });
    219     return result;
    220   }
    221 
    222   bool result;
    223   @autoreleasepool
    224   {
    225     NSAlert* alert = [[[NSAlert alloc] init] autorelease];
    226     [alert setMessageText:CocoaTools::StringViewToNSString(message)];
    227     [alert addButtonWithTitle:@"Yes"];
    228     [alert addButtonWithTitle:@"No"];
    229     result = ([alert runModal] == NSAlertFirstButtonReturn);
    230   }
    231 
    232   return result;
    233 }
    234 
    235 void CocoaProgressCallback::ModalInformation(const std::string_view message)
    236 {
    237   if (![NSThread isMainThread])
    238   {
    239     dispatch_sync(dispatch_get_main_queue(), [this, message]() { ModalInformation(message); });
    240     return;
    241   }
    242 
    243   @autoreleasepool
    244   {
    245     NSAlert* alert = [[[NSAlert alloc] init] autorelease];
    246     [alert setMessageText:CocoaTools::StringViewToNSString(message)];
    247     [alert runModal];
    248   }
    249 }