XRC: add GetBitmapBundle function

The function creates wxBitmapBundle from <bitmaps> xrc tag.

Co-authored-by: VZ <vz-github@zeitlins.org>
This commit is contained in:
Alexander Koshelev
2022-01-20 17:10:47 +03:00
parent 3cc4ef5f77
commit df1504dd8f
5 changed files with 223 additions and 35 deletions

View File

@@ -340,8 +340,9 @@ or translations are done.
@subsection overview_xrcformat_type_bitmap Bitmap
Bitmap properties contain specification of a single bitmap or icon. In the most
basic form, their text value is simply a relative URL of the bitmap to use.
Bitmap properties contain specification of a single bitmap, icon, a set of bitmaps
or SVG file. In the most basic form, their text value is simply a relative URL of
the bitmap to use.
For example:
@code
<object class="tool" name="wxID_NEW">
@@ -363,6 +364,31 @@ percent-encoded, e.g. here is the correct way to specify a bitmap with the path
Bitmap file paths can include environment variables that are expanded if
wxXRC_USE_ENVVARS was passed to the wxXmlResource constructor.
It is possible to specify the multi-resolution bitmap by a set of bitmaps or
an SVG file, which are mutually exclusive. The set of bitmaps should contain
one or more relative URLs of a bitmap, separated by @c ';'.
For example, to specify two bitmaps, to be used in standard and 200% DPI
scaling respectively, you could write:
@code
<bitmap>new.png;new_2x.png</bitmap>
@endcode
Here the first bitmap is special, as its size determines the logical size of
the bitmap. In other words, this bitmap is the one used when DPI scaling
is not in effect. Any subsequent bitmaps can come in any order and will be used
when the DPI scaling factor is equal, or at least close, to the ratio of their
size to the size of the first bitmap. Using @c _2x naming convention here is common,
but @e not required, the names of the bitmaps can be arbitrary, e.g.
@code
<bitmap>new_32x32.png;new_64x64.png</bitmap>
@endcode
would work just as well.
When using SVG file you must also specify @c default_size attribute
(even if the size is specified in SVG file, it may be different from the size needed here):
@code
<bitmap default_size="32,32">new.svg</bitmap>
@endcode
Alternatively, it is possible to specify the bitmap using wxArtProvider IDs.
In this case, the property element has no textual value (filename) and instead
has the @c stock_id XML attribute that contains stock art ID as accepted by

View File

@@ -583,6 +583,11 @@ public:
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize) wxOVERRIDE;
// Gets a bitmap bundle.
wxBitmapBundle GetBitmapBundle(const wxString& param = wxT("bitmap"),
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize) wxOVERRIDE;
// Gets an icon.
wxIcon GetIcon(const wxString& param = wxT("icon"),
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),

View File

@@ -90,6 +90,9 @@ public:
virtual wxBitmap GetBitmap(const wxXmlNode* node,
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize) = 0;
virtual wxBitmapBundle GetBitmapBundle(const wxString& param = wxT("bitmap"),
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize) = 0;
virtual wxIcon GetIcon(const wxString& param = wxT("icon"),
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize) = 0;
@@ -332,6 +335,12 @@ protected:
{
return GetImpl()->GetBitmap(node, defaultArtClient, size);
}
wxBitmapBundle GetBitmapBundle(const wxString& param = wxT("bitmap"),
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize)
{
return GetImpl()->GetBitmapBundle(param, defaultArtClient, size);
}
wxIcon GetIcon(const wxString& param = wxT("icon"),
const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER),
wxSize size = wxDefaultSize)

View File

@@ -467,7 +467,8 @@ t_bitmap = t_url?,
(
attribute stock_id { t_identifier},
attribute stock_client { t_identifier}?
)?
)?,
attribute default_size { t_size}?
t_font = (
[xrc:p="o"] element size {_, t_float }* &

View File

@@ -1852,6 +1852,52 @@ bool GetStockArtAttrs(const wxXmlNode *paramNode,
return false;
}
// Load a bitmap from a file system element.
wxBitmap LoadBitmapFromFS(wxXmlResourceHandlerImpl* impl,
const wxString& path,
wxSize size,
const wxString& nodeName)
{
if (path.empty()) return wxNullBitmap;
#if wxUSE_FILESYSTEM
wxFSFile *fsfile = impl->GetCurFileSystem().OpenFile(path, wxFS_READ | wxFS_SEEKABLE);
if (fsfile == NULL)
{
impl->ReportParamError
(
nodeName,
wxString::Format("cannot open bitmap resource \"%s\"", path)
);
return wxNullBitmap;
}
wxImage img(*(fsfile->GetStream()));
delete fsfile;
#else
wxImage img(name);
#endif
if (!img.IsOk())
{
impl->ReportParamError
(
nodeName,
wxString::Format("cannot create bitmap from \"%s\"", path)
);
return wxNullBitmap;
}
if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y);
return wxBitmap(img);
}
// forward declaration
template <typename T>
T
ParseStringInPixels(wxXmlResourceHandlerImpl* impl,
const wxString& param,
const wxString& str,
const T& defaultValue,
wxWindow *windowToUse = NULL);
} // anonymous namespace
wxBitmap wxXmlResourceHandlerImpl::GetBitmap(const wxString& param,
@@ -1892,36 +1938,126 @@ wxBitmap wxXmlResourceHandlerImpl::GetBitmap(const wxXmlNode* node,
}
/* ...or load the bitmap from file: */
wxString name = GetFilePath(node);
if (name.empty()) return wxNullBitmap;
#if wxUSE_FILESYSTEM
wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
if (fsfile == NULL)
{
ReportParamError
(
node->GetName(),
wxString::Format("cannot open bitmap resource \"%s\"", name)
);
return wxNullBitmap;
}
wxImage img(*(fsfile->GetStream()));
delete fsfile;
#else
wxImage img(name);
#endif
return LoadBitmapFromFS(this, GetFilePath(node), size, node->GetName());
}
if (!img.IsOk())
wxBitmapBundle wxXmlResourceHandlerImpl::GetBitmapBundle(const wxString& param,
const wxArtClient& defaultArtClient,
wxSize size)
{
wxASSERT_MSG( !param.empty(), "bitmap parameter name can't be empty" );
const wxXmlNode* const node = GetParamNode(param);
if ( !node )
{
ReportParamError
(
node->GetName(),
wxString::Format("cannot create bitmap from \"%s\"", name)
);
// this is not an error as bitmap parameter could be optional
return wxNullBitmap;
}
if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y);
return wxBitmap(img);
/* If the bitmap is specified as stock item, query wxArtProvider for it: */
wxString art_id, art_client;
if ( GetStockArtAttrs(node, defaultArtClient,
art_id, art_client) )
{
wxBitmapBundle stockArt(wxArtProvider::GetBitmapBundle(art_id, art_client, size));
if ( stockArt.IsOk() )
return stockArt;
}
wxBitmapBundle bitmapBundle;
wxString paramValue = GetParamValue(node);
if ( paramValue.EndsWith(".svg") )
{
if ( paramValue.Contains(";") )
{
ReportParamError
(
param,
"may contain either one SVG file or a list of files separated by ';'"
);
return bitmapBundle;
}
// it is a bundle from svg file
wxString svgDefaultSizeAttr = node->GetAttribute("default_size", "");
if ( svgDefaultSizeAttr.empty() )
{
ReportParamError
(
param,
"'default_size' attribute required with svg file"
);
}
else
{
#ifdef wxHAS_SVG
wxSize svgDefaultSize = ParseStringInPixels(this, param, svgDefaultSizeAttr, wxDefaultSize);
#if wxUSE_FILESYSTEM
wxFSFile* fsfile = GetCurFileSystem().OpenFile(paramValue, wxFS_READ | wxFS_SEEKABLE);
if (fsfile == NULL)
{
ReportParamError
(
param,
wxString::Format("cannot open SVG resource \"%s\"", paramValue)
);
}
else
{
wxInputStream* s = fsfile->GetStream();
const size_t len = static_cast<size_t>(s->GetLength());
wxCharBuffer buf(len);
char* const ptr = buf.data();
if (s->ReadAll(ptr, len))
{
bitmapBundle = wxBitmapBundle::FromSVG(ptr, svgDefaultSize);
}
delete fsfile;
}
#else
bitmapBundle = wxBitmapBundle::FromSVGFile(paramValue, svgDefaultSize);
#endif
#else // !wxHAS_SVG
ReportParamError
(
param,
"SVG bitmaps are not supported in this build of the library"
);
#endif // wxHAS_SVG/!wxHAS_SVG
}
}
else
{
if ( paramValue.Contains(".svg;") )
{
ReportParamError
(
param,
"may contain either one SVG file or a list of files separated by ';'"
);
return bitmapBundle;
}
// it is a bundle from bitmaps
wxVector<wxBitmap> bitmaps;
wxArrayString paths = wxSplit(paramValue, ';', '\0');
for ( wxArrayString::const_iterator i = paths.begin(); i != paths.end(); ++i )
{
wxBitmap bmpNext = LoadBitmapFromFS(this, *i, size, param);
if ( !bmpNext.IsOk() )
{
// error in loading wxBitmap, return invalid wxBitmapBundle
return bitmapBundle;
}
bitmaps.push_back(bmpNext);
}
bitmapBundle = wxBitmapBundle::FromBitmaps(bitmaps);
}
return bitmapBundle;
}
@@ -2182,17 +2318,17 @@ void XRCConvertFromDLU(wxWindow* w, T& value)
value = w->ConvertDialogToPixels(value);
}
// Helper for parsing values (of type T, for which XRCConvertFromAbsValue() and
// XRCConvertFromDLU() functions must be defined) which can be expressed either
// in pixels or dialog units.
// Helper for parsing strings which contain values (of type T, for which
// XRCConvertFromAbsValue() and XRCConvertFromDLU() functions must be defined)
// which can be expressed either in pixels or dialog units.
template <typename T>
T
ParseValueInPixels(wxXmlResourceHandlerImpl* impl,
ParseStringInPixels(wxXmlResourceHandlerImpl* impl,
const wxString& param,
const wxString& s,
const T& defaultValue,
wxWindow *windowToUse = NULL)
wxWindow *windowToUse)
{
const wxString s = impl->GetParamValue(param);
if ( s.empty() )
return defaultValue;
@@ -2235,6 +2371,17 @@ ParseValueInPixels(wxXmlResourceHandlerImpl* impl,
return value;
}
// Helper for parsing values which uses ParseStringInPixels() above.
template <typename T>
T
ParseValueInPixels(wxXmlResourceHandlerImpl* impl,
const wxString& param,
const T& defaultValue,
wxWindow *windowToUse = NULL)
{
return ParseStringInPixels(impl, param, impl->GetParamValue(param), defaultValue, windowToUse);
}
} // anonymous namespace
wxSize wxXmlResourceHandlerImpl::GetSize(const wxString& param,