From: David Elliott Date: Thu, 18 Oct 2007 07:54:43 +0000 (+0000) Subject: Halfway reasonable implementation of wxFont for wxCocoa. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/51146826fc0a0a949d88f23fb9d83fc1f1ada14e Halfway reasonable implementation of wxFont for wxCocoa. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@49213 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/cocoa/font.h b/include/wx/cocoa/font.h index a8bf9f2275..1b6698e89e 100644 --- a/include/wx/cocoa/font.h +++ b/include/wx/cocoa/font.h @@ -16,12 +16,30 @@ // wxFont // ---------------------------------------------------------------------------- +DECLARE_WXCOCOA_OBJC_CLASS(NSFont); + +// Internal class that bridges us with code like wxSystemSettings +class wxCocoaFontFactory; +// We have c-tors/methods taking pointers of these +class wxFontRefData; + +/*! @discussion + wxCocoa's implementation of wxFont is very incomplete. In particular, + a lot of work needs to be done on wxNativeFontInfo which is currently + using the totally generic implementation. + + See the documentation in src/cocoa/font.mm for more implementatoin details. + */ class WXDLLEXPORT wxFont : public wxFontBase { + friend class wxCocoaFontFactory; public: - // ctors and such + /*! @abstract Default construction of invalid font for 2-step construct then Create process. + */ wxFont() { } + /*! @abstract Platform-independent construction with individual properties + */ wxFont(int size, int family, int style, @@ -33,13 +51,20 @@ public: (void)Create(size, family, style, weight, underlined, face, encoding); } + /*! @abstract Construction with opaque wxNativeFontInfo + */ wxFont(const wxNativeFontInfo& info) { (void)Create(info); } + /*! @abstract Construction with platform-dependent font descriptor string. + @param fontDesc Usually the result of wxNativeFontInfo::ToUserString() + */ wxFont(const wxString& fontDesc); + // NOTE: Copy c-tor and assignment from wxObject is fine + bool Create(int size, int family, int style, @@ -73,11 +98,36 @@ public: // implementation only from now on // ------------------------------- + /*! @abstract Defined on some ports (not including this one) in wxGDIObject + @discussion + The intention here I suppose is to allow one to create a wxFont without yet + creating the underlying native object. There's no point not to create the + NSFont immediately in wxCocoa so this is useless. + This method came from the stub code copied in the early days of wxCocoa. + FIXME(1): Remove this in trunk. FIXME(2): Is it really a good idea for this to + be part of the public API for wxGDIObject? + */ virtual bool RealizeResource(); protected: + /*! @abstract Helper method for COW. + @discussion + wxFont can be considered a mutable holder of an immutable opaque implementation object. + All methods that mutate the font should first call Unshare() to ensure that mutating + the implementation object does not cause another wxFont that happened to share the + same ref data to mutate. + */ void Unshare(); + /*! @abstract Internal constructor with ref data + @discussion + Takes ownership of @a refData. That is, it is assumed that refData has either just been + created using new (which initializes its m_refCount to 1) or if you are sharing a ref that + you have called IncRef on it before passing it to this method. + */ + explicit wxFont(wxFontRefData *refData) + { Create(refData); } + bool Create(wxFontRefData *refData); private: DECLARE_DYNAMIC_CLASS(wxFont) }; diff --git a/src/cocoa/font.mm b/src/cocoa/font.mm index 26a0226aba..e18a4768b3 100644 --- a/src/cocoa/font.mm +++ b/src/cocoa/font.mm @@ -9,6 +9,68 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +/*! @file font.mm + @disucssion + Cocoa has three classes which interact to form the font system: + + NSFont: Represents a specific font (e.g. Times-Bold) at a specific size + with specific attributes. That is, it's basically like this class. + Notably, it doesn't hold an underlined flag, so this class does. + Available on all OS X versions. + + NSFontManager: Fairly broad controller class which ties together the + model (NSFont) with the view (NSFontPanel). We are ignoring NSFontPanel + in this discussion. NSFontManager is actually a little broader than just + a controller though. It's also the traditional way of mutating fonts + and asking for a font with a certain family and certain attributes. + For example, you can use NSFont's factor methods to create a font named + @"Times-Roman" then use NSFontManager to imbue attributes like italic and + bold. You might do this if, for instance, you already have times at the + Roman weight but want to make it bold. + + Alternatively, you can use NSFontManager to ask for a font in the @"Times" + family with the bold attribute. + + NSFontManager is available on all OS X versions. + + NSFontDescriptor: Added in OS X 10.3. Prior to this there was no specific + class to represent all of the attributes of a font. Instead, a regular + NSDictionary was used with a set of well-defined keys. Unfortunately, + there was no method accepting the attributes dictionary, only a method + to retrieve it from an NSFont. That meant that in order to create + a new font by imbueing certain attributes like Bold one would need + to use the specific method in NSFontManager to do so. + + The NSFontDescriptor class, on the other hand, has factory methods which + can create a new font descriptor with an attributes dictionary as well + as mutate (by copy) an existing font descriptor using attributes from + an attributes dictionary. + + In theory, most of what can be done using NSFontDescriptor can just as + well be done without it. NSFontDescriptor is basically just a shell + around an NSMutableDictionary with well-defined keys. + + + Getting back to the broad overview, font matching is one of the weaker points + in Cocoa's API and NSFontDescriptor is the easier to use solution. + + That said, it's not impossible to implement font matching without it. For instance, + if you have a family name and want to list available variants (e.g. Bold, italic, + underlined) then you can ask NSFontManager for availableMembersOfFontFamily:. + + The problem is that you can either match on family name or on font attributes + but not on both. To match both you have to do one then the other. + NSFontDescriptor allows you to get a list of fonts matching both a family name + and a particular set of attributes. Furthermore, the attributes instead of + being flags as in NSFontManager are instead well-defined keys in a dictionary. + + The only way to get that behavior without NSFontManager is to pare down the + list as much as possible using the classic NSFontManager methods and then + to instantiate each font in the list and match on each font's afmDictionary. + + A reasonable guess is that that's probably more or less exactly what + NSFontDescriptor does internally. + */ #include "wx/wxprec.h" #include "wx/font.h" @@ -21,29 +83,67 @@ #include "wx/fontutil.h" #include "wx/encinfo.h" +#include "wx/cocoa/string.h" +#include "wx/cocoa/private/fontfactory.h" +#include "wx/cocoa/autorelease.h" + +#include +#include + +// Helper methods for NSFont/wxNativeFontInfo +static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info); +static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont); +static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined = false); +static wxNativeFontInfo MakeNativeFontInfo(int size, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding); + +/*! @discussion + Due to 2.8 ABI compatibility concerns we probably don't want to change wxNativeFontInfo + although this may be unfounded because this class is supposed to be internal as is + wxNativeFontInfo so anyone who subclassed it or created one without going through + wxFont should expect what they get (i.e. horrible breakage) + There's a concern that wxFontRefData was in the public header when 2.8 shipped so + it's possible that someone did subclass it to get better font behavior. + + For right now, the plan is to write it strictly ABI compatible with 2.8 and eventually + to enhance it in trunk to accurately represent font attributes as Cocoa sees them. + + I'd like to let at least one 2.8 release go out the door and get feedback as to whether + this is going to be a problem or not. If so, we'll keep it strictly ABI compatible. + If not, we'll update it. + */ class WXDLLEXPORT wxFontRefData: public wxGDIRefData { friend class WXDLLIMPEXP_FWD_CORE wxFont; public: wxFontRefData() - : m_fontId(0) + : m_cocoaNSFont(nil) + , m_info(MakeNativeFontInfo(10, wxDEFAULT, wxNORMAL, wxNORMAL, FALSE, + wxT("Geneva"), wxFONTENCODING_DEFAULT)) { - Init(10, wxDEFAULT, wxNORMAL, wxNORMAL, FALSE, - wxT("Geneva"), wxFONTENCODING_DEFAULT); + CreateNSFontAndUpdateInfo(); } wxFontRefData(const wxFontRefData& data) : wxGDIRefData() - , m_fontId(data.m_fontId) + , m_cocoaNSFont([data.m_cocoaNSFont retain]) , m_info(data.m_info) { } + wxFontRefData(NSFont *cocoaNSFont, bool underlined) + : wxGDIRefData() + , m_cocoaNSFont([cocoaNSFont retain]) + , m_info(MakeNativeFontInfoForNSFont(m_cocoaNSFont, underlined)) + { + } + wxFontRefData(const wxNativeFontInfo& info) : wxGDIRefData() - , m_fontId(0) + , m_cocoaNSFont(nil) , m_info(info) - {} + { + CreateNSFontAndUpdateInfo(); + } wxFontRefData(int size, int family, @@ -52,14 +152,19 @@ public: bool underlined, const wxString& faceName, wxFontEncoding encoding) - : m_fontId(0) + : m_cocoaNSFont(nil) + , m_info(MakeNativeFontInfo(size, family, style, weight, underlined, faceName, encoding)) { - Init(size, family, style, weight, underlined, faceName, encoding); + CreateNSFontAndUpdateInfo(); } virtual ~wxFontRefData(); protected: - // common part of all ctors + /*! @abstract common part of some ctors + @discussion + This is a leftover of sorts from the old stub code. + FIXME: Remove from trunk + */ void Init(int size, int family, int style, @@ -68,17 +173,119 @@ protected: const wxString& faceName, wxFontEncoding encoding); + /*! @discussion + Uses the native font info to create an NSFont and then updates that info with + the attributes of the font. This is necessary because, for example, a font + can be created with an empty faceName in which case some concrete typeface must + be chosen. + We choose to handle this by first initializing the wxNativeFontInfo with the + properties as given by the user and then creating the NSFont and updating + the wxNativeFontInfo to match the NSFont. + */ + void CreateNSFontAndUpdateInfo() + { + wxAutoNSAutoreleasePool pool; + [m_cocoaNSFont release]; + m_cocoaNSFont = [GetNSFontForNativeFontInfo(m_info) retain]; + UpdateNativeFontInfoWithNSFont(m_info, m_cocoaNSFont); + } + // font characterstics - int m_fontId; + NSFont *m_cocoaNSFont; wxNativeFontInfo m_info; public: }; +NSString *GetFamilyName(wxFontFamily family) +{ + switch(family) + { + case wxFONTFAMILY_DEFAULT: + default: + return @"Times"; + case wxFONTFAMILY_DECORATIVE: + case wxFONTFAMILY_ROMAN: + case wxFONTFAMILY_SCRIPT: + return @"Times"; + case wxFONTFAMILY_SWISS: + return @"Lucida Grande"; + case wxFONTFAMILY_MODERN: + case wxFONTFAMILY_TELETYPE: + return @"Monaco"; + }; +} +// Returns an NSFont given the native font info +// NOTE: It is not considered alloc'd (i.e. this is a get method not an alloc/new method) +static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info) +{ + if(!info.faceName.empty()) + { + NSFont *font = [NSFont fontWithName:wxNSStringWithWxString(info.faceName) size:info.pointSize]; + // TODO: use NSFontManager to mix in the weights and whatnot + if(font != nil) + return font; + // To err or not to err? + } + // No font with that face name or no face name + + NSFontTraitMask cocoaTraits = 0; + int cocoaWeight = 5; + NSFont *font = [[NSFontManager sharedFontManager] fontWithFamily:GetFamilyName(info.family) traits:cocoaTraits weight:cocoaWeight size:info.pointSize]; + return font; +} + +/*! @discussion + Updates all fields of @a info except for underlined which is not a property of NSFont. + */ +static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont) +{ + info.pointSize = [cocoaNSFont pointSize]; + + // FIXME: We could maybe improve on this? + info.family = wxFONTFAMILY_DEFAULT; + + // FIXME: italicAngle might indicate a slanted rather than truly italic font? + info.style = [cocoaNSFont italicAngle] == 0.0?wxFONTSTYLE_NORMAL:wxFONTSTYLE_ITALIC; + + int cocoaWeight = [[NSFontManager sharedFontManager] weightOfFont:cocoaNSFont]; + if(cocoaWeight < 5) + info.weight = wxFONTWEIGHT_LIGHT; + else if(cocoaWeight < 9) + info.weight = wxFONTWEIGHT_NORMAL; + else + info.weight = wxFONTWEIGHT_BOLD; + + // FIXME: Is this right? I think so. + info.faceName = wxStringWithNSString([cocoaNSFont fontName]); + + // TODO: Translate NSStringEncoding to wxFontEncoding + info.encoding = wxFONTENCODING_SYSTEM; +} + +/*! @discussion + Creates a new generic wxNativeFontInfo from an NSFont and an underlined flag. + Uses UpdateNativeFontInfoWithNSFont to do the work and sets the underlined field + of wxNativeFontInfo to the @a underlined argument. + */ +static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined) +{ + wxNativeFontInfo info; + UpdateNativeFontInfoWithNSFont(info, cocoaNSFont); + + // NSFont are never underlined.. that's a function of the drawing system + info.underlined = underlined; + + return info; +} + +//#include "_font_test_2_8_abi_compat.h" + IMPLEMENT_DYNAMIC_CLASS(wxFont, wxGDIObject) -void wxFontRefData::Init(int size, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding) +static wxNativeFontInfo MakeNativeFontInfo(int size, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding) { + wxNativeFontInfo m_info; // NOTE: not an i-var despite name m_info.pointSize = size; m_info.family = static_cast(family); m_info.style = static_cast(style); @@ -86,15 +293,36 @@ void wxFontRefData::Init(int size, int family, int style, int weight, bool under m_info.underlined = underlined; m_info.faceName = faceName; m_info.encoding = encoding; + return m_info; +} + +void wxFontRefData::Init(int size, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding) +{ + m_info = MakeNativeFontInfo(size, family, style, weight, underlined, faceName, encoding); } wxFontRefData::~wxFontRefData() { + [m_cocoaNSFont release]; + m_cocoaNSFont = nil; // TODO: delete font data } #define M_FONTDATA ((wxFontRefData*)m_refData) +wxFont wxCocoaFontFactory::InstanceForNSFont(WX_NSFont cocoaFont, bool underlined) +{ + return wxFont(new wxFontRefData(cocoaFont, underlined)); +} + +bool wxFont::Create(wxFontRefData *refData) +{ + UnRef(); + m_refData = refData; + + return m_refData != NULL; +} + bool wxFont::Create(const wxNativeFontInfo& nativeFontInfo) { UnRef(); diff --git a/src/cocoa/settings.mm b/src/cocoa/settings.mm index 0b87a457af..0f6ffb05f0 100644 --- a/src/cocoa/settings.mm +++ b/src/cocoa/settings.mm @@ -19,8 +19,10 @@ #endif #include "wx/cocoa/autorelease.h" +#include "wx/cocoa/private/fontfactory.h" #import +#import // ---------------------------------------------------------------------------- // wxSystemSettingsNative @@ -106,8 +108,10 @@ wxColour wxSystemSettingsNative::GetColour(wxSystemColour index) wxFont wxSystemSettingsNative::GetFont(wxSystemFont index) { -// return a nonworking font object, crash from wxInitializeStockObjects - return wxFont(); + // Return the system font for now + { wxAutoNSAutoreleasePool pool; + return wxCocoaFontFactory::InstanceForNSFont([NSFont systemFontOfSize:0.0], false); + } switch (index) { case wxSYS_ANSI_VAR_FONT :