diff options
-rw-r--r-- | include/cru/common/Logger.hpp | 16 | ||||
-rw-r--r-- | include/cru/osx/graphics/quartz/Painter.hpp | 20 | ||||
-rw-r--r-- | include/cru/osx/graphics/quartz/TextLayout.hpp | 2 | ||||
-rw-r--r-- | include/cru/platform/Color.hpp | 5 | ||||
-rw-r--r-- | src/common/Logger.cpp | 29 | ||||
-rw-r--r-- | src/osx/graphics/quartz/Painter.cpp | 49 | ||||
-rw-r--r-- | src/osx/gui/UiApplication.mm | 4 | ||||
-rw-r--r-- | src/osx/gui/Window.mm | 92 |
8 files changed, 183 insertions, 34 deletions
diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp index 3dabeb91..7d43fc5a 100644 --- a/include/cru/common/Logger.hpp +++ b/include/cru/common/Logger.hpp @@ -94,4 +94,20 @@ void TagError(StringView tag, TArgs&&... args) { Logger::GetInstance()->Log(LogLevel::Error, tag, Format(std::forward<TArgs>(args)...)); } + +class StdioLogSource : public Object, public virtual ILogSource { + public: + explicit StdioLogSource(bool use_lock = false); + + CRU_DELETE_COPY(StdioLogSource) + CRU_DELETE_MOVE(StdioLogSource) + + ~StdioLogSource() override; + + public: + void Write(LogLevel level, StringView s) override; + + private: + bool use_lock_; +}; } // namespace cru::log diff --git a/include/cru/osx/graphics/quartz/Painter.hpp b/include/cru/osx/graphics/quartz/Painter.hpp index 8d693d8b..ea64c3d5 100644 --- a/include/cru/osx/graphics/quartz/Painter.hpp +++ b/include/cru/osx/graphics/quartz/Painter.hpp @@ -6,17 +6,19 @@ #include <CoreGraphics/CoreGraphics.h> +#include <functional> + namespace cru::platform::graphics::osx::quartz { class QuartzCGContextPainter : public OsxQuartzResource, public virtual IPainter { + CRU_DEFINE_CLASS_LOG_TAG( + u"cru::platform::graphics::osx::quartz::QuartzCGContextPainter") + public: - explicit QuartzCGContextPainter(IGraphicsFactory* graphics_factory, - CGContextRef cg_context, bool auto_release, - const Size& size) - : OsxQuartzResource(graphics_factory), - cg_context_(cg_context), - auto_release_(auto_release), - size_(size) {} + explicit QuartzCGContextPainter( + IGraphicsFactory* graphics_factory, CGContextRef cg_context, + bool auto_release, const Size& size, + std::function<void(QuartzCGContextPainter*)> on_end_draw); CRU_DELETE_COPY(QuartzCGContextPainter) CRU_DELETE_MOVE(QuartzCGContextPainter) @@ -48,6 +50,8 @@ class QuartzCGContextPainter : public OsxQuartzResource, void EndDraw() override; private: + void DoEndDraw(); + void Validate(); private: @@ -56,5 +60,7 @@ class QuartzCGContextPainter : public OsxQuartzResource, bool auto_release_; Size size_; + + std::function<void(QuartzCGContextPainter*)> on_end_draw_; }; } // namespace cru::platform::graphics::osx::quartz diff --git a/include/cru/osx/graphics/quartz/TextLayout.hpp b/include/cru/osx/graphics/quartz/TextLayout.hpp index 4d23a76e..dd170142 100644 --- a/include/cru/osx/graphics/quartz/TextLayout.hpp +++ b/include/cru/osx/graphics/quartz/TextLayout.hpp @@ -33,6 +33,8 @@ class OsxCTTextLayout : public OsxQuartzResource, public virtual ITextLayout { Point TextSinglePoint(Index position, bool trailing) override; TextHitTestResult HitTest(const Point& point) override; + CTFrameRef GetCTFrameRef() const { return ct_frame_; } + private: void RecreateFrame(); diff --git a/include/cru/platform/Color.hpp b/include/cru/platform/Color.hpp index 3a72aa65..f60ab692 100644 --- a/include/cru/platform/Color.hpp +++ b/include/cru/platform/Color.hpp @@ -257,4 +257,9 @@ extern const std::unordered_map<StringView, Color> predefined_name_color_map; } // namespace details std::optional<Color> GetPredefinedColorByName(StringView name); + +inline String ToString(const Color& color) { + return cru::Format(u"rgba({}, {}, {}, {})", color.red, color.green, + color.blue, color.alpha); +} } // namespace cru::platform diff --git a/src/common/Logger.cpp b/src/common/Logger.cpp index 6ba5d578..3034c0ad 100644 --- a/src/common/Logger.cpp +++ b/src/common/Logger.cpp @@ -3,7 +3,9 @@ #include <array> #include <cstdlib> #include <ctime> +#include <iostream> #include <memory> +#include <mutex> #include <string_view> namespace cru::log { @@ -76,4 +78,31 @@ void Logger::Log(LogLevel level, StringView tag, StringView message) { LogLevelToString(level), tag, message)); } } + +namespace { +std::mutex stdio_lock; + +void WriteStdio(LogLevel level, StringView s) { + std::string m = s.ToString().ToUtf8(); + + if (level == LogLevel::Error) { + std::cerr << m; + } else { + std::cout << m; + } +} +} // namespace + +StdioLogSource::StdioLogSource(bool use_lock) : use_lock_(use_lock) {} + +StdioLogSource::~StdioLogSource() {} + +void StdioLogSource::Write(LogLevel level, StringView s) { + if (use_lock_) { + std::lock_guard<std::mutex> guard(stdio_lock); + WriteStdio(level, s); + } else { + WriteStdio(level, s); + } +} } // namespace cru::log diff --git a/src/osx/graphics/quartz/Painter.cpp b/src/osx/graphics/quartz/Painter.cpp index 041184c6..c22f80fc 100644 --- a/src/osx/graphics/quartz/Painter.cpp +++ b/src/osx/graphics/quartz/Painter.cpp @@ -1,14 +1,32 @@ #include "cru/osx/graphics/quartz/Painter.hpp" +#include "cru/common/Logger.hpp" #include "cru/osx/graphics/quartz/Brush.hpp" #include "cru/osx/graphics/quartz/Convert.hpp" #include "cru/osx/graphics/quartz/Geometry.hpp" +#include "cru/osx/graphics/quartz/TextLayout.hpp" #include "cru/platform/Check.hpp" #include "cru/platform/Exception.hpp" +#include "cru/platform/graphics/util/Painter.hpp" namespace cru::platform::graphics::osx::quartz { +QuartzCGContextPainter::QuartzCGContextPainter( + IGraphicsFactory* graphics_factory, CGContextRef cg_context, + bool auto_release, const Size& size, + std::function<void(QuartzCGContextPainter*)> on_end_draw) + : OsxQuartzResource(graphics_factory), + cg_context_(cg_context), + auto_release_(auto_release), + size_(size), + on_end_draw_(std::move(on_end_draw)) { + Expects(cg_context); + log::TagDebug(log_tag, + u"Created with CGContext: {}, Auto Release: {}, Size: {}.", + cg_context, auto_release, size_); +} + QuartzCGContextPainter::~QuartzCGContextPainter() { - EndDraw(); + DoEndDraw(); if (auto_release_) { CGContextRelease(cg_context_); cg_context_ = nullptr; @@ -29,13 +47,12 @@ void QuartzCGContextPainter::SetTransform(const Matrix& matrix) { void QuartzCGContextPainter::Clear(const Color& color) { Validate(); - CGColorRef c = - CGColorCreateGenericRGB(color.GetFloatRed(), color.GetFloatGreen(), - color.GetFloatBlue(), color.GetFloatAlpha()); - CGContextSetFillColorWithColor(cg_context_, c); - CGColorRelease(c); - + CGContextSetRGBFillColor(cg_context_, color.GetFloatRed(), + color.GetFloatGreen(), color.GetFloatBlue(), + color.GetFloatAlpha()); CGContextFillRect(cg_context_, Convert(Rect{Point{}, size_})); + + log::TagDebug(log_tag, u"Clear with color {}, size {}.", color, size_); } void QuartzCGContextPainter::DrawLine(const Point& start, const Point& end, @@ -98,7 +115,15 @@ void QuartzCGContextPainter::FillGeometry(IGeometry* geometry, IBrush* brush) { void QuartzCGContextPainter::DrawText(const Point& offset, ITextLayout* text_layout, IBrush* brush) { - // TODO: Implement this. + Validate(); + + auto tl = CheckPlatform<OsxCTTextLayout>(text_layout, GetPlatformId()); + auto b = CheckPlatform<QuartzBrush>(brush, GetPlatformId()); + + util::WithTransform(this, Matrix::Translation(offset), + [this, tl, b](IPainter*) { + CTFrameDraw(tl->GetCTFrameRef(), cg_context_); + }); } void QuartzCGContextPainter::PushLayer(const Rect& bounds) { @@ -109,10 +134,16 @@ void QuartzCGContextPainter::PopLayer() { // TODO: Implement this. } -void QuartzCGContextPainter::EndDraw() { +void QuartzCGContextPainter::EndDraw() { DoEndDraw(); } + +void QuartzCGContextPainter::DoEndDraw() { if (cg_context_) { + on_end_draw_(this); + CGContextFlush(cg_context_); CGContextSynchronize(cg_context_); + + log::TagDebug(log_tag, u"End draw and flush."); } } diff --git a/src/osx/gui/UiApplication.mm b/src/osx/gui/UiApplication.mm index 4ac3d1df..11763af6 100644 --- a/src/osx/gui/UiApplication.mm +++ b/src/osx/gui/UiApplication.mm @@ -1,5 +1,6 @@ #include "cru/osx/gui/UiApplication.hpp" +#include "cru/common/Logger.hpp" #include "cru/osx/graphics/quartz/Factory.hpp" #include "cru/osx/gui/Cursor.hpp" #include "cru/osx/gui/Window.hpp" @@ -66,6 +67,9 @@ void OsxUiApplicationPrivate::CallQuitHandlers() { OsxUiApplication::OsxUiApplication() : OsxGuiResource(this), p_(new details::OsxUiApplicationPrivate(this)) { + // Add stdio logger. + log::Logger::GetInstance()->AddSource(std::make_unique<log::StdioLogSource>()); + [NSApp setDelegate:p_->app_delegate_]; p_->quartz_graphics_factory_ = std::make_unique<graphics::osx::quartz::QuartzGraphicsFactory>(); p_->cursor_manager_ = std::make_unique<OsxCursorManager>(this); diff --git a/src/osx/gui/Window.mm b/src/osx/gui/Window.mm index ce28e8a2..d1d6ae8c 100644 --- a/src/osx/gui/Window.mm +++ b/src/osx/gui/Window.mm @@ -1,6 +1,7 @@ #include "cru/osx/gui/Window.hpp" #include "CursorPrivate.h" +#include "cru/common/Logger.hpp" #include "cru/common/Range.hpp" #include "cru/osx/Convert.hpp" #include "cru/osx/graphics/quartz/Convert.hpp" @@ -14,6 +15,7 @@ #include "cru/platform/gui/Cursor.hpp" #include "cru/platform/gui/InputMethod.hpp" #include "cru/platform/gui/Keyboard.hpp" +#include "cru/platform/gui/TimerHelper.hpp" #include "cru/platform/gui/Window.hpp" #include <AppKit/NSGraphicsContext.h> @@ -22,16 +24,17 @@ #include <Foundation/NSAttributedString.h> #include <Foundation/NSString.h> +#include <gsl/gsl_assert> #include <limits> #include <memory> using cru::platform::osx::Convert; -@interface WindowDelegate : NSObject <NSWindowDelegate> +@interface CruWindowDelegate : NSObject <NSWindowDelegate> - (id)init:(cru::platform::gui::osx::details::OsxWindowPrivate*)p; @end -@interface Window : NSWindow +@interface CruWindow : NSWindow - (instancetype)init:(cru::platform::gui::osx::details::OsxWindowPrivate*)p contentRect:(NSRect)contentRect style:(NSWindowStyleMask)style; @@ -48,7 +51,12 @@ using cru::platform::osx::Convert; - (void)keyUp:(NSEvent*)event; @end -@interface InputClient : NSObject <NSTextInputClient> +@interface CruView : NSView +- (instancetype)init:(cru::platform::gui::osx::details::OsxWindowPrivate*)p + frame:(cru::platform::Rect)frame; +@end + +@interface CruInputClient : NSObject <NSTextInputClient> - (id)init:(cru::platform::gui::osx::details::OsxInputMethodContextPrivate*)p; @end @@ -89,6 +97,8 @@ class OsxWindowPrivate { void OnWindowDidUpdate(); void OnWindowDidResize(); + CGLayerRef GetDrawLayer() { return draw_layer_; } + private: void UpdateCursor(); @@ -101,32 +111,38 @@ class OsxWindowPrivate { Rect content_rect_; NSWindow* window_; - WindowDelegate* window_delegate_; - NSGraphicsContext* graphics_context_; + CruWindowDelegate* window_delegate_; + + CGLayerRef draw_layer_; bool mouse_in_ = false; std::shared_ptr<OsxCursor> cursor_ = nullptr; std::unique_ptr<OsxInputMethodContext> input_method_context_; + + TimerAutoCanceler draw_timer_; }; void OsxWindowPrivate::OnWindowWillClose() { osx_window_->destroy_event_.Raise(nullptr); window_ = nil; - graphics_context_ = nil; + CGLayerRelease(draw_layer_); } void OsxWindowPrivate::OnWindowDidExpose() { osx_window_->RequestRepaint(); } void OsxWindowPrivate::OnWindowDidUpdate() {} void OsxWindowPrivate::OnWindowDidResize() { - osx_window_->resize_event_.Raise(osx_window_->GetClientSize()); - NSRect rect = [NSWindow contentRectForFrameRect:[window_ frame] styleMask:CalcWindowStyleMask(frame_)]; - content_rect_ = cru::platform::graphics::osx::quartz::Convert(rect); + osx_window_->resize_event_.Raise(osx_window_->GetClientSize()); + + CGLayerRelease(draw_layer_); + draw_layer_ = CGLayerCreateWithContext(nullptr, rect.size, nullptr); + Ensures(draw_layer_); + osx_window_->RequestRepaint(); } @@ -175,7 +191,7 @@ void OsxWindowPrivate::UpdateCursor() { OsxWindow::OsxWindow(OsxUiApplication* ui_application, INativeWindow* parent, bool frame) : OsxGuiResource(ui_application), p_(new details::OsxWindowPrivate(this)) { - p_->window_delegate_ = [[WindowDelegate alloc] init:p_.get()]; + p_->window_delegate_ = [[CruWindowDelegate alloc] init:p_.get()]; p_->parent_ = parent; @@ -251,14 +267,23 @@ void OsxWindow::SetWindowRect(const Rect& rect) { } void OsxWindow::RequestRepaint() { - GetUiApplication()->SetImmediate([this] { paint_event_.Raise(nullptr); }); + if (!p_->draw_timer_) { + p_->draw_timer_ = GetUiApplication()->SetImmediate([this] { + this->paint_event_.Raise(nullptr); + this->p_->draw_timer_.Release(); + }); + } } std::unique_ptr<graphics::IPainter> OsxWindow::BeginPaint() { - CGContextRef cg_context = [p_->graphics_context_ CGContext]; + CGContextRef cg_context = CGLayerGetContext(p_->draw_layer_); return std::make_unique<cru::platform::graphics::osx::quartz::QuartzCGContextPainter>( - GetUiApplication()->GetGraphicsFactory(), cg_context, false, GetClientSize()); + GetUiApplication()->GetGraphicsFactory(), cg_context, false, GetClientSize(), + [this](graphics::osx::quartz::QuartzCGContextPainter*) { + log::Debug(u"Finish painting and invalidate view."); + [[p_->window_ contentView] setNeedsDisplay:YES]; + }); } void OsxWindow::CreateWindow() { @@ -267,11 +292,20 @@ void OsxWindow::CreateWindow() { NSWindowStyleMask style_mask = CalcWindowStyleMask(p_->frame_); - p_->window_ = [[Window alloc] init:p_.get() contentRect:content_rect style:style_mask]; + p_->window_ = [[CruWindow alloc] init:p_.get() contentRect:content_rect style:style_mask]; [p_->window_ setDelegate:p_->window_delegate_]; - p_->graphics_context_ = [NSGraphicsContext graphicsContextWithWindow:p_->window_]; + NSView* content_view = [[CruView alloc] init:p_.get() + frame:Rect(Point{}, p_->content_rect_.GetSize())]; + + [p_->window_ setContentView:content_view]; + + p_->draw_layer_ = CGLayerCreateWithContext( + nullptr, cru::platform::graphics::osx::quartz::Convert(p_->content_rect_.GetSize()), nullptr); + Ensures(p_->draw_layer_); + + RequestRepaint(); } Point OsxWindow::GetMousePosition() { @@ -405,7 +439,7 @@ IEvent<std::nullptr_t>* OsxInputMethodContext::CompositionEvent() { IEvent<StringView>* OsxInputMethodContext::TextEvent() { return &p_->text_event_; } } -@implementation Window { +@implementation CruWindow { cru::platform::gui::osx::details::OsxWindowPrivate* _p; } @@ -524,7 +558,29 @@ IEvent<StringView>* OsxInputMethodContext::TextEvent() { return &p_->text_event_ } @end -@implementation WindowDelegate { +@implementation CruView { + cru::platform::gui::osx::details::OsxWindowPrivate* _p; +} + +- (instancetype)init:(cru::platform::gui::osx::details::OsxWindowPrivate*)p + frame:(cru::platform::Rect)frame { + [super initWithFrame:cru::platform::graphics::osx::quartz::Convert(frame)]; + _p = p; + + return self; +} + +- (void)drawRect:(NSRect)dirtyRect { + cru::log::TagDebug(u"CruView", u"Begin to draw layer in view."); + auto cg_context = [[NSGraphicsContext currentContext] CGContext]; + auto layer = _p->GetDrawLayer(); + Ensures(layer); + CGContextDrawLayerAtPoint(cg_context, CGPointMake(0, 0), layer); +} + +@end + +@implementation CruWindowDelegate { cru::platform::gui::osx::details::OsxWindowPrivate* _p; } @@ -550,7 +606,7 @@ IEvent<StringView>* OsxInputMethodContext::TextEvent() { return &p_->text_event_ } @end -@implementation InputClient { +@implementation CruInputClient { cru::platform::gui::osx::details::OsxInputMethodContextPrivate* _p; NSMutableAttributedString* _text; } |