view dw/style.hh @ 2030:382160be8c2f

cookies comments I was going to say something in the comments about simplicity and not implementing every little bit of the rfc when we don't know of cookies that make it necessary, but then I suppose that's all implied with dillo.
author corvid <corvid@lavabit.com>
date Tue, 17 May 2011 22:48:50 +0000
parents 433a380f83a1
children
line wrap: on
line source
#ifndef __DW_STYLE_HH__
#define __DW_STYLE_HH__

#include <stdint.h>

#ifndef __INCLUDED_FROM_DW_CORE_HH__
#   error Do not include this file directly, use "core.hh" instead.
#endif

namespace dw {
namespace core {

/**
 * \brief Anything related to Dillo %Widget styles is defined here.
 *
 * <h3>Overview</h3>
 *
 * dw::core::style::Style provides some resources and attributes for
 * drawing widgets, as well as for parts of a widget (e.g., dw::Textblock
 * uses styles for its words). Creating a style is done by filling a
 * dw::core::style::StyleAttrs with the attributes and calling
 * dw::core::style::Style::create:
 *
 * \code
 * dw::core::style::Style styleAttrs;
 * dw::core::style::Style *style;
 * dw::core::Layout *layout;
 *
 * // ...
 *
 * styleAttrs.foo = bar;
 * // etc.
 * style = dw::core::style::Style::create (&styleAttrs, layout);
 * // do something with style
 * \endcode
 *
 * After this, the attributes of a dw::core::style::Style should not be
 * changed anymore, since styles are often shared between different
 * widgets etc. (see below). Most times, you simply copy the attributes
 * of another style (possible, since dw::core::style::Style is a sub
 * class of dw::core::style::StyleAttrs), modify them and create a new
 * style:
 *
 * \code
 * styleAttrs = *anotherStyle;
 * styleAttrs.foo = baz;
 * style = dw::core::style::Style::create (&styleAttrs, layout);
 * \endcode
 *
 * The dw::core::style::Font structure can be created by
 * dw::core::style::Font::create, in a similar, with
 * dw::core::style::FontAttrs, and colors by
 * dw::core::style::Color::create, passing 0xrrggbb as an
 * argument. Furthermore, there is dw::core::style::Tooltip, created by
 * dw::core::style::Tooltip::create.
 *
 * Notice that fonts, colors and tooltips are only intended to be used in
 * conjunction with dw::core::style::Style.
 *
 *
 * <h3>Naming</h3>
 *
 * dw::core::style::Style will become important for CSS, each CSS
 * attribute, which is supported by dillo, will refer to an attribute in
 * dw::core::style::Style. For this reason, the attributes in
 * dw::core::style::Style get the names from the CSS attributes, with
 * "camelCase" instead of hyphens (e.g. "background-color" becomes
 * "backgroundColor").
 *
 * However, dw::core::style::Style will be extended by some more
 * attributes, which are not defined by CSS. To distinguish them, they
 * get the prefix "x_", e.g. dw::core::style::Style::x_link.
 *
 *
 * <h3>Lengths and Percentages</h3>
 *
 * dw::core::style::Length is a simple data type for lengths and
 * percentages:
 *
 * <ul>
 * <li> A length refers to an absolute measurement. It is used to
 *      represent the HTML type %Pixels; and the CSS type \<length\>.
 *
 *      For CSS lengths, there are two units: (i) pixels and absolute
 *      units, which have to be converted to pixels (a pixel is, unlike
 *      in the CSS specification, treated as absolute unit), and (ii) the
 *      relative units "em" and "ex" (see below).
 *
 * <li> A percentage refers to a value relative to another value. It is
 *      used for the HTML type %Length; (except %Pixels;), and the CSS
 *      type \<percentage\>.
 *
 * <li> A relative length can be used in lists of HTML MultiLengths.
 * </ul>
 *
 * Since many values in CSS may be either lengths or percentages, a
 * single type is very useful.
 *
 * <h4>Useful Functions</h4>
 *
 * Creating lengths:
 *
 * <ul>
 * <li> dw::core::style::createAbsLength
 * <li> dw::core::style::createPerLength
 * <li> dw::core::style::createRelLength
 * </ul>
 *
 * Examine lengths:
 *
 * <ul>
 * <li> dw::core::style::isAbsLength
 * <li> dw::core::style::isPerLength
 * <li> dw::core::style::isRelLength
 * <li> dw::core::style::absLengthVal
 * <li> dw::core::style::perLengthVal
 * <li> dw::core::style::relLengthVal
 * </ul>
 *
 *
 * <h3>Boxes</h3>
 *
 * <h4>The CSS %Box Model</h4>
 *
 * For borders, margins etc., the box model defined by CSS2 is
 * used. dw::core::style::Style contains some members defining these
 * attributes. A dw::core::Widget must use these values for any
 * calculation of sizes. There are some helper functions (see
 * dw/style.hh). A dw::core::style::Style box looks quite similar to a
 * CSS box:
 *
 * \image html dw-style-box-model.png
 *
 * <h4>Background colors</h4>
 *
 * The background color is stored in
 * dw::core::style::Style::backgroundColor, which may be NULL (the
 * background color of the parent widget is shining through).
 *
 * For toplevel widgets, this color is set as the background color of the
 * views (dw::core::View::setBgColor), for other widgets, a filled
 * rectangle is drawn, covering the content and padding. (This is
 * compliant with CSS2, the background color of the toplevel element
 * covers the whole canvas.)
 *
 * <h4>Drawing</h4>
 *
 * The following methods may be useful:
 *
 * <ul>
 * <li> dw::core::Widget::drawWidgetBox for drawing the box of a widget
 *      (typically at the beginning of the implementation of
 *      dw::core::Widget::draw), and
 *
 * <li> dw::core::Widget::drawBox, for drawing parts of a widget (e.g.
 *      dw::Textblock::Word, which has its own dw::Textblock::Word::style).
 * </ul>
 *
 *
 * <h3>Notes on Memory Management</h3>
 *
 * Memory management is done by reference counting,
 * dw::core::style::Style::create returns a pointer to
 * dw::core::style::Style with an increased reference counter, so you
 * should care about calling dw::core::style::Style::unref if it is not
 * used anymore. You do \em not need to care about the reference counters
 * of fonts and styles.
 *
 * In detail:
 *
 * <ul>
 * <li> dw::core::style::Style::ref is called in
 *
 *      <ul>
 *      <li> dw::core::Widget::setStyle to assign a style to a widget,
 *      <li> dw::Textblock::addText, dw::Textblock::addWidget,
 *           dw::Textblock::addAnchor, dw::Textblock::addSpace,
 *           dw::Textblock::addParbreak and dw::Textblock::addLinebreak,
 *           to assign a style to a dw::Textblock::Word, and
 *      <li> by the HTML parser, when pushing an element on the stack.
 *      </ul>
 *
 * <li> dw::core::style::Style::unref is called in
 *
 *      <ul>
 *      <li> dw::core::Widget::~Widget, dw::Textblock::~Textblock, by the
 *           HTML parser, when popping an element fom the stack, and
 *      <li> dw::core::Widget::setStyle, dw::Textblock::addText etc.,
 *           these methods overwrite an existing style.
 *      </ul>
 * </ul>
 */
namespace style {

enum Cursor {
   CURSOR_CROSSHAIR,
   CURSOR_DEFAULT,
   CURSOR_POINTER,
   CURSOR_MOVE,
   CURSOR_E_RESIZE,
   CURSOR_NE_RESIZE,
   CURSOR_NW_RESIZE,
   CURSOR_N_RESIZE,
   CURSOR_SE_RESIZE,
   CURSOR_SW_RESIZE,
   CURSOR_S_RESIZE,
   CURSOR_W_RESIZE,
   CURSOR_TEXT,
   CURSOR_WAIT,
   CURSOR_HELP
};

enum BorderCollapse {
   BORDER_MODEL_SEPARATE,
   BORDER_MODEL_COLLAPSE
};

enum BorderStyle {
   BORDER_NONE,
   BORDER_HIDDEN,
   BORDER_DOTTED,
   BORDER_DASHED,
   BORDER_SOLID,
   BORDER_DOUBLE,
   BORDER_GROOVE,
   BORDER_RIDGE,
   BORDER_INSET,
   BORDER_OUTSET
};

enum TextAlignType {
   TEXT_ALIGN_LEFT,
   TEXT_ALIGN_RIGHT,
   TEXT_ALIGN_CENTER,
   TEXT_ALIGN_JUSTIFY,
   TEXT_ALIGN_STRING
};

enum VAlignType {
   VALIGN_TOP,
   VALIGN_BOTTOM,
   VALIGN_MIDDLE,
   VALIGN_BASELINE,
   VALIGN_SUB,
   VALIGN_SUPER,
   VALIGN_TEXT_TOP,
   VALIGN_TEXT_BOTTOM,
};

/**
 * \todo Incomplete. Has to be completed for a CSS implementation.
 */
enum DisplayType {
   DISPLAY_BLOCK,
   DISPLAY_INLINE,
   DISPLAY_LIST_ITEM,
   DISPLAY_NONE,
   DISPLAY_TABLE,
   DISPLAY_TABLE_ROW_GROUP,
   DISPLAY_TABLE_HEADER_GROUP,
   DISPLAY_TABLE_FOOTER_GROUP,
   DISPLAY_TABLE_ROW,
   DISPLAY_TABLE_CELL
};

enum LineType {
   LINE_NORMAL,
   LINE_DOTTED,
   LINE_DASHED
};

enum ListStylePosition {
   LIST_STYLE_POSITION_INSIDE,
   LIST_STYLE_POSITION_OUTSIDE
};

enum ListStyleType {
   LIST_STYLE_TYPE_DISC,
   LIST_STYLE_TYPE_CIRCLE,
   LIST_STYLE_TYPE_SQUARE,
   LIST_STYLE_TYPE_DECIMAL,
   LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO,
   LIST_STYLE_TYPE_LOWER_ROMAN,
   LIST_STYLE_TYPE_UPPER_ROMAN,
   LIST_STYLE_TYPE_LOWER_GREEK,
   LIST_STYLE_TYPE_LOWER_ALPHA,
   LIST_STYLE_TYPE_LOWER_LATIN,
   LIST_STYLE_TYPE_UPPER_ALPHA,
   LIST_STYLE_TYPE_UPPER_LATIN,
   LIST_STYLE_TYPE_HEBREW,
   LIST_STYLE_TYPE_ARMENIAN,
   LIST_STYLE_TYPE_GEORGIAN,
   LIST_STYLE_TYPE_CJK_IDEOGRAPHIC,
   LIST_STYLE_TYPE_HIRAGANA,
   LIST_STYLE_TYPE_KATAKANA,
   LIST_STYLE_TYPE_HIRAGANA_IROHA,
   LIST_STYLE_TYPE_KATAKANA_IROHA,
   LIST_STYLE_TYPE_NONE
};

enum FontStyle {
  FONT_STYLE_NORMAL,
  FONT_STYLE_ITALIC,
  FONT_STYLE_OBLIQUE
};

enum FontVariant {
   FONT_VARIANT_NORMAL,
   FONT_VARIANT_SMALL_CAPS
};

enum TextDecoration {
   TEXT_DECORATION_NONE         = 0,
   TEXT_DECORATION_UNDERLINE    = 1 << 0,
   TEXT_DECORATION_OVERLINE     = 1 << 1,
   TEXT_DECORATION_LINE_THROUGH = 1 << 2,
   TEXT_DECORATION_BLINK        = 1 << 3
};

enum WhiteSpace {
   WHITE_SPACE_NORMAL,
   WHITE_SPACE_PRE,
   WHITE_SPACE_NOWRAP,
   WHITE_SPACE_PRE_WRAP,
   WHITE_SPACE_PRE_LINE,
};

/**
 * \brief Type for representing all lengths within dw::core::style.
 *
 * Lengths are int's. Absolute lengths are represented in the following way:
 *
 * \image html dw-style-length-absolute.png
 *
 * Percentages:
 *
 * \image html dw-style-length-percentage.png
 *
 * Relative lengths (only used in HTML):
 *
 * \image html dw-style-length-relative.png
 *
 * This is an implementation detail, use one of the following functions:
 *
 * Creating lengths:
 *
 * <ul>
 * <li> dw::core::style::createAbsLength
 * <li> dw::core::style::createPerLength
 * <li> dw::core::style::createRelLength
 * </ul>
 *
 * Examine lengths:
 *
 * <ul>
 * <li> dw::core::style::isAbsLength
 * <li> dw::core::style::isPerLength
 * <li> dw::core::style::isRelLength
 * <li> dw::core::style::absLengthVal
 * <li> dw::core::style::perLengthVal
 * <li> dw::core::style::relLengthVal
 * </ul>
 *
 * "auto" lengths are represented as dw::core::style::LENGTH_AUTO.
 */
typedef int Length;

/** \brief Returns a length of \em n pixels. */
inline Length createAbsLength(int n) { return (n << 2) | 1; }

/** \brief Returns a percentage, \em v is relative to 1, not to 100. */
inline Length createPerLength(double v) {
   return ((int)(v * (1 << 18)) & ~3) | 2; }

/** \brief Returns a relative length. */
inline Length createRelLength(double v) {
   return ((int)(v * (1 << 18)) & ~3) | 3; }

/** \brief Returns true if \em l is an absolute length. */
inline bool isAbsLength(Length l) { return (l & 3) == 1; }

/** \brief Returns true if \em l is a percentage. */
inline bool isPerLength(Length l) { return (l & 3) == 2; }

/** \brief Returns true if \em l is a relative length. */
inline bool isRelLength(Length l) { return (l & 3) == 3; }

/** \brief Returns the value of a length in pixels, as an integer. */
inline int absLengthVal(Length l) { return l >> 2; }

/** \brief Returns the value of a percentage, relative to 1, as a double. */
inline double perLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }

/** \brief Returns the value of a relative length, as a float. */
inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }

enum {
   /** \brief Represents "auto" lengths. */
   LENGTH_AUTO = 0
};

/**
 * \brief Represents a dimension box according to the CSS box model.
 *
 * Used for dw::core::style::Style::margin,
 * dw::core::style::Style::borderWidth, and dw::core::style::Style::padding.
 */
class Box
{
public:
   /* in future also percentages */
   int top, right, bottom, left;

   inline void setVal(int val) { top = right = bottom = left = val; }
   inline bool equals (Box *other) {
      return top == other->top &&
         right == other->right &&
         bottom == other->bottom &&
         left == other->left;
   }
   inline int hashValue () {
      return top + right + bottom + left;
   }
};

class Font;
class Color;
class Tooltip;

/**
 * \sa dw::core::style
 */
class StyleAttrs : public lout::object::Object
{
public:
   Font *font;
   int textDecoration; /* No TextDecoration because of problems converting
                        * TextDecoration <-> int */
   Color *color, *backgroundColor;

   TextAlignType textAlign;
   VAlignType valign;
   char textAlignChar; /* In future, strings will be supported. */

   int hBorderSpacing, vBorderSpacing, wordSpacing;
   Length width, height, lineHeight, textIndent;

   Box margin, borderWidth, padding;
   BorderCollapse borderCollapse;
   struct { Color *top, *right, *bottom, *left; } borderColor;
   struct { BorderStyle top, right, bottom, left; } borderStyle;

   DisplayType display;
   WhiteSpace whiteSpace;
   ListStylePosition listStylePosition;
   ListStyleType listStyleType;
   Cursor cursor;

   int x_link;
   int x_img;
   Tooltip *x_tooltip;

   void initValues ();
   void resetValues ();

   bool sizeDiffs (StyleAttrs *otherStyleAttrs);

   inline void setBorderColor(Color *val) {
      borderColor.top = borderColor.right = borderColor.bottom
         = borderColor.left = val; }
   inline void setBorderStyle(BorderStyle val) {
      borderStyle.top = borderStyle.right = borderStyle.bottom
         = borderStyle.left = val; }

   inline int boxOffsetX ()
   {
      return margin.left + borderWidth.left + padding.left;
   }
   inline int boxRestWidth ()
   {
      return margin.right + borderWidth.right + padding.right;
   }
   inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); }
   inline int boxOffsetY ()
   {
      return margin.top + borderWidth.top + padding.top;
   }
   inline int boxRestHeight ()
   {
      return margin.bottom + borderWidth.bottom + padding.bottom;
   }
   inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }

   inline bool hasBackground () { return backgroundColor != NULL; }

   bool equals (lout::object::Object *other);
   int hashValue ();
};


/**
 * \sa dw::core::style
 */
class Style: public StyleAttrs
{
private:
   static int totalRef;
   int refCount;
   static lout::container::typed::HashTable <StyleAttrs, Style> *styleTable;

   Style (StyleAttrs *attrs);

protected:
   ~Style();

   void copyAttrs (StyleAttrs *attrs);

public:
   inline static Style *create (Layout *layout, StyleAttrs *attrs)
   {
      Style *style = styleTable->get (attrs);
      if (style) {
         style->ref ();
      } else {
         style = new Style (attrs);
         styleTable->put(style, style);
      }
      return style;
   }

   inline void ref () { refCount++; }
   inline void unref () { if (--refCount == 0) delete this; }
};


/**
 * \sa dw::core::style
 */
class TooltipAttrs: public lout::object::String
{
public:
   TooltipAttrs(const char *text): lout::object::String(text) { }
};

/**
 * \sa dw::core::style
 */
class Tooltip: public TooltipAttrs
{
private:
   int refCount;

protected:
   Tooltip (const char *text): TooltipAttrs(text) { refCount = 0; }

public:
   static Tooltip *create (dw::core::Layout *layout, const char *text);
   inline void ref () { refCount++; }
   inline void unref ()
   { if (--refCount == 0) delete this; }

   inline virtual void onEnter () { }
   inline virtual void onLeave () { }
   inline virtual void onMotion () { }
};


/**
 * \sa dw::core::style
 */
class FontAttrs: public lout::object::Object
{
public:
   const char *name;
   int size;
   int weight;
   int letterSpacing;
   FontVariant fontVariant;
   FontStyle style;

   bool equals(lout::object::Object *other);
   int hashValue();
};


/**
 * \sa dw::core::style
 */
class Font: public FontAttrs
{
private:
   int refCount;

   static Font *create0 (Layout *layout, FontAttrs *attrs, bool tryEverything);

protected:
   inline Font () { refCount = 0; }
   virtual ~Font ();

   void copyAttrs (FontAttrs *attrs);

public:
   int ascent, descent;
   int spaceWidth;
   int xHeight;

   static Font *create (Layout *layout, FontAttrs *attrs);
   static bool exists (Layout *layout, const char *name);

   inline void ref () { refCount++; }
   inline void unref () { if (--refCount == 0) delete this; }
};


/**
 * \sa dw::core::style
 */
class ColorAttrs: public lout::object::Object
{
protected:
   int color;

public:
   inline ColorAttrs(int color)
   {
      this->color = color;
   }

   inline int getColor () { return color; }

   bool equals(lout::object::Object *other);
   int hashValue();
};


/**
 * \sa dw::core::style
 */
class Color: public ColorAttrs
{
private:
   int refCount;

   void remove(dw::core::Layout *layout);
   int shadeColor (int color, int d);

protected:
   inline Color (int color): ColorAttrs (color) {
      refCount = 0; }
   virtual ~Color ();

public:
   enum Shading { SHADING_NORMAL, SHADING_INVERSE, SHADING_DARK, SHADING_LIGHT,
                  SHADING_NUM };

protected:
   int shadeColor (int color, Shading shading);

public:
   static Color *create (Layout *layout, int color);

   inline void ref () { refCount++; }
   inline void unref ()
   { if (--refCount == 0) delete this; }
};

void drawBorder (View *view, Rectangle *area,
                 int x, int y, int width, int height,
                 Style *style, bool inverse);
void drawBackground (View *view, Rectangle *area,
                     int x, int y, int width, int height,
                     Style *style, bool inverse);
void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType);

} // namespace style
} // namespace dw
} // namespace core

#endif // __DW_STYLE_HH__