X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/05725592b93f7c16b08c6b09bfa508883012482a..404b319a85dadd7decf7a5a5331020520031a41c:/src/cocoa/font.mm diff --git a/src/cocoa/font.mm b/src/cocoa/font.mm index 26a0226aba..abb9ddd09e 100644 --- a/src/cocoa/font.mm +++ b/src/cocoa/font.mm @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: src/cocoa/font.cpp +// Name: src/cocoa/font.mm // Purpose: wxFont class // Author: AUTHOR // Modified by: @@ -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 behaviour 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,64 +83,207 @@ #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, wxFontFamily family, wxFontStyle style, wxFontWeight 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 behaviour. + + 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, - int style, - int weight, + wxFontFamily family, + wxFontStyle style, + wxFontWeight weight, 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, - int weight, + wxFontFamily family, + wxFontStyle style, + wxFontWeight weight, bool underlined, 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: }; -IMPLEMENT_DYNAMIC_CLASS(wxFont, wxGDIObject) +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; -void wxFontRefData::Init(int size, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding) + return info; +} + +//#include "_font_test_2_8_abi_compat.h" + +static wxNativeFontInfo MakeNativeFontInfo(int size, wxFontFamily family, wxFontStyle style, wxFontWeight 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,23 +291,54 @@ 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, wxFontFamily family, wxFontStyle style, wxFontWeight 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(); m_refData = new wxFontRefData(nativeFontInfo); - + return true; } +wxGDIRefData *wxFont::CreateGDIRefData() const +{ + return new wxFontRefData; +} + +wxGDIRefData *wxFont::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxFontRefData(*static_cast(data)); +} + void wxFont::SetEncoding(wxFontEncoding) { } @@ -114,7 +350,7 @@ wxFontEncoding wxFont::GetEncoding() const int wxFont::GetPointSize() const { - wxCHECK_MSG( Ok(), 0, wxT("invalid font") ); + wxCHECK_MSG( IsOk(), 0, wxT("invalid font") ); return M_FONTDATA->m_info.pointSize; } @@ -126,31 +362,30 @@ bool wxFont::GetUnderlined() const return false; } -int wxFont::GetStyle() const +wxFontStyle wxFont::GetStyle() const { - wxCHECK_MSG( Ok(), 0, wxT("invalid font") ); + wxCHECK_MSG( IsOk(), 0, wxT("invalid font") ); return M_FONTDATA->m_info.style; } -int wxFont::GetFamily() const +wxFontFamily wxFont::DoGetFamily() const { - wxCHECK_MSG( Ok(), 0, wxT("invalid font") ); return M_FONTDATA->m_info.family; } -int wxFont::GetWeight() const +wxFontWeight wxFont::GetWeight() const { - wxCHECK_MSG( Ok(), 0, wxT("invalid font") ); + wxCHECK_MSG( IsOk(), 0, wxT("invalid font") ); return M_FONTDATA->m_info.weight; } const wxNativeFontInfo *wxFont::GetNativeFontInfo() const { - wxCHECK_MSG( Ok(), 0, wxT("invalid font") ); + wxCHECK_MSG( IsOk(), 0, wxT("invalid font") ); return &M_FONTDATA->m_info; } -bool wxFont::Create(int pointSize, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding) +bool wxFont::Create(int pointSize, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding) { UnRef(); m_refData = new wxFontRefData(pointSize, family, style, weight, underlined, faceName, encoding); @@ -170,51 +405,36 @@ bool wxFont::RealizeResource() return false; } -void wxFont::Unshare() -{ - // Don't change shared data - if (!m_refData) - { - m_refData = new wxFontRefData(); - } - else - { - wxFontRefData* ref = new wxFontRefData(*(wxFontRefData*)m_refData); - UnRef(); - m_refData = ref; - } -} - void wxFont::SetPointSize(int pointSize) { - Unshare(); + AllocExclusive(); M_FONTDATA->m_info.pointSize = pointSize; RealizeResource(); } -void wxFont::SetFamily(int family) +void wxFont::SetFamily(wxFontFamily family) { - Unshare(); + AllocExclusive(); M_FONTDATA->m_info.family = static_cast(family); RealizeResource(); } -void wxFont::SetStyle(int style) +void wxFont::SetStyle(wxFontStyle style) { - Unshare(); + AllocExclusive(); M_FONTDATA->m_info.style = static_cast(style); RealizeResource(); } -void wxFont::SetWeight(int weight) +void wxFont::SetWeight(wxFontWeight weight) { - Unshare(); + AllocExclusive(); M_FONTDATA->m_info.weight = static_cast(weight); @@ -223,7 +443,7 @@ void wxFont::SetWeight(int weight) bool wxFont::SetFaceName(const wxString& faceName) { - Unshare(); + AllocExclusive(); M_FONTDATA->m_info.faceName = faceName; @@ -234,7 +454,7 @@ bool wxFont::SetFaceName(const wxString& faceName) void wxFont::SetUnderlined(bool underlined) { - Unshare(); + AllocExclusive(); M_FONTDATA->m_info.underlined = underlined;