[dasher: 12/43] CDasherScreen::MakeLabel can wrap to screen width given font size



commit dfbeee8989a33d4a4cbd4e757cf5ba687c695dc2
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Fri Jun 10 11:34:26 2011 +0100

    CDasherScreen::MakeLabel can wrap to screen width given font size
    
    MakeLabel(string,int) - size optional, if so, label should be wrapped at that
     font size (and subclasses are only required  to support use at that use).
     If no size specified, then text should not be wrapped, but may be rendered
     at multiple sizes (e.g. suitable for DasherNode labels)

 Src/Common/OpenGLScreen.h      |   16 ++++++++++++----
 Src/Common/OpenGLScreen.mm     |   14 +++++++-------
 Src/DasherCore/DasherScreen.h  |   21 +++++++++++++++++----
 Src/Gtk2/Canvas.cpp            |    6 +++---
 Src/Gtk2/Canvas.h              |    6 +++---
 Src/MacOSX/DasherViewOpenGL.mm |   14 +++++++++-----
 Src/Win32/Widgets/Screen.cpp   |   24 ++++++++++++++++++------
 Src/Win32/Widgets/Screen.h     |    8 +++++---
 Src/iPhone/Classes/EAGLView.h  |    4 ++--
 Src/iPhone/Classes/EAGLView.mm |   14 ++++++++++----
 10 files changed, 86 insertions(+), 41 deletions(-)
---
diff --git a/Src/Common/OpenGLScreen.h b/Src/Common/OpenGLScreen.h
index 895da5d..ecb753e 100755
--- a/Src/Common/OpenGLScreen.h
+++ b/Src/Common/OpenGLScreen.h
@@ -23,7 +23,7 @@ namespace Dasher {
       NSString *str;
       GLuint texture;
       GLfloat texcoords[8];
-      AlphabetLetter(OpenGLScreen *pScreen, const std::string &strText);
+      AlphabetLetter(OpenGLScreen *pScreen, const std::string &strText, unsigned int iWrapSize);
       ~AlphabetLetter();
       void PrepareTexture();
     };    
@@ -40,15 +40,23 @@ namespace Dasher {
     void DrawCircle(screenint iCX, screenint iCY, screenint iR, int iFillColour, int iLineColour, int iLineWidth);
     
     bool MultiSizeFonts() {return true;}
-    AlphabetLetter *MakeLabel(const std::string &strText);
+    AlphabetLetter *MakeLabel(const std::string &strText,unsigned int iWrapSize=0);
     std::pair<screenint,screenint> TextSize(CDasherScreen::Label *label, unsigned int iFontSize);
     void DrawString(CDasherScreen::Label *label, screenint x, screenint y, unsigned int iFontSize, int iColour);
     //leave virtual: SendMarker, Display
   protected:
     void resize(screenint iWidth, screenint iHeight, GLshort backingWidth, GLshort backingHeight, GLfloat tc_x, GLfloat tc_y);
     void RegenerateLabels();
-    virtual void RenderStringOntoCGContext(NSString *string, CGContextRef context)=0;
-    virtual CGSize TextSize(NSString *str, unsigned int iFontSize)=0;
+    ///Render a string onto a CoreGraphics context, using the context's current colour etc.
+    /// \param iFontSize if 0, render on a single line, in 36pt font; any other value,
+    /// render in that size, but constrained to the screen width, wrapping across multiple
+    /// lines if necessary
+    virtual void RenderStringOntoCGContext(NSString *string, CGContextRef context, unsigned int iFontSize)=0;
+    /// Get the pixel dimensions of a string when rendered in a specified font size
+    /// \param bWrap if true, string should be wrapped to the screen width, possibly
+    /// over multiple lines (=> returned height will reflect this); if false,
+    /// keep on one line (even if that makes it wider than the screen)
+    virtual CGSize TextSize(NSString *str, unsigned int iFontSize, bool bWrap)=0;
   private:
     typedef struct {
       float r, g, b;
diff --git a/Src/Common/OpenGLScreen.mm b/Src/Common/OpenGLScreen.mm
index ad6935a..e4c18f2 100644
--- a/Src/Common/OpenGLScreen.mm
+++ b/Src/Common/OpenGLScreen.mm
@@ -196,7 +196,7 @@ void dump(char *data, int width, int height)
 	std::cout << buf;
 }
 
-OpenGLScreen::AlphabetLetter::AlphabetLetter(OpenGLScreen *pScreen, const string &strText) : Label(pScreen,strText), str(NSStringFromStdString(strText)) {
+OpenGLScreen::AlphabetLetter::AlphabetLetter(OpenGLScreen *pScreen, const string &strText, unsigned int iWrapSize) : Label(pScreen,strText,iWrapSize), str(NSStringFromStdString(strText)) {
   [str retain];
 
   glGenTextures(1, &texture);
@@ -208,7 +208,7 @@ void OpenGLScreen::AlphabetLetter::PrepareTexture() {
   int width=1, height=1;
   GLfloat texw,texh;
   {
-    CGSize sz = static_cast<OpenGLScreen *>(m_pScreen)->TextSize(str,36);
+    CGSize sz = static_cast<OpenGLScreen *>(m_pScreen)->TextSize(str,m_iWrapSize ? m_iWrapSize : 36,m_iWrapSize);
     while (width<sz.width) width<<=1;
     while (height<sz.height) height<<=1;
     texw = sz.width/(float)width;
@@ -220,7 +220,7 @@ void OpenGLScreen::AlphabetLetter::PrepareTexture() {
   CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width*4, colorSpace, kCGImageAlphaPremultipliedLast);
   CGContextClearRect(context, CGRectMake(0.0, 0.0, width, height));
 
-  static_cast<OpenGLScreen *>(m_pScreen)->RenderStringOntoCGContext(str,context);
+  static_cast<OpenGLScreen *>(m_pScreen)->RenderStringOntoCGContext(str,context,m_iWrapSize);
 
   glBindTexture(GL_TEXTURE_2D, texture);
   //...but tell the GL _not_ to interpolate between texels, as that results in a _big_
@@ -248,8 +248,8 @@ OpenGLScreen::AlphabetLetter::~AlphabetLetter() {
   glDeleteTextures(1, &texture);
 }
 
-OpenGLScreen::AlphabetLetter *OpenGLScreen::MakeLabel(const std::string &strText) {
-  return new AlphabetLetter(this,strText);
+OpenGLScreen::AlphabetLetter *OpenGLScreen::MakeLabel(const std::string &strText,unsigned int iWrapSize) {
+  return new AlphabetLetter(this,strText,iWrapSize);
 }
 
 void OpenGLScreen::RegenerateLabels() {
@@ -267,7 +267,7 @@ void OpenGLScreen::DrawString(CDasherScreen::Label *label, screenint x, screenin
   // by the currently selected GL foreground colour
 	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 	glColor4f(colourTable[iColour].r, colourTable[iColour].g, colourTable[iColour].b, 1.0); //so we select the colour we want the text to appear in
-	CGSize sz = TextSize(l->str, iFontSize);
+	CGSize sz = TextSize(l->str, iFontSize, l->m_iWrapSize);
 	GLshort coords[8];
 	coords[0] = x; coords[1]=y;
 	coords[2] = x+sz.width; coords[3] = y;
@@ -285,7 +285,7 @@ void OpenGLScreen::DrawString(CDasherScreen::Label *label, screenint x, screenin
 
 pair<screenint,screenint> OpenGLScreen::TextSize(CDasherScreen::Label *label, unsigned int iFontSize) {
   const AlphabetLetter *l(static_cast<AlphabetLetter *> (label));
-  CGSize sz = TextSize(l->str, iFontSize);
+  CGSize sz = TextSize(l->str, iFontSize, l->m_iWrapSize);
   //apply "ceil" to floating-point width/height ?
   return pair<screenint,screenint>(sz.width, sz.height);
 }
diff --git a/Src/DasherCore/DasherScreen.h b/Src/DasherCore/DasherScreen.h
index ee83a95..48e7ae0 100644
--- a/Src/DasherCore/DasherScreen.h
+++ b/Src/DasherCore/DasherScreen.h
@@ -72,9 +72,16 @@ public:
   class Label {
     friend class CDasherScreen;
   protected:
-    Label(const std::string &strText) : m_strText(strText) {};
+    Label(const std::string &strText, unsigned int iWrapSize)
+    : m_strText(strText), m_iWrapSize(iWrapSize) {};
   public:
     const std::string m_strText;
+    ///If 0, Label is to be rendered on a single line.
+    /// Any other value, Label need only be renderable at that size, but should 
+    /// be _wrapped_ to fit the screen width. (It is up to platforms to decide
+    /// whether to support DrawString/TextSize at any other size but this is
+    /// NOT required.)
+    unsigned int m_iWrapSize;
     ///Delete the label. This should free up any resources associated with
     /// drawing the string onto the screen, e.g. layouts or textures.
     virtual ~Label() {}
@@ -82,7 +89,13 @@ public:
 
   ///Make a label for use with this screen.
   /// \param strText UTF8-encoded text.
-  virtual Label *MakeLabel(const std::string &strText) {return new Label(strText);}
+  /// \param iWrapSize 0 => create a Label that will be rendered on a single line,
+  /// potentially at multiple sizes; appropriate for DasherNode labels.
+  /// Any other value => Label SHOULD ONLY BE USED AT THAT SIZE, but should 
+  /// be _wrapped_ onto multiple lines if necessary to fit within the screen width.
+  /// (DrawString/TextSize with any other font size may produce unpredictable results,
+  /// depending on platform.)
+  virtual Label *MakeLabel(const std::string &strText, unsigned int iWrapSize=0) {return new Label(strText,iWrapSize);}
 
   ///Get Width and Height of a Label previously created by MakeLabel. Note behaviour
   /// undefined if the Label is not one returned from a call to MakeLabel _on_this_Screen_.
@@ -181,8 +194,8 @@ protected:
   }
   class Label : public CDasherScreen::Label {
   public: //to instances of CLabelListScreen and subclasses
-    Label(CLabelListScreen *pScreen, const std::string &strText)
-    : CDasherScreen::Label(strText), m_pScreen(pScreen) {
+    Label(CLabelListScreen *pScreen, const std::string &strText, unsigned int iWrapSize)
+    : CDasherScreen::Label(strText, iWrapSize), m_pScreen(pScreen) {
       m_pScreen->m_sLabels.insert(this);
     }
     ~Label() {
diff --git a/Src/Gtk2/Canvas.cpp b/Src/Gtk2/Canvas.cpp
index 5fc66f9..faefb57 100644
--- a/Src/Gtk2/Canvas.cpp
+++ b/Src/Gtk2/Canvas.cpp
@@ -412,8 +412,8 @@ void CCanvas::Polyline(Dasher::CDasherScreen::point *Points, int Number, int iWi
   END_DRAWING;
 }
 
-CDasherScreen::Label *CCanvas::MakeLabel(const string &strText) {
-  return new CPangoLabel(this, strText);
+CDasherScreen::Label *CCanvas::MakeLabel(const string &strText, unsigned int iWrapFontSize) {
+  return new CPangoLabel(this, strText, iWrapFontSize);
 }
 
 void CCanvas::SetFont(const std::string &strName) {
@@ -443,7 +443,7 @@ PangoLayout *CCanvas::GetLayout(CPangoLabel *label, unsigned int iFontSize) {
     PangoLayout *pNewPangoLayout(gtk_widget_create_pango_layout(pCanvas, ""));
 #endif
   label->m_mLayouts.insert(pair<unsigned int,PangoLayout *>(iFontSize, pNewPangoLayout));
-
+  if (label->m_iWrapSize) pango_layout_set_width(pNewPangoLayout, GetWidth() * PANGO_SCALE);
   pango_layout_set_text(pNewPangoLayout, label->m_strText.c_str(), -1);
   
   PangoFontDescription *pF;
diff --git a/Src/Gtk2/Canvas.h b/Src/Gtk2/Canvas.h
index 44987a4..05217c7 100644
--- a/Src/Gtk2/Canvas.h
+++ b/Src/Gtk2/Canvas.h
@@ -92,7 +92,7 @@ public:
   void SetFont(const std::string &strName);
 
   ///Make a label for use with this screen; caches Pango layout information inside it.
-  CDasherScreen::Label *MakeLabel(const std::string &strText);
+  CDasherScreen::Label *MakeLabel(const std::string &strText, unsigned int iWrapSize=0);
 
   ///
   /// Return the physical extent of a given string being rendered at a given size.
@@ -240,8 +240,8 @@ private:
 
   class CPangoLabel : public CLabelListScreen::Label {
   public:
-    CPangoLabel(CCanvas *pCanvas, const std::string &strText)
-    : CLabelListScreen::Label(pCanvas, strText) {
+    CPangoLabel(CCanvas *pCanvas, const std::string &strText, unsigned int iWrapFontSize)
+    : CLabelListScreen::Label(pCanvas, strText, iWrapFontSize) {
     }
     std::map<unsigned int,PangoLayout *> m_mLayouts;
   };
diff --git a/Src/MacOSX/DasherViewOpenGL.mm b/Src/MacOSX/DasherViewOpenGL.mm
index 9a74852..4f6e3e6 100755
--- a/Src/MacOSX/DasherViewOpenGL.mm
+++ b/Src/MacOSX/DasherViewOpenGL.mm
@@ -62,16 +62,20 @@ public:
   }
   
 protected:
-  void RenderStringOntoCGContext(NSString *string, CGContextRef context) {
+  void RenderStringOntoCGContext(NSString *string, CGContextRef context, unsigned int iWrapSize) {
     NSGraphicsContext *old = [NSGraphicsContext currentContext];
     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]];
-    
-    [string drawAtPoint:NSMakePoint(0.0, 0.0) withAttributes:fontAttrs];
+
+    if (iWrapSize)
+      [string drawWithRect:NSMakeRect(0.0, 0.0, GetWidth(), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:dasherView.cachedFontName size:iWrapSize],NSFontAttributeName,[NSColor whiteColor],NSForegroundColorAttributeName,nil]];
+    else
+      [string drawAtPoint:NSMakePoint(0.0, 0.0) withAttributes:fontAttrs];
     [NSGraphicsContext setCurrentContext:old];  
   }
   
-  CGSize TextSize(NSString *str, unsigned int iFontSize) {
-    return NSSizeToCGSize([str sizeWithAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:dasherView.cachedFontName size:iFontSize] forKey:NSFontAttributeName]]);    
+  CGSize TextSize(NSString *str, unsigned int iFontSize, bool bWrap) {
+    NSDictionary *attrs =[NSDictionary dictionaryWithObject:[NSFont fontWithName:dasherView.cachedFontName size:iFontSize] forKey:NSFontAttributeName];
+    return NSSizeToCGSize(bWrap ? ([str boundingRectWithSize:NSMakeSize(GetWidth(), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs].size) : ([str sizeWithAttributes:attrs]));
   }
 };
 
diff --git a/Src/Win32/Widgets/Screen.cpp b/Src/Win32/Widgets/Screen.cpp
index 445f0b9..6c72883 100644
--- a/Src/Win32/Widgets/Screen.cpp
+++ b/Src/Win32/Widgets/Screen.cpp
@@ -132,11 +132,11 @@ void CScreen::resize(screenint width, screenint height) {
   CreateBuffers();
 }
 
-CScreen::Label::Label(CScreen *pScreen, const string &strText) : CLabelListScreen::Label(pScreen, strText), m_OutputText(WinUTF8::UTF8string_to_wstring(m_strText)) {
+CScreen::Label::Label(CScreen *pScreen, const string &strText, unsigned int iWrapSize) : CLabelListScreen::Label(pScreen, strText, iWrapSize), m_OutputText(WinUTF8::UTF8string_to_wstring(m_strText)) {
 }
 
-CDasherScreen::Label *CScreen::MakeLabel(const string &strText) {
-  return new Label(this,strText);
+CDasherScreen::Label *CScreen::MakeLabel(const string &strText, unsigned int iWrapSize) {
+  return new Label(this,strText,iWrapSize);
 }
 
 void CScreen::DrawString(CDasherScreen::Label *lab, screenint x1, screenint y1, unsigned int iSize, int Colour) {
@@ -144,8 +144,8 @@ void CScreen::DrawString(CDasherScreen::Label *lab, screenint x1, screenint y1,
   RECT Rect;
   Rect.left = x1;
   Rect.top = y1;
-  Rect.right = x1 + 50;
-  Rect.bottom = y1 + 50;
+  Rect.right = x1 + (lab->m_iWrapSize ? GetWidth() : 50); //if not wrapping, will extend beyond RHS because of DT_NOCLIP
+  Rect.bottom = y1 + 50; //and beyond bottom in either case
 
   HFONT old = (HFONT) SelectObject(m_hDCBuffer, CScreen::GetFont(iSize));
 
@@ -158,7 +158,7 @@ void CScreen::DrawString(CDasherScreen::Label *lab, screenint x1, screenint y1,
   iCRefOld = SetTextColor(m_hDCBuffer, iCRefNew);
 
   // The Windows API dumps all its function names in the global namespace, ::
-  ::DrawText(m_hDCBuffer, label->m_OutputText.c_str(), label->m_OutputText.size(), &Rect, DT_LEFT | DT_TOP | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE);
+  ::DrawText(m_hDCBuffer, label->m_OutputText.c_str(), label->m_OutputText.size(), &Rect, (label->m_iWrapSize ? DT_CENTER | DT_WORDBREAK : DT_LEFT | DT_SINGLELINE) | DT_TOP | DT_NOCLIP | DT_NOPREFIX);
   
   SetTextColor(m_hDCBuffer, iCRefOld);
   SelectObject(m_hDCBuffer, old);
@@ -189,6 +189,18 @@ pair<screenint,screenint> CScreen::TextSize_Impl(CScreen::Label *label, unsigned
   HFONT old = (HFONT) SelectObject(m_hDCBuffer, CScreen::GetFont(iSize));
 
   // Get the dimensions of the text in pixels
+  if (label->m_iWrapSize) {
+    RECT Rect;
+    Rect.left = 0;
+    Rect.top = 0;
+    Rect.right = GetWidth(); //will be wrapped to this
+    Rect.bottom = 0; //this'll be overwritten
+    //This is the only way to get size of text w/ wrapping:
+    // -the DT_WORDBREAK flag says to wrap (no way to pass this to GetTextExtentPoint32)
+    // -the DT_CALCRECT flag says to modify Rect with the size, but not to draw the string.
+    ::DrawText(m_hDCBuffer, label->m_OutputText.c_str(), label->m_OutputText.size(), &Rect, DT_LEFT | DT_WORDBREAK | DT_TOP | DT_NOPREFIX | DT_CALCRECT);
+    return pair<screenint,screenint>(Rect.right,Rect.bottom);
+  }
   SIZE OutSize;
   GetTextExtentPoint32(m_hDCBuffer, label->m_OutputText.c_str(), label->m_OutputText.size(), &OutSize);
   SelectObject(m_hDCBuffer, old);
diff --git a/Src/Win32/Widgets/Screen.h b/Src/Win32/Widgets/Screen.h
index dd1cf61..ae998aa 100644
--- a/Src/Win32/Widgets/Screen.h
+++ b/Src/Win32/Widgets/Screen.h
@@ -40,8 +40,10 @@ public:
 
   void DrawMousePosBox(int which, int iMousePosDist,int layer=0);
 
-  //! Make label from UTF8-encoded string
-  CDasherScreen::Label *MakeLabel(const std::string &strText);
+  /// Make label from UTF8-encoded string
+  /// \param iWrapSize 0 => single-line label (for nodes); any other value => wrapped to screen width
+  /// (we wrap the text in whatever fontsize it's DrawString/TextSize'd in, even tho we don't have to)
+  CDasherScreen::Label *MakeLabel(const std::string &strText, unsigned int iWrapSize=0);
 
   std::pair<screenint,screenint> TextSize(CDasherScreen::Label *label, unsigned int Size);
 
@@ -119,7 +121,7 @@ private:
   class Label : public CLabelListScreen::Label {
   public:
     const Tstring m_OutputText;
-    Label(CScreen *pScreen, const std::string &strText);
+    Label(CScreen *pScreen, const std::string &strText,unsigned int iWrapSize);
     map<unsigned int,pair<screenint,screenint> > m_sizeCache;
   };
 
diff --git a/Src/iPhone/Classes/EAGLView.h b/Src/iPhone/Classes/EAGLView.h
index 7e58bf5..414e405 100644
--- a/Src/iPhone/Classes/EAGLView.h
+++ b/Src/iPhone/Classes/EAGLView.h
@@ -28,8 +28,8 @@ public:
   void SendMarker(int iMarker);
   
 protected:
-  void RenderStringOntoCGContext(NSString *str, CGContextRef context);
-  CGSize TextSize(NSString *str, unsigned int iFontSize);
+  void RenderStringOntoCGContext(NSString *str, CGContextRef context, unsigned int iWrapFontSize);
+  CGSize TextSize(NSString *str, unsigned int iFontSize, bool bWrap);
 };
 
 /*
diff --git a/Src/iPhone/Classes/EAGLView.mm b/Src/iPhone/Classes/EAGLView.mm
index 6a334f2..852522e 100644
--- a/Src/iPhone/Classes/EAGLView.mm
+++ b/Src/iPhone/Classes/EAGLView.mm
@@ -47,20 +47,26 @@ void CDasherScreenBridge::SendMarker(int iMarker) {
   [view sendMarker:iMarker];
 }
 
-void CDasherScreenBridge::RenderStringOntoCGContext(NSString *str, CGContextRef context) {
+void CDasherScreenBridge::RenderStringOntoCGContext(NSString *str, CGContextRef context, unsigned int iFontWrapSize) {
   UIGraphicsPushContext(context);
   //white text on transparent background means that when we texture
   //a surface using a colour, the text appears in that colour...
   const CGFloat whiteComps[] = {1.0, 1.0, 1.0, 1.0};
   CGColorRef white = CGColorCreate(CGBitmapContextGetColorSpace(context), whiteComps);
   CGContextSetFillColorWithColor(context, white);
-  [str drawAtPoint:CGPointMake(0.0, 0.0) withFont:[UIFont systemFontOfSize:36]];
+  if (iFontWrapSize)
+    [str drawInRect:CGRectMake(0.0, 0.0, GetWidth(), CGFLOAT_MAX) withFont:[UIFont systemFontOfSize:iFontWrapSize]];
+  else
+    [str drawAtPoint:CGPointMake(0.0, 0.0) withFont:[UIFont systemFontOfSize:36]];
   CGColorRelease(white);
   UIGraphicsPopContext();  
 }
   
-CGSize CDasherScreenBridge::TextSize(NSString *str, unsigned int iFontSize) {
-  return [str sizeWithFont:[UIFont systemFontOfSize:iFontSize]];
+CGSize CDasherScreenBridge::TextSize(NSString *str, unsigned int iFontSize, bool bWrap) {
+  UIFont *font = [UIFont systemFontOfSize:iFontSize];
+  return bWrap
+    ? [str sizeWithFont:font constrainedToSize:CGSizeMake(GetWidth(), CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap]
+    : [str sizeWithFont:font];
 }
 
 @implementation EAGLView



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]