#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasMgr.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
+#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/misc/sg_path.hxx>
addStyle("fill", &Image::setFill, this);
addStyle("slice", &Image::setSlice, this);
+ addStyle("slice-width", &Image::setSliceWidth, this);
addStyle("outset", &Image::setOutset, this);
setFill("#ffffff"); // TODO how should we handle default values?
SGRect<float> region = _region;
if( _outset.valid )
{
- region.t() -= _outset.t;
- region.r() += _outset.r;
- region.b() += _outset.b;
- region.l() -= _outset.l;
+ const CSSOffsets& outset = _outset.getAbsOffsets(tex_dim);
+ region.t() -= outset.t;
+ region.r() += outset.r;
+ region.b() += outset.b;
+ region.l() -= outset.l;
}
if( !_slice.valid )
-------------------- - y[3]
*/
+ const CSSOffsets& slice =
+ (_slice_width.valid ? _slice_width : _slice).getAbsOffsets(tex_dim);
float x[4] = {
region.l(),
- region.l() + _slice.l * tex_dim.width(),
- region.r() - _slice.r * tex_dim.width(),
+ region.l() + slice.l,
+ region.r() - slice.r,
region.r()
};
float y[4] = {
region.t(),
- region.t() + _slice.t * tex_dim.height(),
- region.b() - _slice.b * tex_dim.height(),
+ region.t() + slice.t,
+ region.b() - slice.b,
region.b()
};
}
else
{
+ const CSSOffsets& slice = _slice.getRelOffsets(tex_dim);
float x[4] = {
src_rect.l(),
- src_rect.l() + _slice.l,
- src_rect.r() - _slice.r,
+ src_rect.l() + slice.l,
+ src_rect.r() - slice.r,
src_rect.r()
};
float y[4] = {
src_rect.t(),
- src_rect.t() - _slice.t,
- src_rect.b() + _slice.b,
+ src_rect.t() - slice.t,
+ src_rect.b() + slice.b,
src_rect.b()
};
_attributes_dirty |= SRC_RECT | DEST_SIZE;
}
+ //----------------------------------------------------------------------------
+ void Image::setSliceWidth(const std::string& width)
+ {
+ _slice_width = parseSideOffsets(width);
+ _attributes_dirty |= DEST_SIZE;
+ }
+
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
return _region;
}
+ //----------------------------------------------------------------------------
+ bool Image::handleMouseEvent(MouseEventPtr event)
+ {
+ CanvasPtr src_canvas = _src_canvas.lock();
+
+ if( !src_canvas )
+ return false;
+
+ if( _outset.valid )
+ {
+ CSSOffsets outset = _outset.getAbsOffsets(getTextureDimensions());
+
+ event.reset( new MouseEvent(*event) );
+ event->client_pos += osg::Vec2f(outset.l, outset.t);
+ event->client_pos.x() *= src_canvas->getViewWidth()
+ / (_region.width() + outset.l + outset.r);
+ event->client_pos.y() *= src_canvas->getViewHeight()
+ / (_region.height() + outset.t + outset.b);
+ }
+
+ return src_canvas->handleMouseEvent(event);
+ }
+
+ //----------------------------------------------------------------------------
+ Image::CSSOffsets
+ Image::CSSBorder::getRelOffsets(const SGRect<int>& dim) const
+ {
+ CSSOffsets ret;
+ for(int i = 0; i < 4; ++i)
+ {
+ ret.val[i] = offsets.val[i];
+ if( !types.rel[i] )
+ ret.val[i] /= (i & 1) ? dim.height() : dim.width();
+ }
+ return ret;
+ }
+
+ //----------------------------------------------------------------------------
+ Image::CSSOffsets
+ Image::CSSBorder::getAbsOffsets(const SGRect<int>& dim) const
+ {
+ CSSOffsets ret;
+ for(int i = 0; i < 4; ++i)
+ {
+ ret.val[i] = offsets.val[i];
+ if( types.rel[i] )
+ ret.val[i] *= (i & 1) ? dim.height() : dim.width();
+ }
+ return ret;
+ }
+
//----------------------------------------------------------------------------
void Image::childChanged(SGPropertyNode* child)
{
}
//----------------------------------------------------------------------------
- Image::CSSOffsets Image::parseSideOffsets(const std::string& str) const
+ Image::CSSBorder Image::parseSideOffsets(const std::string& str) const
{
if( str.empty() )
- return CSSOffsets();
-
- const SGRect<int>& dim = getTextureDimensions();
+ return CSSBorder();
// [<number>'%'?]{1,4} (top[,right[,bottom[,left]]])
//
// image for the horizontal offsets, the height for vertical offsets.
// Numbers represent pixels in the image.
int c = 0;
- CSSOffsets ret;
+ CSSBorder ret;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
const boost::char_separator<char> del(" \t\n");
ret.keyword = *tok;
else
{
- bool rel = (*tok->rbegin() == '%');
- ret.offsets[c] =
+ bool rel = ret.types.rel[c] = (*tok->rbegin() == '%');
+ ret.offsets.val[c] =
// Negative values are not allowed and values bigger than the size of
- // the image are interpreted as ‘100%’.
- std::max(0.f,
- std::min(1.f,
+ // the image are interpreted as ‘100%’. TODO check max
+ std::max
+ (
+ 0.f,
boost::lexical_cast<float>
(
rel ? boost::make_iterator_range(tok->begin(), tok->end() - 1)
: *tok
)
/
- (
- rel ? 100
- : (c & 1) ? dim.width() : dim.height()
- )
- ));
-
+ (rel ? 100 : 1)
+ );
++c;
}
}
// When four values are specified, they set the offsets on the top, right,
// bottom and left sides in that order.
+
+#define CSS_COPY_VAL(dest, src)\
+ {\
+ ret.offsets.val[dest] = ret.offsets.val[src];\
+ ret.types.rel[dest] = ret.types.rel[src];\
+ }
+
if( c < 4 )
{
if( c < 3 )
{
if( c < 2 )
// if the right is missing, it is the same as the top.
- ret.offsets[1] = ret.offsets[0];
+ CSS_COPY_VAL(1, 0);
// if the bottom is missing, it is the same as the top
- ret.offsets[2] = ret.offsets[0];
+ CSS_COPY_VAL(2, 0);
}
// If the left is missing, it is the same as the right
- ret.offsets[3] = ret.offsets[1];
+ CSS_COPY_VAL(3, 1);
}
+#undef CSS_COPY_VAL
+
ret.valid = true;
return ret;
}
*/
void setSlice(const std::string& slice);
+ /**
+ * Set image slice width.
+ *
+ * By default the size of the 9-scale grid is the same as specified
+ * with setSlice/"slice". Using this method allows setting values
+ * different to the size in the source image.
+ *
+ * @see http://www.w3.org/TR/css3-background/#border-image-width
+ */
+ void setSliceWidth(const std::string& width);
+
/**
* http://www.w3.org/TR/css3-background/#border-image-outset
*/
const SGRect<float>& getRegion() const;
+ bool handleMouseEvent(MouseEventPtr event);
+
protected:
enum ImageAttributes
DEST_SIZE = SRC_RECT << 1 // Element size
};
- struct CSSOffsets
+ union CSSOffsets
+ {
+ float val[4];
+ struct { float t, r, b, l; };
+ };
+
+ union CSSOffsetsTypes
+ {
+ bool rel[4];
+ struct { bool t_rel, r_rel, b_rel, l_rel; };
+ };
+
+ struct CSSBorder
{
- CSSOffsets():
+ CSSBorder():
valid(false)
{}
- union
- {
- float offsets[4];
- struct { float t, r, b, l; };
- };
+ CSSOffsets getRelOffsets(const SGRect<int>& dim) const;
+ CSSOffsets getAbsOffsets(const SGRect<int>& dim) const;
- std::string keyword;
- bool valid;
+ CSSOffsets offsets;
+ CSSOffsetsTypes types;
+ std::string keyword;
+ bool valid;
};
virtual void childChanged(SGPropertyNode * child);
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
- CSSOffsets parseSideOffsets(const std::string& str) const;
+ CSSBorder parseSideOffsets(const std::string& str) const;
osg::ref_ptr<osg::Texture2D> _texture;
// TODO optionally forward events to canvas
SGRect<float> _src_rect,
_region;
- CSSOffsets _slice,
+ CSSBorder _slice,
+ _slice_width,
_outset;
};