Initial release
This commit is contained in:
+76
@@ -0,0 +1,76 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "fileresourceinputstream.h"
|
||||
|
||||
#if WINDOWS
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
PlatformResourceInputStreamPtr FileResourceInputStream::create (const std::string& path)
|
||||
{
|
||||
auto cstr = path.data ();
|
||||
if (auto handle = fopen (cstr, "rb"))
|
||||
return PlatformResourceInputStreamPtr (new FileResourceInputStream (handle));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
FileResourceInputStream::FileResourceInputStream (FILE* handle) : fileHandle (handle) {}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
FileResourceInputStream::~FileResourceInputStream () noexcept
|
||||
{
|
||||
fclose (fileHandle);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t FileResourceInputStream::readRaw (void* buffer, uint32_t size)
|
||||
{
|
||||
uint32_t readResult = static_cast<uint32_t> (fread (buffer, 1, size, fileHandle));
|
||||
if (readResult == 0)
|
||||
{
|
||||
if (ferror (fileHandle) != 0)
|
||||
{
|
||||
readResult = kStreamIOError;
|
||||
clearerr (fileHandle);
|
||||
}
|
||||
}
|
||||
return readResult;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int64_t FileResourceInputStream::seek (int64_t pos, SeekMode mode)
|
||||
{
|
||||
int whence;
|
||||
switch (mode)
|
||||
{
|
||||
case SeekMode::Set:
|
||||
whence = SEEK_SET;
|
||||
break;
|
||||
case SeekMode::Current:
|
||||
whence = SEEK_CUR;
|
||||
break;
|
||||
case SeekMode::End:
|
||||
default:
|
||||
whence = SEEK_END;
|
||||
break;
|
||||
}
|
||||
if (fseeko (fileHandle, pos, whence) == 0)
|
||||
return tell ();
|
||||
return kStreamSeekError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int64_t FileResourceInputStream::tell ()
|
||||
{
|
||||
return ftello (fileHandle);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformresourceinputstream.h"
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class FileResourceInputStream : public IPlatformResourceInputStream
|
||||
{
|
||||
public:
|
||||
static PlatformResourceInputStreamPtr create (const std::string& path);
|
||||
|
||||
private:
|
||||
FileResourceInputStream (FILE* handle);
|
||||
~FileResourceInputStream () noexcept override;
|
||||
|
||||
uint32_t readRaw (void* buffer, uint32_t size) override;
|
||||
int64_t seek (int64_t pos, SeekMode mode) override;
|
||||
int64_t tell () override;
|
||||
|
||||
FILE* fileHandle;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+754
@@ -0,0 +1,754 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "genericoptionmenu.h"
|
||||
|
||||
#include "../../animation/animations.h"
|
||||
#include "../../animation/timingfunctions.h"
|
||||
#include "../../cdatabrowser.h"
|
||||
#include "../../cfont.h"
|
||||
#include "../../cframe.h"
|
||||
#include "../../cgraphicspath.h"
|
||||
#include "../../clayeredviewcontainer.h"
|
||||
#include "../../coffscreencontext.h"
|
||||
#include "../../controls/coptionmenu.h"
|
||||
#include "../../controls/cscrollbar.h"
|
||||
#include "../../cvstguitimer.h"
|
||||
#include "../../events.h"
|
||||
#include "../../idatabrowserdelegate.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace GenericOptionMenuDetail {
|
||||
|
||||
using ClickCallback = std::function<void (COptionMenu* menu, int32_t itemIndex)>;
|
||||
|
||||
class DataSource;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template <typename Proc>
|
||||
CView* setupGenericOptionMenu (Proc clickCallback, CViewContainer* container,
|
||||
COptionMenu* optionMenu, GenericOptionMenuTheme& theme,
|
||||
CRect viewRect, DataSource* parentDataSource);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class DataSource : public DataBrowserDelegateAdapter,
|
||||
public IMouseObserver,
|
||||
public NonAtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
DataSource (CViewContainer* mainContainer, COptionMenu* menu,
|
||||
const ClickCallback& clickCallback, GenericOptionMenuTheme theme,
|
||||
DataSource* parentDataSource)
|
||||
: mainContainer (mainContainer)
|
||||
, menu (menu)
|
||||
, parentDataSource (parentDataSource)
|
||||
, clickCallback (clickCallback)
|
||||
, theme (theme)
|
||||
{
|
||||
vstgui_assert (menu->getNbEntries () > 0);
|
||||
}
|
||||
|
||||
CCoord dbGetRowHeight (CDataBrowser* browser) override
|
||||
{
|
||||
return std::ceil (theme.font->getSize () + 8);
|
||||
}
|
||||
|
||||
CCoord calculateMaxWidth (CFrame* frame)
|
||||
{
|
||||
if (maxWidth >= 0.)
|
||||
return maxWidth;
|
||||
auto context = COffscreenContext::create ({1., 1.});
|
||||
context->setFont (theme.font);
|
||||
maxWidth = 0.;
|
||||
maxTitleWidth = 0.;
|
||||
hasRightMargin = false;
|
||||
for (auto& item : *menu->getItems ())
|
||||
{
|
||||
if (item->isSeparator ())
|
||||
continue;
|
||||
auto width = context->getStringWidth (item->getTitle ());
|
||||
hasRightMargin |= item->getSubmenu () ? true : false;
|
||||
hasRightMargin |= item->getIcon () ? true : false;
|
||||
if (maxTitleWidth < width)
|
||||
maxTitleWidth = width;
|
||||
}
|
||||
maxWidth = maxTitleWidth + getCheckmarkWidth () * 2.;
|
||||
if (hasRightMargin)
|
||||
maxWidth += getSubmenuIndicatorWidth ();
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
CCoord calculateMaxHeight () { return menu->getNbEntries () * dbGetHeaderHeight (nullptr); }
|
||||
|
||||
bool setMaxWidth (CCoord width)
|
||||
{
|
||||
vstgui_assert (maxWidth >= 0.);
|
||||
auto minWidth = getCheckmarkWidth () * 2.;
|
||||
if (hasRightMargin)
|
||||
minWidth += getSubmenuIndicatorWidth ();
|
||||
if (minWidth > width)
|
||||
return false;
|
||||
if (minWidth + maxTitleWidth < width)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
maxWidth = width;
|
||||
maxTitleWidth = maxWidth - minWidth;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int32_t ViewRemoved = -2;
|
||||
|
||||
void dbAttached (CDataBrowser* browser) override
|
||||
{
|
||||
db = browser;
|
||||
db->getFrame ()->registerMouseObserver (this);
|
||||
}
|
||||
|
||||
void dbRemoved (CDataBrowser* browser) override
|
||||
{
|
||||
vstgui_assert (db == browser, "unexpected");
|
||||
closeSubMenu (false);
|
||||
db->getFrame ()->unregisterMouseObserver (this);
|
||||
db = nullptr;
|
||||
clickCallback (menu, ViewRemoved);
|
||||
}
|
||||
|
||||
void onMouseEntered (CView* view, CFrame* frame) override
|
||||
{
|
||||
if (view == subMenuView)
|
||||
{
|
||||
if (selectedRow >= 0)
|
||||
db->setSelectedRow (selectedRow);
|
||||
}
|
||||
}
|
||||
|
||||
void onMouseExited (CView* view, CFrame* frame) override
|
||||
{
|
||||
if (view != db)
|
||||
return;
|
||||
selectedRow = db->getSelectedRow ();
|
||||
db->setSelectedRow (CDataBrowser::kNoSelection);
|
||||
db->getFrame ()->doAfterEventProcessing ([this] () {
|
||||
if (db->getSelectedRow () == CDataBrowser::kNoSelection && subMenuView)
|
||||
{
|
||||
closeSubMenu ();
|
||||
}
|
||||
});
|
||||
}
|
||||
void onMouseEvent (MouseEvent& event, CFrame* frame) override {}
|
||||
|
||||
int32_t dbGetNumRows (CDataBrowser* browser) override { return menu->getNbEntries (); }
|
||||
int32_t dbGetNumColumns (CDataBrowser* browser) override { return 1; }
|
||||
CCoord dbGetCurrentColumnWidth (int32_t index, CDataBrowser* browser) override
|
||||
{
|
||||
return browser->getWidth ();
|
||||
}
|
||||
|
||||
void dbDrawHeader (CDrawContext*, const CRect&, int32_t, int32_t, CDataBrowser*) override {}
|
||||
|
||||
void alterSelection (int32_t index, int32_t direction)
|
||||
{
|
||||
if (index == CDataBrowser::kNoSelection)
|
||||
{
|
||||
if (direction == 1)
|
||||
index = -1;
|
||||
else
|
||||
index = menu->getNbEntries ();
|
||||
}
|
||||
index += direction;
|
||||
if (auto item = menu->getEntry (index))
|
||||
{
|
||||
if (item->isEnabled () && !item->isSeparator () && !item->isTitle ())
|
||||
{
|
||||
closeSubMenu ();
|
||||
db->setSelectedRow (index, true);
|
||||
}
|
||||
else
|
||||
alterSelection (index, direction);
|
||||
}
|
||||
}
|
||||
|
||||
void dbOnKeyboardEvent (KeyboardEvent& event, CDataBrowser* browser) override
|
||||
{
|
||||
if (event.type != EventType::KeyDown || event.character != 0 || !event.modifiers.empty ())
|
||||
return;
|
||||
switch (event.virt)
|
||||
{
|
||||
default: return;
|
||||
case VirtualKey::Down:
|
||||
{
|
||||
alterSelection (browser->getSelectedRow (), 1);
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case VirtualKey::Up:
|
||||
{
|
||||
alterSelection (browser->getSelectedRow (), -1);
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case VirtualKey::Escape:
|
||||
{
|
||||
clickCallback (menu, CDataBrowser::kNoSelection);
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case VirtualKey::Return:
|
||||
case VirtualKey::Enter:
|
||||
{
|
||||
if (clickCallback)
|
||||
clickCallback (menu, browser->getSelectedRow ());
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case VirtualKey::Left:
|
||||
{
|
||||
if (parentDataSource)
|
||||
{
|
||||
parentDataSource->closeSubMenu ();
|
||||
event.consumed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case VirtualKey::Right:
|
||||
{
|
||||
auto row = db->getSelectedRow ();
|
||||
if (auto item = menu->getEntry (row))
|
||||
{
|
||||
if (item->getSubmenu ())
|
||||
{
|
||||
auto r = db->getCellBounds ({row, 0});
|
||||
openSubMenu (item, r);
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CMouseEventResult dbOnMouseMoved (const CPoint& where, const CButtonState& buttons, int32_t row,
|
||||
int32_t column, CDataBrowser* browser) override
|
||||
{
|
||||
if (auto item = menu->getEntry (row))
|
||||
{
|
||||
if (browser->getSelectedRow () != row)
|
||||
{
|
||||
closeSubMenu ();
|
||||
if (item->isSeparator () || !item->isEnabled () || item->isTitle ())
|
||||
browser->setSelectedRow (CDataBrowser::kNoSelection);
|
||||
else
|
||||
{
|
||||
browser->setSelectedRow (row, true);
|
||||
auto r = browser->getCellBounds ({row, column});
|
||||
openSubMenu (item, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
CMouseEventResult dbOnMouseDown (const CPoint& where, const CButtonState& buttons, int32_t row,
|
||||
int32_t column, CDataBrowser* browser) override
|
||||
{
|
||||
if (auto item = menu->getEntry (row))
|
||||
{
|
||||
if (item->isTitle () || !item->isEnabled () || item->isSeparator ())
|
||||
browser->setSelectedRow (CDataBrowser::kNoSelection);
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
CMouseEventResult dbOnMouseUp (const CPoint& where, const CButtonState& buttons, int32_t row,
|
||||
int32_t column, CDataBrowser* browser) override
|
||||
{
|
||||
if (auto item = menu->getEntry (row))
|
||||
{
|
||||
if (!item->isSeparator () && !item->isTitle () && item->isEnabled () && clickCallback)
|
||||
clickCallback (menu, row);
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
void closeSubMenu (bool allowAnimation = true)
|
||||
{
|
||||
using namespace Animation;
|
||||
if (subMenuView)
|
||||
{
|
||||
if (!allowAnimation)
|
||||
{
|
||||
subMenuView->getParentView ()->asViewContainer ()->removeView (subMenuView);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto view = shared (subMenuView);
|
||||
subMenuView = nullptr;
|
||||
view->addAnimation (
|
||||
"AlphaAnimation", new AlphaValueAnimation (0.f, true),
|
||||
new CubicBezierTimingFunction (
|
||||
CubicBezierTimingFunction::easyOut (theme.menuAnimationTime)),
|
||||
[view] (CView*, const IdStringPtr, IAnimationTarget*) {
|
||||
if (view->isAttached ())
|
||||
view->getParentView ()->asViewContainer ()->removeView (view);
|
||||
});
|
||||
if (db)
|
||||
{
|
||||
if (auto frame = db->getFrame ())
|
||||
frame->setFocusView (db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openSubMenu (CMenuItem* item, CRect cellRect)
|
||||
{
|
||||
closeSubMenu ();
|
||||
if (auto subMenu = item->getSubmenu ())
|
||||
{
|
||||
auto callback = [this] (COptionMenu* m, int32_t index) {
|
||||
if (index != ViewRemoved)
|
||||
clickCallback (m, index);
|
||||
};
|
||||
db->translateToGlobal (cellRect, true);
|
||||
subMenuView =
|
||||
setupGenericOptionMenu (callback, mainContainer, subMenu, theme, cellRect, this);
|
||||
}
|
||||
}
|
||||
|
||||
void drawCheckMark (CDrawContext* context, CRect size, bool selected)
|
||||
{
|
||||
if (auto checkMarkPath = owned (context->createGraphicsPath ()))
|
||||
{
|
||||
CRect r (0., 0., size.getHeight () * 0.4, size.getHeight () * 0.4);
|
||||
r.centerInside (size);
|
||||
checkMarkPath->beginSubpath ({r.left, r.top + r.getHeight () / 2.});
|
||||
checkMarkPath->addLine ({r.left + r.getWidth () / 3., r.bottom});
|
||||
checkMarkPath->addLine ({r.right, r.top});
|
||||
context->setFrameColor (selected ? theme.selectedTextColor : theme.textColor);
|
||||
context->drawGraphicsPath (checkMarkPath, CDrawContext::kPathStroked);
|
||||
}
|
||||
}
|
||||
|
||||
void drawSubmenuIndicator (CDrawContext* context, CRect size, bool selected)
|
||||
{
|
||||
if (auto path = owned (context->createGraphicsPath ()))
|
||||
{
|
||||
CRect r = size;
|
||||
r.setWidth (r.getWidth () / 2.);
|
||||
r.setHeight (r.getHeight () / 2.);
|
||||
r.offset (size.getHeight () / 2., size.getHeight () / 4.);
|
||||
path->beginSubpath (r.getTopLeft ());
|
||||
path->addLine (r.getBottomLeft ());
|
||||
path->addLine ({r.right, r.top + r.getHeight () / 2.});
|
||||
path->closeSubpath ();
|
||||
context->setFillColor (selected ? theme.selectedTextColor : theme.textColor);
|
||||
context->drawGraphicsPath (path, CDrawContext::kPathFilled);
|
||||
}
|
||||
}
|
||||
|
||||
void drawItemIcon (CDrawContext* context, CRect size, CBitmap* bitmap)
|
||||
{
|
||||
ConcatClip cc (*context, size);
|
||||
CRect iconRect;
|
||||
iconRect.setSize (bitmap->getSize ());
|
||||
iconRect.centerInside (size);
|
||||
bitmap->draw (context, iconRect);
|
||||
}
|
||||
|
||||
void dbDrawCell (CDrawContext* context, const CRect& size, int32_t row, int32_t column,
|
||||
int32_t flags, CDataBrowser* browser) override
|
||||
{
|
||||
if (auto item = menu->getEntry (row))
|
||||
{
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
if (item->isSeparator ())
|
||||
{
|
||||
context->setFillColor (theme.separatorColor);
|
||||
auto r = size;
|
||||
r.inset (0, r.getHeight () / 2);
|
||||
r.setHeight (1.);
|
||||
context->drawRect (r, kDrawFilled);
|
||||
return;
|
||||
}
|
||||
context->saveGlobalState ();
|
||||
if (flags & kRowSelected)
|
||||
{
|
||||
context->setFillColor (theme.selectedBackgroundColor);
|
||||
context->drawRect (size, kDrawFilled);
|
||||
context->setFontColor (theme.selectedTextColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
CColor c = item->isTitle () ?
|
||||
theme.titleTextColor :
|
||||
item->isEnabled () ? theme.textColor : theme.disabledTextColor;
|
||||
context->setFontColor (c);
|
||||
}
|
||||
if (item->isTitle ())
|
||||
context->setFont (theme.font, 0, kBoldFace);
|
||||
else
|
||||
context->setFont (theme.font);
|
||||
if (item->isChecked ())
|
||||
{
|
||||
auto r = size;
|
||||
r.setWidth (getCheckmarkWidth ());
|
||||
drawCheckMark (context, r, flags & kRowSelected);
|
||||
}
|
||||
auto r = size;
|
||||
CHoriTxtAlign textAlign = kLeftText;
|
||||
if (item->isTitle ())
|
||||
{
|
||||
textAlign = kCenterText;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.left += getCheckmarkWidth ();
|
||||
r.setWidth (maxTitleWidth);
|
||||
}
|
||||
{
|
||||
ConcatClip cc (*context, r);
|
||||
context->drawString (item->getTitle ().getPlatformString (), r, textAlign);
|
||||
}
|
||||
r.right = size.right - getCheckmarkWidth () / 2.;
|
||||
r.left = r.right - getSubmenuIndicatorWidth ();
|
||||
if (item->getSubmenu ())
|
||||
{
|
||||
drawSubmenuIndicator (context, r, flags & kRowSelected);
|
||||
}
|
||||
else if (auto icon = item->getIcon ())
|
||||
{
|
||||
drawItemIcon (context, r, icon);
|
||||
}
|
||||
context->restoreGlobalState ();
|
||||
}
|
||||
}
|
||||
|
||||
CCoord getCheckmarkWidth ()
|
||||
{
|
||||
if (checkmarkSize == 0.)
|
||||
checkmarkSize = theme.font->getSize () * 1.6;
|
||||
return checkmarkSize;
|
||||
}
|
||||
CCoord getSubmenuIndicatorWidth () { return dbGetHeaderHeight (nullptr); }
|
||||
|
||||
CViewContainer* mainContainer;
|
||||
COptionMenu* menu;
|
||||
CDataBrowser* db {nullptr};
|
||||
CView* subMenuView {nullptr};
|
||||
DataSource* parentDataSource {nullptr};
|
||||
ClickCallback clickCallback;
|
||||
CCoord checkmarkSize {0.};
|
||||
CCoord maxWidth {-1.};
|
||||
CCoord maxTitleWidth {-1.};
|
||||
int32_t selectedRow {-1};
|
||||
bool hasRightMargin {false};
|
||||
GenericOptionMenuTheme theme;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline CColor makeDarkerColor (CColor baseColor)
|
||||
{
|
||||
auto color = baseColor;
|
||||
double h, s, l;
|
||||
color.toHSL (h, s, l);
|
||||
l *= 0.7;
|
||||
color.fromHSL (h, s, l);
|
||||
return color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template <typename Proc>
|
||||
CView* setupGenericOptionMenu (Proc clickCallback, CViewContainer* container,
|
||||
COptionMenu* optionMenu, GenericOptionMenuTheme& theme,
|
||||
CRect viewRect, DataSource* parentDataSource)
|
||||
{
|
||||
auto frame = container->getFrame ();
|
||||
auto dataSource =
|
||||
makeOwned<DataSource> (container, optionMenu, clickCallback, theme, parentDataSource);
|
||||
auto maxWidth = dataSource->calculateMaxWidth (frame);
|
||||
if (parentDataSource)
|
||||
{
|
||||
viewRect.offset (viewRect.getWidth (), 0);
|
||||
viewRect.setWidth (maxWidth);
|
||||
}
|
||||
else if (optionMenu->isPopupStyle ())
|
||||
{
|
||||
auto offset = optionMenu->getValue () * dataSource->dbGetRowHeight (nullptr);
|
||||
viewRect.offset (0, -offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewRect.top = viewRect.bottom;
|
||||
}
|
||||
bool multipleCheck = optionMenu->isMultipleCheckStyle ();
|
||||
if (!multipleCheck && optionMenu->isCheckStyle ())
|
||||
{
|
||||
optionMenu->checkEntryAlone (static_cast<int32_t> (optionMenu->getValue ()));
|
||||
}
|
||||
viewRect.setHeight (dataSource->calculateMaxHeight ());
|
||||
if (viewRect.getWidth () < maxWidth)
|
||||
{
|
||||
viewRect.setWidth (maxWidth);
|
||||
}
|
||||
if (container)
|
||||
{
|
||||
auto frSize = container->getViewSize ();
|
||||
frSize.inset (theme.inset);
|
||||
|
||||
if (frSize.bottom < viewRect.bottom)
|
||||
{
|
||||
viewRect.offset (0, frSize.bottom - viewRect.bottom);
|
||||
}
|
||||
if (frSize.top > viewRect.top)
|
||||
{
|
||||
viewRect.offset (0, frSize.top - viewRect.top);
|
||||
}
|
||||
if (frSize.right < viewRect.right)
|
||||
{
|
||||
viewRect.offset (frSize.right - viewRect.right, 0);
|
||||
}
|
||||
if (frSize.left > viewRect.left)
|
||||
{
|
||||
viewRect.offset (frSize.left - viewRect.left, 0);
|
||||
}
|
||||
viewRect.bound (frSize);
|
||||
if (maxWidth > viewRect.getWidth ())
|
||||
dataSource->setMaxWidth (viewRect.getWidth ());
|
||||
}
|
||||
viewRect.makeIntegral ();
|
||||
viewRect.inset (-1, -1);
|
||||
viewRect.offset (1, 1);
|
||||
auto decorView = new CViewContainer (viewRect);
|
||||
decorView->setBackgroundColor (
|
||||
GenericOptionMenuDetail::makeDarkerColor (theme.backgroundColor));
|
||||
decorView->setBackgroundColorDrawStyle (kDrawStroked);
|
||||
viewRect.originize ();
|
||||
viewRect.inset (1., 1.);
|
||||
auto browser =
|
||||
new CDataBrowser (viewRect, dataSource,
|
||||
CDataBrowser::kDontDrawFrame | CDataBrowser::kVerticalScrollbar |
|
||||
CDataBrowser::kOverlayScrollbars,
|
||||
2);
|
||||
if (auto sv = browser->getVerticalScrollbar ())
|
||||
{
|
||||
sv->setBackgroundColor (kTransparentCColor);
|
||||
sv->setFrameColor (kTransparentCColor);
|
||||
sv->setScrollerColor (theme.textColor);
|
||||
}
|
||||
browser->setBackgroundColor (theme.backgroundColor);
|
||||
decorView->addView (browser);
|
||||
|
||||
container->addView (decorView);
|
||||
|
||||
if (frame)
|
||||
frame->setFocusView (browser);
|
||||
|
||||
using namespace Animation;
|
||||
decorView->setAlphaValue (0.f);
|
||||
decorView->addAnimation ("AlphaAnimation", new AlphaValueAnimation (1.f, true),
|
||||
new CubicBezierTimingFunction (
|
||||
CubicBezierTimingFunction::easyIn (theme.menuAnimationTime / 2)));
|
||||
if (!parentDataSource && optionMenu->isCheckStyle ())
|
||||
{
|
||||
browser->makeRowVisible (static_cast<int32_t> (optionMenu->getValue ()));
|
||||
}
|
||||
return decorView;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct GenericOptionMenu::Impl
|
||||
{
|
||||
using ContainerT = CLayeredViewContainer;
|
||||
SharedPointer<CFrame> frame;
|
||||
SharedPointer<COptionMenu> menu;
|
||||
SharedPointer<ContainerT> container;
|
||||
SharedPointer<CVSTGUITimer> mouseUpTimer;
|
||||
Optional<ModalViewSessionID> modalViewSession;
|
||||
IGenericOptionMenuListener* listener {nullptr};
|
||||
GenericOptionMenuTheme theme;
|
||||
Callback callback;
|
||||
MouseEventButtonState initialButtonState;
|
||||
bool focusDrawingWasEnabled {false};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
GenericOptionMenu::GenericOptionMenu (CFrame* frame, MouseEventButtonState initialButtons,
|
||||
GenericOptionMenuTheme theme)
|
||||
{
|
||||
auto frameSize = frame->getViewSize ();
|
||||
frame->getTransform ().inverse ().transform (frameSize);
|
||||
frameSize.originize ();
|
||||
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
impl->frame = frame;
|
||||
impl->theme = theme;
|
||||
impl->container = new Impl::ContainerT (frameSize);
|
||||
impl->container->setZIndex (100);
|
||||
impl->container->setTransparency (true);
|
||||
impl->container->registerViewEventListener (this);
|
||||
impl->modalViewSession = impl->frame->beginModalViewSession (impl->container);
|
||||
impl->focusDrawingWasEnabled = impl->frame->focusDrawingEnabled ();
|
||||
impl->frame->setFocusDrawingEnabled (false);
|
||||
impl->initialButtonState = initialButtons;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
GenericOptionMenu::~GenericOptionMenu () noexcept
|
||||
{
|
||||
impl->frame->setFocusDrawingEnabled (impl->focusDrawingWasEnabled);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GenericOptionMenu::setListener (IGenericOptionMenuListener* listener)
|
||||
{
|
||||
impl->listener = listener;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GenericOptionMenu::removeModalView (PlatformOptionMenuResult result)
|
||||
{
|
||||
using namespace Animation;
|
||||
if (impl->callback)
|
||||
{
|
||||
if (impl->listener)
|
||||
impl->listener->optionMenuPopupStopped ();
|
||||
|
||||
auto self = shared (this);
|
||||
impl->container->addAnimation (
|
||||
"OptionMenuDone", new AlphaValueAnimation (0.f, true),
|
||||
new CubicBezierTimingFunction (
|
||||
CubicBezierTimingFunction::easyOut (impl->theme.menuAnimationTime)),
|
||||
[self, result] (CView*, const IdStringPtr, IAnimationTarget*) {
|
||||
if (!self->impl->container)
|
||||
return;
|
||||
auto callback = std::move (self->impl->callback);
|
||||
self->impl->callback = nullptr;
|
||||
self->impl->container->unregisterViewEventListener (self);
|
||||
if (self->impl->modalViewSession)
|
||||
{
|
||||
self->impl->frame->endModalViewSession (*self->impl->modalViewSession);
|
||||
self->impl->modalViewSession = {};
|
||||
}
|
||||
callback (self->impl->menu, result);
|
||||
self->impl->frame->setFocusView (self->impl->menu);
|
||||
self->impl->container = nullptr;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GenericOptionMenu::viewOnEvent (CView* view, Event& event)
|
||||
{
|
||||
if (event.type == EventType::MouseDown)
|
||||
{
|
||||
if (auto container = view->asViewContainer ())
|
||||
{
|
||||
auto& downEvent = castMouseDownEvent (event);
|
||||
CViewContainer::ViewList views;
|
||||
if (container->getViewsAt (downEvent.mousePosition, views, GetViewOptions ().deep ().includeInvisible ()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto self = shared (this);
|
||||
self->removeModalView ({nullptr, -1});
|
||||
downEvent.ignoreFollowUpMoveAndUpEvents (true);
|
||||
downEvent.consumed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (event.type == EventType::MouseUp)
|
||||
{
|
||||
auto& upEvent = castMouseUpEvent (event);
|
||||
if (impl->initialButtonState == upEvent.buttonState && !impl->mouseUpTimer)
|
||||
{
|
||||
if (auto container = view->asViewContainer ())
|
||||
{
|
||||
CViewContainer::ViewList views;
|
||||
if (container->getViewsAt (upEvent.mousePosition, views, GetViewOptions ().deep ().includeInvisible ()))
|
||||
{
|
||||
auto pos = upEvent.mousePosition;
|
||||
view->translateToGlobal (pos);
|
||||
MouseDownEvent downEvent;
|
||||
downEvent.buttonState = upEvent.buttonState;
|
||||
downEvent.clickCount = 1;
|
||||
for (auto& v : views)
|
||||
{
|
||||
downEvent.mousePosition = pos;
|
||||
v->translateToLocal (downEvent.mousePosition);
|
||||
v->dispatchEvent (downEvent);
|
||||
if (downEvent.consumed)
|
||||
{
|
||||
upEvent.mousePosition = downEvent.mousePosition;
|
||||
v->dispatchEvent (upEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto self = shared (this);
|
||||
self->removeModalView ({nullptr, -1});
|
||||
upEvent.ignoreFollowUpMoveAndUpEvents (true);
|
||||
upEvent.consumed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GenericOptionMenu::popup (COptionMenu* optionMenu, const Callback& callback)
|
||||
{
|
||||
impl->menu = optionMenu;
|
||||
impl->callback = callback;
|
||||
|
||||
auto self = shared (this);
|
||||
auto clickCallback = [self] (COptionMenu* menu, int32_t index) {
|
||||
self->impl->container->unregisterViewEventListener (self);
|
||||
self->removeModalView ({menu, index});
|
||||
};
|
||||
|
||||
auto viewRect = optionMenu->translateToGlobal (optionMenu->getViewSize (), true);
|
||||
auto where = viewRect.getCenter ();
|
||||
|
||||
GenericOptionMenuDetail::setupGenericOptionMenu (clickCallback, impl->container, optionMenu,
|
||||
impl->theme, viewRect, nullptr);
|
||||
|
||||
if (auto view = impl->frame->getViewAt (where, GetViewOptions ().deep ().includeInvisible ()))
|
||||
{
|
||||
if (!impl->initialButtonState.empty ())
|
||||
{
|
||||
MouseMoveEvent moveEvent;
|
||||
moveEvent.buttonState = impl->initialButtonState;
|
||||
impl->frame->getCurrentMouseLocation (moveEvent.mousePosition);
|
||||
view->translateToLocal (moveEvent.mousePosition);
|
||||
view->dispatchEvent (moveEvent);
|
||||
}
|
||||
}
|
||||
if (!impl->initialButtonState.empty ())
|
||||
{
|
||||
impl->mouseUpTimer = makeOwned<CVSTGUITimer> (
|
||||
[this] (CVSTGUITimer*) {
|
||||
impl->mouseUpTimer = nullptr;
|
||||
if (!impl->container ||
|
||||
impl->frame->getCurrentMouseButtons ().getButtonState () == 0)
|
||||
return;
|
||||
},
|
||||
200);
|
||||
}
|
||||
if (impl->listener)
|
||||
impl->listener->optionMenuPopupStarted ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,66 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformoptionmenu.h"
|
||||
|
||||
#include "../../ccolor.h"
|
||||
#include "../../cfont.h"
|
||||
#include "../../events.h"
|
||||
#include "../../iviewlistener.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct GenericOptionMenuTheme
|
||||
{
|
||||
SharedPointer<CFontDesc> font {kSystemFont};
|
||||
CColor backgroundColor {MakeCColor (0x39, 0x3c, 0x3f, 252)};
|
||||
CColor selectedBackgroundColor {MakeCColor (200, 200, 200, 235)};
|
||||
CColor textColor {MakeCColor (255, 255, 255, 255)};
|
||||
CColor selectedTextColor {MakeCColor (0, 0, 0, 255)};
|
||||
CColor disabledTextColor {MakeCColor (150, 150, 150, 255)};
|
||||
CColor titleTextColor {MakeCColor (150, 150, 150, 255)};
|
||||
CColor separatorColor {MakeCColor (100, 100, 100, 255)};
|
||||
CPoint inset {6., 6.};
|
||||
uint32_t menuAnimationTime {240};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct IGenericOptionMenuListener
|
||||
{
|
||||
virtual ~IGenericOptionMenuListener () noexcept = default;
|
||||
|
||||
virtual void optionMenuPopupStarted () = 0;
|
||||
virtual void optionMenuPopupStopped () = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class GenericOptionMenu
|
||||
: public IPlatformOptionMenu
|
||||
, public ViewEventListenerAdapter
|
||||
{
|
||||
public:
|
||||
GenericOptionMenu (CFrame* frame, MouseEventButtonState initialButtons,
|
||||
GenericOptionMenuTheme theme = {});
|
||||
~GenericOptionMenu () noexcept override;
|
||||
|
||||
void setListener (IGenericOptionMenuListener* listener);
|
||||
|
||||
void popup (COptionMenu* optionMenu, const Callback& callback) override;
|
||||
|
||||
private:
|
||||
void removeModalView (PlatformOptionMenuResult result);
|
||||
void viewOnEvent (CView* view, Event& event) override;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+868
@@ -0,0 +1,868 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "generictextedit.h"
|
||||
#include "../iplatformfont.h"
|
||||
#include "../iplatformframe.h"
|
||||
#include "../../controls/ctextlabel.h"
|
||||
#include "../../cframe.h"
|
||||
#include "../../cvstguitimer.h"
|
||||
#include "../../cdropsource.h"
|
||||
#include "../../events.h"
|
||||
#include "../../cdrawcontext.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
#define VSTGUI_STB_TEXTEDIT_USE_UNICODE 1
|
||||
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900 && _MSC_VER < 1920
|
||||
using STB_CharT = wchar_t;
|
||||
#else
|
||||
using STB_CharT = char16_t;
|
||||
#endif
|
||||
using StringConvert = std::wstring_convert<std::codecvt_utf8_utf16<STB_CharT>, STB_CharT>;
|
||||
#else
|
||||
using STB_CharT = char;
|
||||
#endif
|
||||
#define STB_TEXTEDIT_CHARTYPE STB_CharT
|
||||
#define STB_TEXTEDIT_POSITIONTYPE int
|
||||
#define STB_TEXTEDIT_STRING STBTextEditView
|
||||
#define STB_TEXTEDIT_KEYTYPE uint32_t
|
||||
|
||||
#include "../../../thirdparty/stb_textedit.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class STBTextEditView
|
||||
: public CTextLabel
|
||||
, public IKeyboardHook
|
||||
, public IMouseObserver
|
||||
{
|
||||
public:
|
||||
STBTextEditView (IPlatformTextEditCallback* callback);
|
||||
|
||||
void draw (CDrawContext* pContext) override;
|
||||
void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr) override;
|
||||
void setText (const UTF8String& txt) override;
|
||||
|
||||
void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) override;
|
||||
|
||||
void onMouseEntered (CView* view, CFrame* frame) override;
|
||||
void onMouseExited (CView* view, CFrame* frame) override;
|
||||
void onMouseEvent (MouseEvent& event, CFrame* frame) override;
|
||||
|
||||
bool attached (CView* parent) override;
|
||||
bool removed (CView* parent) override;
|
||||
void drawStyleChanged () override;
|
||||
|
||||
void selectAll ();
|
||||
bool doCut ();
|
||||
bool doCopy ();
|
||||
bool doPaste ();
|
||||
|
||||
static int deleteChars (STBTextEditView* self, size_t pos, size_t num);
|
||||
static int insertChars (STBTextEditView* self, size_t pos, const STB_CharT* text, size_t num);
|
||||
static void layout (StbTexteditRow* row, STBTextEditView* self, int start_i);
|
||||
static float getCharWidth (STBTextEditView* self, int n, int i);
|
||||
static STB_CharT getChar (STBTextEditView* self, int pos);
|
||||
static int getLength (STBTextEditView* self);
|
||||
|
||||
private:
|
||||
using CTextLabel::onKeyboardEvent;
|
||||
using CTextLabel::onMouseEntered;
|
||||
using CTextLabel::onMouseExited;
|
||||
using CTextLabel::onMouseMoved;
|
||||
using CTextLabel::onMouseDown;
|
||||
|
||||
template<typename Proc>
|
||||
bool callSTB (Proc proc);
|
||||
void onStateChanged ();
|
||||
void onTextChange ();
|
||||
void fillCharWidthCache ();
|
||||
void calcCursorSizes ();
|
||||
CCoord getCharWidth (STB_CharT c, STB_CharT pc) const;
|
||||
|
||||
static constexpr auto BitRecursiveKeyGuard = 1 << 0;
|
||||
static constexpr auto BitBlinkToggle = 1 << 1;
|
||||
static constexpr auto BitCursorIsSet = 1 << 2;
|
||||
static constexpr auto BitCursorSizesValid = 1 << 3;
|
||||
static constexpr auto BitNotifyTextChange = 1 << 4;
|
||||
static constexpr auto BitMouseDownHandling = 1 << 5;
|
||||
|
||||
bool isRecursiveKeyEventGuard () const { return hasBit (flags, BitRecursiveKeyGuard); }
|
||||
bool isBlinkToggle () const { return hasBit (flags, BitBlinkToggle); }
|
||||
bool isCursorSet () const { return hasBit (flags, BitCursorIsSet); }
|
||||
bool cursorSizesValid () const { return hasBit (flags, BitCursorSizesValid); }
|
||||
bool notifyTextChange () const { return hasBit (flags, BitNotifyTextChange); }
|
||||
bool mouseDownHandling () const { return hasBit (flags, BitMouseDownHandling); }
|
||||
|
||||
void setRecursiveKeyEventGuard (bool state) { setBit (flags, BitRecursiveKeyGuard, state); }
|
||||
void setBlinkToggle (bool state) { setBit (flags, BitBlinkToggle, state); }
|
||||
void setCursorIsSet (bool state) { setBit (flags, BitCursorIsSet, state); }
|
||||
void setCursorSizesValid (bool state) { setBit (flags, BitCursorSizesValid, state); }
|
||||
void setNotifyTextChange (bool state) { setBit (flags, BitNotifyTextChange, state); }
|
||||
void setMouseDownHandling (bool state) { setBit (flags, BitMouseDownHandling, state); }
|
||||
|
||||
SharedPointer<CVSTGUITimer> blinkTimer;
|
||||
IPlatformTextEditCallback* callback;
|
||||
STB_TexteditState editState;
|
||||
std::vector<CCoord> charWidthCache;
|
||||
CColor selectionColor{kBlueCColor};
|
||||
CCoord cursorOffset{0.};
|
||||
CCoord cursorHeight{0.};
|
||||
uint32_t flags{0};
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
std::u16string uString;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#define VIRTUAL_KEY_BIT 0x80000000
|
||||
#define STB_TEXTEDIT_K_SHIFT 0x40000000
|
||||
#define STB_TEXTEDIT_K_CONTROL 0x20000000
|
||||
#define STB_TEXTEDIT_K_ALT 0x10000000
|
||||
// key-bindings
|
||||
#define STB_TEXTEDIT_K_LEFT (VIRTUAL_KEY_BIT | VKEY_LEFT)
|
||||
#define STB_TEXTEDIT_K_RIGHT (VIRTUAL_KEY_BIT | VKEY_RIGHT)
|
||||
#define STB_TEXTEDIT_K_UP (VIRTUAL_KEY_BIT | VKEY_UP)
|
||||
#define STB_TEXTEDIT_K_DOWN (VIRTUAL_KEY_BIT | VKEY_DOWN)
|
||||
#if MAC
|
||||
# define STB_TEXTEDIT_K_LINESTART (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_LEFT)
|
||||
# define STB_TEXTEDIT_K_LINEEND (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_RIGHT)
|
||||
# define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_ALT | STB_TEXTEDIT_K_LEFT)
|
||||
# define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_ALT | STB_TEXTEDIT_K_RIGHT)
|
||||
# define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_UP)
|
||||
# define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_DOWN)
|
||||
#else
|
||||
# define STB_TEXTEDIT_K_LINESTART (VIRTUAL_KEY_BIT | VKEY_HOME)
|
||||
# define STB_TEXTEDIT_K_LINEEND (VIRTUAL_KEY_BIT | VKEY_END)
|
||||
# define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL)
|
||||
# define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL)
|
||||
# define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL)
|
||||
# define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL)
|
||||
#endif
|
||||
#define STB_TEXTEDIT_K_DELETE (VIRTUAL_KEY_BIT | VKEY_DELETE)
|
||||
#define STB_TEXTEDIT_K_BACKSPACE (VIRTUAL_KEY_BIT | VKEY_BACK)
|
||||
#define STB_TEXTEDIT_K_UNDO (STB_TEXTEDIT_K_CONTROL | 'z')
|
||||
#define STB_TEXTEDIT_K_REDO (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_SHIFT | 'z')
|
||||
#define STB_TEXTEDIT_K_INSERT (VIRTUAL_KEY_BIT | VKEY_INSERT)
|
||||
#define STB_TEXTEDIT_K_PGUP (VIRTUAL_KEY_BIT | VKEY_PAGEUP)
|
||||
#define STB_TEXTEDIT_K_PGDOWN (VIRTUAL_KEY_BIT | VKEY_PAGEDOWN)
|
||||
// functions
|
||||
#define STB_TEXTEDIT_STRINGLEN(tc) STBTextEditView::getLength (tc)
|
||||
#define STB_TEXTEDIT_LAYOUTROW STBTextEditView::layout
|
||||
#define STB_TEXTEDIT_GETWIDTH(tc, n, i) STBTextEditView::getCharWidth (tc, n, i)
|
||||
#define STB_TEXTEDIT_KEYTOTEXT(key) \
|
||||
((key & VIRTUAL_KEY_BIT) ? 0 : ((key & STB_TEXTEDIT_K_CONTROL) ? 0 : (key & (~0xF0000000))));
|
||||
#define STB_TEXTEDIT_GETCHAR(tc, i) STBTextEditView::getChar (tc, i)
|
||||
#define STB_TEXTEDIT_NEWLINE '\n'
|
||||
#define STB_TEXTEDIT_IS_SPACE(ch) isSpace (ch)
|
||||
#define STB_TEXTEDIT_DELETECHARS STBTextEditView::deleteChars
|
||||
#define STB_TEXTEDIT_INSERTCHARS STBTextEditView::insertChars
|
||||
|
||||
#define STB_TEXTEDIT_IMPLEMENTATION
|
||||
#include "../../../thirdparty/stb_textedit.h"
|
||||
#undef STB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct GenericTextEdit::Impl
|
||||
{
|
||||
STBTextEditView* view;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
GenericTextEdit::GenericTextEdit (IPlatformTextEditCallback* callback)
|
||||
: IPlatformTextEdit (callback)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
impl->view = new STBTextEditView (callback);
|
||||
auto view = dynamic_cast<CView*> (callback);
|
||||
vstgui_assert (view);
|
||||
view->getParentView ()->asViewContainer ()->addView (impl->view);
|
||||
|
||||
auto font = shared (callback->platformGetFont ());
|
||||
auto fontSize = font->getSize () / impl->view->getGlobalTransform ().m11;
|
||||
if (fontSize != font->getSize ())
|
||||
{
|
||||
font = makeOwned<CFontDesc> (*font);
|
||||
font->setSize (fontSize);
|
||||
}
|
||||
impl->view->setFont (font);
|
||||
impl->view->setFontColor (callback->platformGetFontColor ());
|
||||
impl->view->setTextInset (callback->platformGetTextInset ());
|
||||
impl->view->setHoriAlign (callback->platformGetHoriTxtAlign ());
|
||||
impl->view->setText (callback->platformGetText ());
|
||||
impl->view->selectAll ();
|
||||
|
||||
updateSize ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
GenericTextEdit::~GenericTextEdit () noexcept
|
||||
{
|
||||
if (impl->view->isAttached ())
|
||||
impl->view->getParentView ()->asViewContainer ()->removeView (impl->view);
|
||||
else
|
||||
impl->view->forget ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
UTF8String GenericTextEdit::getText ()
|
||||
{
|
||||
return impl->view->getText ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GenericTextEdit::setText (const UTF8String& text)
|
||||
{
|
||||
impl->view->setText (text);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GenericTextEdit::updateSize ()
|
||||
{
|
||||
auto r = textEdit->platformGetVisibleSize ();
|
||||
r = impl->view->translateToLocal (r);
|
||||
impl->view->setViewSize (r);
|
||||
impl->view->setMouseableArea (r);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
STBTextEditView::STBTextEditView (IPlatformTextEditCallback* callback)
|
||||
: CTextLabel ({}), callback (callback)
|
||||
{
|
||||
stb_textedit_initialize_state (&editState, true);
|
||||
setTransparency (true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename Proc>
|
||||
bool STBTextEditView::callSTB (Proc proc)
|
||||
{
|
||||
auto oldState = editState;
|
||||
proc ();
|
||||
if (memcmp (&oldState, &editState, sizeof (STB_TexteditState)) != 0)
|
||||
{
|
||||
onStateChanged ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::onKeyboardEvent (KeyboardEvent& event, CFrame* frame)
|
||||
{
|
||||
if (event.type == EventType::KeyUp)
|
||||
return;
|
||||
|
||||
if (isRecursiveKeyEventGuard ())
|
||||
return;
|
||||
auto selfGuard = SharedPointer<CBaseObject> (this);
|
||||
BitScopeToggleT<uint32_t, uint32_t> br (flags, BitRecursiveKeyGuard);
|
||||
callback->platformOnKeyboardEvent (event);
|
||||
if (event.consumed)
|
||||
return;
|
||||
|
||||
if (event.character == 0 && event.virt == VirtualKey::None)
|
||||
return;
|
||||
|
||||
if (event.modifiers.is (ModifierKey::Control))
|
||||
{
|
||||
switch (event.character)
|
||||
{
|
||||
case 'a':
|
||||
{
|
||||
selectAll ();
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case 'x':
|
||||
{
|
||||
if (doCut ())
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
if (doCopy ())
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
case 'v':
|
||||
{
|
||||
if (doPaste ())
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto key = event.character;
|
||||
if (key)
|
||||
{
|
||||
if (auto text = getFrame ()->getPlatformFrame ()->convertCurrentKeyEventToText ())
|
||||
{
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
auto tmp = StringConvert{}.from_bytes (text->getString ());
|
||||
key = tmp[0];
|
||||
#else
|
||||
if (text->length () != 1)
|
||||
return;
|
||||
key = text->getString ()[0];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (event.virt != VirtualKey::None)
|
||||
{
|
||||
switch (event.virt)
|
||||
{
|
||||
case VirtualKey::Space:
|
||||
{
|
||||
key = 0x20;
|
||||
break;
|
||||
}
|
||||
case VirtualKey::Tab:
|
||||
{
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
key = static_cast<uint32_t> (event.virt) | VIRTUAL_KEY_BIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.modifiers.has (ModifierKey::Control))
|
||||
key |= STB_TEXTEDIT_K_CONTROL;
|
||||
if (event.modifiers.has (ModifierKey::Alt))
|
||||
key |= STB_TEXTEDIT_K_ALT;
|
||||
if (event.modifiers.has (ModifierKey::Shift))
|
||||
key |= STB_TEXTEDIT_K_SHIFT;
|
||||
if (callSTB ([&]() { stb_textedit_key (this, &editState, key); }))
|
||||
event.consumed = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::onMouseEvent (MouseEvent& event, CFrame* frame)
|
||||
{
|
||||
if (event.buttonState.isLeft () == false)
|
||||
return;
|
||||
|
||||
if (getParentView ())
|
||||
{
|
||||
auto where = event.mousePosition;
|
||||
where = translateToLocal (where, true);
|
||||
if (mouseDownHandling () || hitTest (where, event))
|
||||
{
|
||||
where.x -= getViewSize ().left;
|
||||
where.y -= getViewSize ().top;
|
||||
switch (event.type)
|
||||
{
|
||||
case EventType::MouseDown:
|
||||
{
|
||||
setMouseDownHandling (true);
|
||||
callSTB ([&] () {
|
||||
stb_textedit_click (this, &editState, static_cast<float> (where.x),
|
||||
static_cast<float> (where.y));
|
||||
});
|
||||
event.consumed = true;
|
||||
break;
|
||||
}
|
||||
case EventType::MouseMove:
|
||||
{
|
||||
if (mouseDownHandling ())
|
||||
{
|
||||
callSTB ([&] () {
|
||||
stb_textedit_drag (this, &editState, static_cast<float> (where.x),
|
||||
static_cast<float> (where.y));
|
||||
});
|
||||
event.consumed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EventType::MouseUp:
|
||||
{
|
||||
if (mouseDownHandling ())
|
||||
{
|
||||
event.consumed = true;
|
||||
setMouseDownHandling (false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult STBTextEditView::onMouseDown (CFrame* frame,
|
||||
const CPoint& _where,
|
||||
const CButtonState& buttons)
|
||||
{
|
||||
auto where = _where;
|
||||
if (auto parent = getParentView ())
|
||||
{
|
||||
where = translateToLocal (where, true);
|
||||
if (buttons.isLeftButton () && hitTest (where, noEvent ()))
|
||||
{
|
||||
where.x -= getViewSize ().left;
|
||||
where.y -= getViewSize ().top;
|
||||
callSTB ([&]() {
|
||||
stb_textedit_click (this, &editState, static_cast<float> (where.x),
|
||||
static_cast<float> (where.y));
|
||||
});
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult STBTextEditView::onMouseMoved (CFrame* frame,
|
||||
const CPoint& _where,
|
||||
const CButtonState& buttons)
|
||||
{
|
||||
auto where = _where;
|
||||
if (auto parent = getParentView ())
|
||||
{
|
||||
where = translateToLocal (where, true);
|
||||
if (buttons.isLeftButton () && hitTest (where, noEvent ()))
|
||||
{
|
||||
where.x -= getViewSize ().left;
|
||||
where.y -= getViewSize ().top;
|
||||
callSTB ([&]() {
|
||||
stb_textedit_drag (this, &editState, static_cast<float> (where.x),
|
||||
static_cast<float> (where.y));
|
||||
});
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::onMouseEntered (CView* view, CFrame* frame)
|
||||
{
|
||||
if (view == this)
|
||||
{
|
||||
setCursorIsSet (true);
|
||||
getFrame ()->setCursor (kCursorIBeam);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::onMouseExited (CView* view, CFrame* frame)
|
||||
{
|
||||
if (view == this)
|
||||
{
|
||||
setCursorIsSet (false);
|
||||
getFrame ()->setCursor (kCursorDefault);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool STBTextEditView::attached (CView* parent)
|
||||
{
|
||||
if (auto frame = parent->getFrame ())
|
||||
{
|
||||
frame->registerMouseObserver (this);
|
||||
frame->registerKeyboardHook (this);
|
||||
selectionColor = frame->getFocusColor ();
|
||||
drawStyleChanged ();
|
||||
}
|
||||
return CTextLabel::attached (parent);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool STBTextEditView::removed (CView* parent)
|
||||
{
|
||||
if (auto frame = getFrame ())
|
||||
{
|
||||
blinkTimer = nullptr;
|
||||
frame->unregisterMouseObserver (this);
|
||||
frame->unregisterKeyboardHook (this);
|
||||
if (isCursorSet ())
|
||||
frame->setCursor (kCursorDefault);
|
||||
}
|
||||
return CTextLabel::removed (parent);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::drawStyleChanged ()
|
||||
{
|
||||
setCursorSizesValid (false);
|
||||
charWidthCache.clear ();
|
||||
CTextLabel::drawStyleChanged ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::selectAll ()
|
||||
{
|
||||
editState.select_start = 0;
|
||||
editState.select_end = static_cast<int> (getText ().length ());
|
||||
onStateChanged ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool STBTextEditView::doCut ()
|
||||
{
|
||||
if (doCopy ())
|
||||
{
|
||||
callSTB ([&]() { stb_textedit_cut (this, &editState); });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool STBTextEditView::doCopy ()
|
||||
{
|
||||
if (editState.select_start == editState.select_end)
|
||||
return false;
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
auto txt = StringConvert{}.to_bytes (reinterpret_cast<const STB_CharT*> (uString.data () + editState.select_start),
|
||||
reinterpret_cast<const STB_CharT*> (uString.data () + editState.select_end));
|
||||
auto dataPackage =
|
||||
CDropSource::create (txt.data (), static_cast<uint32_t> (txt.size ()), IDataPackage::kText);
|
||||
#else
|
||||
auto dataPackage =
|
||||
CDropSource::create (getText ().data (), getText ().length (), IDataPackage::kText);
|
||||
#endif
|
||||
getFrame ()->setClipboard (dataPackage);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool STBTextEditView::doPaste ()
|
||||
{
|
||||
if (auto clipboard = getFrame ()->getClipboard ())
|
||||
{
|
||||
auto count = clipboard->getCount ();
|
||||
for (auto i = 0u; i < count; ++i)
|
||||
{
|
||||
const void* buffer;
|
||||
IDataPackage::Type dataType;
|
||||
auto size = clipboard->getData (i, buffer, dataType);
|
||||
if (dataType == IDataPackage::kText)
|
||||
{
|
||||
auto text = reinterpret_cast<const char*> (buffer);
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
auto uText = StringConvert {}.from_bytes (text, text + size);
|
||||
callSTB ([&] () {
|
||||
stb_textedit_paste (this, &editState, uText.data (),
|
||||
static_cast<int> (uText.size ()));
|
||||
});
|
||||
#else
|
||||
callSTB ([&]() { stb_textedit_paste (this, &editState, text, size); });
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::onStateChanged ()
|
||||
{
|
||||
setBlinkToggle (true);
|
||||
if (isAttached ())
|
||||
{
|
||||
blinkTimer = makeOwned<CVSTGUITimer> (
|
||||
[&](CVSTGUITimer* timer) {
|
||||
setBlinkToggle (!isBlinkToggle ());
|
||||
if (editState.select_start == editState.select_end)
|
||||
invalid ();
|
||||
},
|
||||
500);
|
||||
}
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::setText (const UTF8String& txt)
|
||||
{
|
||||
charWidthCache.clear ();
|
||||
CTextLabel::setText (txt);
|
||||
if (editState.select_start != editState.select_end)
|
||||
selectAll ();
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
auto tmpStr = StringConvert{}.from_bytes (CTextLabel::getText ().getString ());
|
||||
uString = {tmpStr.data (), tmpStr.data () + tmpStr.size ()};
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CCoord STBTextEditView::getCharWidth (STB_CharT c, STB_CharT pc) const
|
||||
{
|
||||
auto platformFont = getFont ()->getPlatformFont ();
|
||||
vstgui_assert (platformFont);
|
||||
auto fontPainter = platformFont->getPainter ();
|
||||
vstgui_assert (fontPainter);
|
||||
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
if (pc)
|
||||
{
|
||||
UTF8String str (StringConvert{}.to_bytes (pc));
|
||||
auto pcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true);
|
||||
str += StringConvert{}.to_bytes (c);
|
||||
auto tcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true);
|
||||
return tcWidth - pcWidth;
|
||||
}
|
||||
UTF8String str (StringConvert{}.to_bytes (c));
|
||||
auto width = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true);
|
||||
return width / getGlobalTransform ().m11;
|
||||
#else
|
||||
if (pc)
|
||||
{
|
||||
UTF8String str (std::string (1, pc));
|
||||
auto pcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true);
|
||||
str += std::string (1, c);
|
||||
auto tcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true);
|
||||
return tcWidth - pcWidth;
|
||||
}
|
||||
|
||||
UTF8String str (std::string (1, c));
|
||||
return fontPainter->getStringWidth (nullptr, str.getPlatformString (), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::fillCharWidthCache ()
|
||||
{
|
||||
if (!charWidthCache.empty ())
|
||||
return;
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
auto num = uString.size ();
|
||||
charWidthCache.resize (num);
|
||||
for (auto i = 0u; i < num; ++i)
|
||||
charWidthCache[i] = getCharWidth (uString[i], i == 0 ? 0 : uString[i - 1]);
|
||||
#else
|
||||
auto num = getText ().length ();
|
||||
charWidthCache.resize (num);
|
||||
const auto& str = getText ().getString ();
|
||||
for (auto i = 0u; i < num; ++i)
|
||||
charWidthCache[i] = getCharWidth (str[i], i == 0 ? 0 : str[i - 1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::calcCursorSizes ()
|
||||
{
|
||||
if (cursorSizesValid ())
|
||||
return;
|
||||
|
||||
auto platformFont = getFont ()->getPlatformFont ();
|
||||
vstgui_assert (platformFont);
|
||||
|
||||
cursorHeight = platformFont->getAscent () + platformFont->getDescent ();
|
||||
auto viewHeight = getViewSize ().getHeight ();
|
||||
cursorOffset = (viewHeight / 2. - cursorHeight / 2.);
|
||||
setCursorSizesValid (true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::draw (CDrawContext* context)
|
||||
{
|
||||
fillCharWidthCache ();
|
||||
calcCursorSizes ();
|
||||
|
||||
drawBack (context, nullptr);
|
||||
drawPlatformText (context, getText ());
|
||||
|
||||
if (!isBlinkToggle () || editState.select_start != editState.select_end)
|
||||
return;
|
||||
|
||||
// draw cursor
|
||||
StbTexteditRow row{};
|
||||
layout (&row, this, 0);
|
||||
|
||||
context->setFillColor (getFontColor ());
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
CRect r = getViewSize ();
|
||||
r.setHeight (cursorHeight);
|
||||
r.offset (row.x0, cursorOffset);
|
||||
r.setWidth (1);
|
||||
for (auto i = 0; i < editState.cursor; ++i)
|
||||
r.offset (charWidthCache[i], 0);
|
||||
r.offset (-0.5, 0);
|
||||
context->drawRect (r, kDrawFilled);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::drawBack (CDrawContext* context, CBitmap* newBack)
|
||||
{
|
||||
CTextLabel::drawBack (context, newBack);
|
||||
|
||||
auto selStart = editState.select_start;
|
||||
auto selEnd = editState.select_end;
|
||||
if (selStart > selEnd)
|
||||
std::swap (selStart, selEnd);
|
||||
|
||||
if (selStart != selEnd)
|
||||
{
|
||||
StbTexteditRow row{};
|
||||
layout (&row, this, 0);
|
||||
|
||||
// draw selection
|
||||
CRect selection = getViewSize ();
|
||||
selection.setHeight (cursorHeight);
|
||||
selection.offset (row.x0, cursorOffset);
|
||||
selection.setWidth (0);
|
||||
auto index = 0;
|
||||
for (; index < selStart; ++index)
|
||||
selection.offset (charWidthCache[index], 0);
|
||||
for (; index < selEnd; ++index)
|
||||
selection.right += charWidthCache[index];
|
||||
context->setFillColor (selectionColor);
|
||||
context->drawRect (selection, kDrawFilled);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::onTextChange ()
|
||||
{
|
||||
if (notifyTextChange ())
|
||||
return;
|
||||
if (auto frame = getFrame ())
|
||||
{
|
||||
if (frame->inEventProcessing ())
|
||||
{
|
||||
setNotifyTextChange (true);
|
||||
auto self = shared (this);
|
||||
frame->doAfterEventProcessing ([self]() {
|
||||
self->setNotifyTextChange (false);
|
||||
self->callback->platformTextDidChange ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int STBTextEditView::deleteChars (STBTextEditView* self, size_t pos, size_t num)
|
||||
{
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
self->uString.erase (pos, num);
|
||||
self->setText (StringConvert{}.to_bytes (reinterpret_cast<const STB_CharT*> (self->uString.data ()), reinterpret_cast<const STB_CharT*> (self->uString.data () + self->uString.size ())));
|
||||
self->onTextChange ();
|
||||
return true;
|
||||
#else
|
||||
auto str = self->text.getString ();
|
||||
str.erase (pos, num);
|
||||
self->setText (str.data ());
|
||||
self->onTextChange ();
|
||||
return true; // success
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int STBTextEditView::insertChars (STBTextEditView* self,
|
||||
size_t pos,
|
||||
const STB_CharT* text,
|
||||
size_t num)
|
||||
{
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
self->uString.insert (pos, reinterpret_cast<const char16_t*> (text), num);
|
||||
self->setText (StringConvert{}.to_bytes (reinterpret_cast<const STB_CharT*> (self->uString.data ()), reinterpret_cast<const STB_CharT*> (self->uString.data () + self->uString.size ())));
|
||||
self->onTextChange ();
|
||||
return true;
|
||||
#else
|
||||
auto str = self->text.getString ();
|
||||
str.insert (pos, text, num);
|
||||
self->setText (str.data ());
|
||||
self->onTextChange ();
|
||||
return true; // success
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
STB_CharT STBTextEditView::getChar (STBTextEditView* self, int pos)
|
||||
{
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
return self->uString[pos];
|
||||
#else
|
||||
return self->getText ().getString ()[pos];
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int STBTextEditView::getLength (STBTextEditView* self)
|
||||
{
|
||||
#if VSTGUI_STB_TEXTEDIT_USE_UNICODE
|
||||
return static_cast<int> (self->uString.size ());
|
||||
#else
|
||||
return static_cast<int> (self->getText ().length ());
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STBTextEditView::layout (StbTexteditRow* row, STBTextEditView* self, int start_i)
|
||||
{
|
||||
vstgui_assert (start_i == 0);
|
||||
|
||||
self->fillCharWidthCache ();
|
||||
auto textWidth = static_cast<float> (
|
||||
std::accumulate (self->charWidthCache.begin (), self->charWidthCache.end (), 0.));
|
||||
|
||||
row->num_chars = static_cast<int> (self->getText ().length ());
|
||||
row->baseline_y_delta = 1.25;
|
||||
row->ymin = 0.f;
|
||||
row->ymax = static_cast<float> (self->getFont ()->getSize ());
|
||||
switch (self->getHoriAlign ())
|
||||
{
|
||||
case kLeftText:
|
||||
{
|
||||
row->x0 = static_cast<float> (self->getTextInset ().x);
|
||||
row->x1 = row->x0 + textWidth;
|
||||
break;
|
||||
}
|
||||
case kCenterText:
|
||||
{
|
||||
row->x0 =
|
||||
static_cast<float> ((self->getViewSize ().getWidth () / 2.) - (textWidth / 2.));
|
||||
row->x1 = row->x0 + textWidth;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
vstgui_assert (false, "Not Implemented !");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float STBTextEditView::getCharWidth (STBTextEditView* self, int n, int i)
|
||||
{
|
||||
self->fillCharWidthCache ();
|
||||
return static_cast<float> (self->charWidthCache[i]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformtextedit.h"
|
||||
#include <memory>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class GenericTextEdit : public IPlatformTextEdit
|
||||
{
|
||||
public:
|
||||
GenericTextEdit (IPlatformTextEditCallback* callback);
|
||||
~GenericTextEdit () noexcept;
|
||||
|
||||
UTF8String getText () override;
|
||||
bool setText (const UTF8String& text) override;
|
||||
bool updateSize () override;
|
||||
bool drawsPlaceholder () const override { return false; }
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformgradient.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class PlatformGradientBase : public IPlatformGradient
|
||||
{
|
||||
public:
|
||||
void setColorStops (const GradientColorStopMap& colorStops) override
|
||||
{
|
||||
map = colorStops;
|
||||
changed ();
|
||||
}
|
||||
void addColorStop (const GradientColorStop& colorStop) override
|
||||
{
|
||||
map.emplace (colorStop);
|
||||
changed ();
|
||||
}
|
||||
const GradientColorStopMap& getColorStops () const override
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
virtual void changed () {}
|
||||
private:
|
||||
GradientColorStopMap map;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformtaskexecutor.h"
|
||||
#include "../../vstguifwd.h"
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Tasks {
|
||||
namespace Detail {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ThreadPool
|
||||
{
|
||||
explicit ThreadPool (size_t numThreads) : workers (numThreads) {}
|
||||
|
||||
~ThreadPool () noexcept
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
stopThreads ();
|
||||
condition.notify_all ();
|
||||
joinAllThreads ();
|
||||
}
|
||||
}
|
||||
|
||||
void enqueue (Task&& task) noexcept
|
||||
{
|
||||
std::unique_lock<std::mutex> lock (queueMutex);
|
||||
vstgui_assert (!stop, "task is not executed, because the thread pool is already stopped");
|
||||
if (stop)
|
||||
return;
|
||||
++numTasks;
|
||||
taskQueue.emplace (std::move (task));
|
||||
if (!started)
|
||||
startThreads ();
|
||||
lock.unlock ();
|
||||
condition.notify_one ();
|
||||
}
|
||||
|
||||
bool empty () const noexcept { return numTasks == 0u; }
|
||||
|
||||
private:
|
||||
void startThreads () noexcept
|
||||
{
|
||||
started = true;
|
||||
for (size_t i = 0; i < workers.size (); ++i)
|
||||
{
|
||||
workers[i] = std::thread ([this] () { workerLoop (); });
|
||||
}
|
||||
}
|
||||
|
||||
void stopThreads () noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (queueMutex);
|
||||
stop = true;
|
||||
started = false;
|
||||
}
|
||||
|
||||
void joinAllThreads () noexcept
|
||||
{
|
||||
for (auto& worker : workers)
|
||||
{
|
||||
worker.join ();
|
||||
}
|
||||
}
|
||||
|
||||
void workerLoop () noexcept
|
||||
{
|
||||
while (!stop)
|
||||
{
|
||||
Task task;
|
||||
std::unique_lock<std::mutex> lock (queueMutex);
|
||||
condition.wait (lock, [this] () { return stop || !taskQueue.empty (); });
|
||||
if (!stop && !taskQueue.empty ())
|
||||
{
|
||||
task = std::move (taskQueue.front ());
|
||||
taskQueue.pop ();
|
||||
}
|
||||
lock.unlock ();
|
||||
if (task)
|
||||
{
|
||||
task ();
|
||||
--numTasks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::thread> workers;
|
||||
std::queue<Task> taskQueue;
|
||||
std::atomic<uint64_t> numTasks {0u};
|
||||
std::atomic<bool> stop {false};
|
||||
std::atomic<bool> started {false};
|
||||
std::mutex queueMutex;
|
||||
std::condition_variable condition;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct SerialQueue
|
||||
{
|
||||
SerialQueue (ThreadPool& pool, uint64_t inIdentifier, const char* inName)
|
||||
: threadPool (pool), identifier (inIdentifier), name (inName)
|
||||
{
|
||||
}
|
||||
|
||||
~SerialQueue () noexcept { vstgui_assert (empty (), "Serial Queue is destroyed non empty"); }
|
||||
|
||||
uint64_t getIdentifier () const noexcept { return identifier; }
|
||||
|
||||
void schedule (Task&& task) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (queueMutex);
|
||||
taskQueue.push (std::move (task));
|
||||
if (!scheduled)
|
||||
{
|
||||
scheduled = true;
|
||||
threadPool.enqueue ([this] () { runAndScheduleNextTask (); });
|
||||
}
|
||||
}
|
||||
|
||||
bool empty () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (queueMutex);
|
||||
return taskQueue.empty ();
|
||||
}
|
||||
|
||||
private:
|
||||
void runAndScheduleNextTask () noexcept
|
||||
{
|
||||
Task task;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (queueMutex);
|
||||
task = std::move (taskQueue.front ());
|
||||
taskQueue.pop ();
|
||||
}
|
||||
task ();
|
||||
std::lock_guard<std::mutex> lock (queueMutex);
|
||||
if (taskQueue.empty ())
|
||||
{
|
||||
scheduled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
threadPool.enqueue ([this] () { runAndScheduleNextTask (); });
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool& threadPool;
|
||||
uint64_t identifier;
|
||||
std::queue<Task> taskQueue;
|
||||
std::string name;
|
||||
std::atomic_bool scheduled {false};
|
||||
mutable std::mutex queueMutex;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Detail
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ThreadPoolTaskExecutor : IPlatformTaskExecutor
|
||||
{
|
||||
using SerialQueueVector = std::vector<std::unique_ptr<Detail::SerialQueue>>;
|
||||
|
||||
ThreadPoolTaskExecutor (PlatformTaskExecutorPtr&& inPlatformTaskExecutor)
|
||||
: backgroundQueue ({inPlatformTaskExecutor->getMainQueue ().identifier + 1})
|
||||
, platformTaskExecutor (std::move (inPlatformTaskExecutor))
|
||||
{
|
||||
queueIdentifierCounter = backgroundQueue.identifier;
|
||||
}
|
||||
|
||||
~ThreadPoolTaskExecutor () noexcept override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (serialQueueMutex);
|
||||
serialQueues.clear ();
|
||||
}
|
||||
|
||||
const Queue& getMainQueue () const override { return platformTaskExecutor->getMainQueue (); }
|
||||
|
||||
const Queue& getBackgroundQueue () const override { return backgroundQueue; }
|
||||
|
||||
Queue makeSerialQueue (const char* name) const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (serialQueueMutex);
|
||||
serialQueues.emplace_back (
|
||||
std::make_unique<Detail::SerialQueue> (threadPool, ++queueIdentifierCounter, name));
|
||||
return {queueIdentifierCounter};
|
||||
}
|
||||
|
||||
void releaseSerialQueue (const Queue& queue) const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (serialQueueMutex);
|
||||
auto it = findQueue (queue.identifier);
|
||||
if (it != serialQueues.end ())
|
||||
{
|
||||
waitAllTasksExecuted (it);
|
||||
serialQueues.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
void schedule (const Queue& queue, Task&& task) const override
|
||||
{
|
||||
if (queue == getMainQueue ())
|
||||
{
|
||||
platformTaskExecutor->schedule (queue, std::move (task));
|
||||
}
|
||||
else if (queue == backgroundQueue)
|
||||
{
|
||||
threadPool.enqueue (std::move (task));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (serialQueueMutex);
|
||||
auto it = findQueue (queue.identifier);
|
||||
if (it != serialQueues.end ())
|
||||
(*it)->schedule (std::move (task));
|
||||
}
|
||||
}
|
||||
|
||||
void waitAllTasksExecuted (const Queue& queue) const override
|
||||
{
|
||||
if (queue == getMainQueue ())
|
||||
{
|
||||
platformTaskExecutor->waitAllTasksExecuted (queue);
|
||||
}
|
||||
else if (queue == backgroundQueue)
|
||||
{
|
||||
while (!threadPool.empty ())
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (1));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (serialQueueMutex);
|
||||
auto it = findQueue (queue.identifier);
|
||||
if (it != serialQueues.end ())
|
||||
waitAllTasksExecuted (it);
|
||||
}
|
||||
}
|
||||
|
||||
void waitAllTasksExecuted () const override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (serialQueueMutex);
|
||||
for (auto it = serialQueues.begin (); it != serialQueues.end (); ++it)
|
||||
waitAllTasksExecuted (it);
|
||||
}
|
||||
waitAllTasksExecuted (backgroundQueue);
|
||||
waitAllTasksExecuted (getMainQueue ());
|
||||
platformTaskExecutor->waitAllTasksExecuted ();
|
||||
}
|
||||
|
||||
private:
|
||||
void waitAllTasksExecuted (SerialQueueVector::const_iterator it) const
|
||||
{
|
||||
while ((*it)->empty () == false)
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (1));
|
||||
}
|
||||
|
||||
SerialQueueVector::const_iterator findQueue (uint64_t identifier) const
|
||||
{
|
||||
return std::find_if (serialQueues.begin (), serialQueues.end (),
|
||||
[&] (const auto& el) { return el->getIdentifier (); });
|
||||
}
|
||||
|
||||
Queue backgroundQueue;
|
||||
PlatformTaskExecutorPtr platformTaskExecutor;
|
||||
mutable Detail::ThreadPool threadPool {std::thread::hardware_concurrency ()};
|
||||
mutable uint64_t queueIdentifierCounter {};
|
||||
mutable SerialQueueVector serialQueues;
|
||||
mutable std::mutex serialQueueMutex;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Tasks
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,46 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include <vector>
|
||||
|
||||
namespace VSTGUI {
|
||||
class IPlatformBitmapPixelAccess;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformBitmap : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
virtual const CPoint& getSize () const = 0;
|
||||
|
||||
virtual SharedPointer<IPlatformBitmapPixelAccess> lockPixels (bool alphaPremultiplied) = 0;
|
||||
|
||||
virtual void setScaleFactor (double factor) = 0;
|
||||
virtual double getScaleFactor () const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
class IPlatformBitmapPixelAccess : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
enum PixelFormat
|
||||
{
|
||||
kARGB,
|
||||
kRGBA,
|
||||
kABGR,
|
||||
kBGRA
|
||||
};
|
||||
|
||||
virtual uint8_t* getAddress () const = 0;
|
||||
virtual uint32_t getBytesPerRow () const = 0;
|
||||
virtual PixelFormat getPixelFormat () const = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,83 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../cstring.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PlatformFileExtension
|
||||
{
|
||||
UTF8String description;
|
||||
UTF8String extension;
|
||||
UTF8String mimeType;
|
||||
UTF8String uti;
|
||||
int32_t macType {0};
|
||||
};
|
||||
|
||||
static PlatformFileExtension PlatformAllFilesExtension = {"All Files", "", "", "", 0};
|
||||
static PlatformFileExtension PlatformNoFileExtension = {};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
enum class PlatformFileSelectorStyle : uint32_t
|
||||
{
|
||||
SelectFile,
|
||||
SelectDirectory,
|
||||
SelectSaveFile,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
enum class PlatformFileSelectorFlags : uint32_t
|
||||
{
|
||||
MultiFileSelection = 1 << 0,
|
||||
RunModal = 1 << 1,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PlatformFileSelectorConfig
|
||||
{
|
||||
using CallbackFunc = std::function<void (std::vector<UTF8String>&&)>;
|
||||
using FileExtensionList = std::vector<PlatformFileExtension>;
|
||||
|
||||
UTF8String title;
|
||||
UTF8String initialPath;
|
||||
UTF8String defaultSaveName;
|
||||
FileExtensionList extensions;
|
||||
PlatformFileExtension defaultExtension;
|
||||
uint32_t flags {0};
|
||||
|
||||
CallbackFunc doneCallback;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformFileSelector
|
||||
{
|
||||
public:
|
||||
virtual bool run (const PlatformFileSelectorConfig& config) = 0;
|
||||
virtual bool cancel () = 0;
|
||||
|
||||
virtual ~IPlatformFileSelector () noexcept = default;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline bool operator== (const PlatformFileExtension& e1, const PlatformFileExtension& e2)
|
||||
{
|
||||
return e1.macType == e2.macType && e1.uti == e2.uti && e1.mimeType == e2.mimeType &&
|
||||
e1.extension == e2.extension && e1.description == e2.description;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline bool operator!= (const PlatformFileExtension& e1, const PlatformFileExtension& e2)
|
||||
{
|
||||
return e1.macType != e2.macType || e1.uti != e2.uti || e1.mimeType != e2.mimeType ||
|
||||
e1.extension != e2.extension || e1.description != e2.description;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,50 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include <list>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// IFontPainter Declaration
|
||||
//! @brief font paint interface
|
||||
//-----------------------------------------------------------------------------
|
||||
class IFontPainter
|
||||
{
|
||||
public:
|
||||
virtual ~IFontPainter () noexcept = default;
|
||||
|
||||
virtual void drawString (const PlatformGraphicsDeviceContextPtr& context,
|
||||
IPlatformString* string, const CPoint& p, const CColor& color,
|
||||
bool antialias = true) const = 0;
|
||||
virtual CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context,
|
||||
IPlatformString* string, bool antialias = true) const = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// IPlatformFont declaration
|
||||
//! @brief platform font class
|
||||
///
|
||||
/// Encapsulation of a platform font.
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformFont : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
/** returns the ascent line offset of the baseline of this font. If not supported returns -1 */
|
||||
virtual double getAscent () const = 0;
|
||||
/** returns the descent line offset of the baseline of this font. If not supported returns -1 */
|
||||
virtual double getDescent () const = 0;
|
||||
/** returns the space between lines for this font. If not supported returns -1 */
|
||||
virtual double getLeading () const = 0;
|
||||
/** returns the height of the highest capital letter for this font. If not supported returns -1
|
||||
*/
|
||||
virtual double getCapHeight () const = 0;
|
||||
|
||||
virtual const IFontPainter* getPainter () const = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,119 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include "../dragging.h"
|
||||
#include "../optional.h"
|
||||
#include "../cstring.h"
|
||||
#include "iplatformframecallback.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
struct GenericOptionMenuTheme;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformFrame : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
/** get the top left position in global coordinates */
|
||||
virtual bool getGlobalPosition (CPoint& pos) const = 0;
|
||||
/** set size of platform representation relative to parent */
|
||||
virtual bool setSize (const CRect& newSize) = 0;
|
||||
/** get size of platform representation relative to parent */
|
||||
virtual bool getSize (CRect& size) const = 0;
|
||||
|
||||
/** get current mouse position out of event stream */
|
||||
virtual bool getCurrentMousePosition (CPoint& mousePosition) const = 0;
|
||||
/** get current mouse buttons out of event stream */
|
||||
virtual bool getCurrentMouseButtons (CButtonState& buttons) const = 0;
|
||||
/** get current hardware modifier key state */
|
||||
virtual bool getCurrentModifiers (Modifiers& modifiers) const = 0;
|
||||
/** set mouse cursor shape */
|
||||
virtual bool setMouseCursor (CCursorType type) = 0;
|
||||
|
||||
/** invalidates rect in platform representation*/
|
||||
virtual bool invalidRect (const CRect& rect) = 0;
|
||||
/** blit scroll the src rect by distance, return false if not supported */
|
||||
virtual bool scrollRect (const CRect& src, const CPoint& distance) = 0;
|
||||
|
||||
/** show tooltip */
|
||||
virtual bool showTooltip (const CRect& rect, const char* utf8Text) = 0;
|
||||
/** hide tooltip */
|
||||
virtual bool hideTooltip () = 0;
|
||||
|
||||
/** TODO: remove this call later when everything is done */
|
||||
virtual void* getPlatformRepresentation () const = 0;
|
||||
|
||||
/** create a native text edit control */
|
||||
virtual SharedPointer<IPlatformTextEdit>
|
||||
createPlatformTextEdit (IPlatformTextEditCallback* textEdit) = 0;
|
||||
/** create a native popup menu */
|
||||
virtual SharedPointer<IPlatformOptionMenu> createPlatformOptionMenu () = 0;
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
/** create a native opengl sub view */
|
||||
virtual SharedPointer<IPlatformOpenGLView> createPlatformOpenGLView () = 0;
|
||||
#endif // VSTGUI_OPENGL_SUPPORT
|
||||
|
||||
/** create a native view layer, may return 0 if not supported */
|
||||
virtual SharedPointer<IPlatformViewLayer> createPlatformViewLayer (
|
||||
IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer = nullptr) = 0;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
/** start a drag operation */
|
||||
virtual DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) = 0;
|
||||
#endif
|
||||
/** start a drag operation
|
||||
*
|
||||
* optional callback will be remembered until the drag is droped or canceled
|
||||
*/
|
||||
virtual bool doDrag (const DragDescription& dragDescription,
|
||||
const SharedPointer<IDragCallback>& callback) = 0;
|
||||
|
||||
/** */
|
||||
virtual PlatformType getPlatformType () const = 0;
|
||||
|
||||
/** called from IPlatformFrameCallback when it's closed */
|
||||
virtual void onFrameClosed () = 0;
|
||||
|
||||
/** when called from a key down/up event converts the event to the actual text. */
|
||||
virtual Optional<UTF8String> convertCurrentKeyEventToText () = 0;
|
||||
|
||||
/** setup to use (or not) the generic option menu and optionally set the theme to use */
|
||||
virtual bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
explicit IPlatformFrame (IPlatformFrameCallback* frame) : frame (frame) {}
|
||||
IPlatformFrameCallback* frame;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/* Extension to support Mac TouchBar */
|
||||
//-----------------------------------------------------------------------------
|
||||
class ITouchBarCreator : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
/** must return an instance of NSTouchBar or nullptr. */
|
||||
virtual void* createTouchBar () = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformFrameTouchBarExtension /* Extents IPlatformFrame */
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformFrameTouchBarExtension () noexcept = default;
|
||||
|
||||
/** set the touchbar creator. */
|
||||
virtual void setTouchBarCreator (const SharedPointer<ITouchBarCreator>& creator) = 0;
|
||||
/** forces the touchbar to be recreated. */
|
||||
virtual void recreateTouchBar () = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
struct VstKeyCode;
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
enum class PlatformType : int32_t {
|
||||
kHWND, // Windows HWND
|
||||
kNSView, // macOS NSView
|
||||
kUIView, // iOS UIView
|
||||
kHWNDTopLevel, // Windows HWDN Top Level (non child)
|
||||
kX11EmbedWindowID, // X11 XID
|
||||
kWaylandSurfaceID, // Wayland Surface ID
|
||||
kGdkWindow, // GdkWindow
|
||||
|
||||
kDefaultNative = -1
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Callback interface from IPlatformFrame implementations
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformFrameCallback
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformFrameCallback () = default;
|
||||
|
||||
virtual void platformDrawRects (const PlatformGraphicsDeviceContextPtr& context,
|
||||
double scaleFactor, const std::vector<CRect>& rects) = 0;
|
||||
|
||||
virtual void platformOnEvent (Event& event) = 0;
|
||||
|
||||
virtual DragOperation platformOnDragEnter (DragEventData data) = 0;
|
||||
virtual DragOperation platformOnDragMove (DragEventData data) = 0;
|
||||
virtual void platformOnDragLeave (DragEventData data) = 0;
|
||||
virtual bool platformOnDrop (DragEventData data) = 0;
|
||||
|
||||
virtual void platformOnActivate (bool state) = 0;
|
||||
virtual void platformOnWindowActivate (bool state) = 0;
|
||||
|
||||
virtual void platformScaleFactorChanged (double newScaleFactor) = 0;
|
||||
|
||||
#if VSTGUI_TOUCH_EVENT_HANDLING
|
||||
virtual void platformOnTouchEvent (ITouchEvent& event) = 0;
|
||||
#endif
|
||||
//------------------------------------------------------------------------------------
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
class IPlatformFrameConfig
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformFrameConfig () noexcept = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,22 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ccolor.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformGradient
|
||||
{
|
||||
public:
|
||||
virtual void setColorStops (const GradientColorStopMap& colorStops) = 0;
|
||||
virtual void addColorStop (const GradientColorStop& colorStop) = 0;
|
||||
virtual const GradientColorStopMap& getColorStops () const = 0;
|
||||
|
||||
virtual ~IPlatformGradient () noexcept = default;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,151 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
enum class PlatformGraphicsDrawStyle : uint32_t
|
||||
{
|
||||
Stroked,
|
||||
Filled,
|
||||
FilledAndStroked
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
enum class PlatformGraphicsPathDrawMode : uint32_t
|
||||
{
|
||||
Filled,
|
||||
FilledEvenOdd,
|
||||
Stroked
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
using TransformMatrix = CGraphicsTransform;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ScreenInfo
|
||||
{
|
||||
using Identifier = uint32_t;
|
||||
/*
|
||||
Identifier identifier;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
*/
|
||||
};
|
||||
static constexpr const ScreenInfo::Identifier DefaultScreenIdentifier = 0u;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformGraphicsDeviceFactory
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformGraphicsDeviceFactory () noexcept = default;
|
||||
|
||||
virtual PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformGraphicsDevice
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformGraphicsDevice () noexcept = default;
|
||||
|
||||
virtual PlatformGraphicsDeviceContextPtr
|
||||
createBitmapContext (const PlatformBitmapPtr& bitmap) const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformGraphicsDeviceContext
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformGraphicsDeviceContext () noexcept = default;
|
||||
|
||||
virtual const IPlatformGraphicsDevice& getDevice () const = 0;
|
||||
virtual PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const = 0;
|
||||
|
||||
virtual bool beginDraw () const = 0;
|
||||
virtual bool endDraw () const = 0;
|
||||
// draw commands
|
||||
virtual bool drawLine (LinePair line) const = 0;
|
||||
virtual bool drawLines (const LineList& lines) const = 0;
|
||||
virtual bool drawPolygon (const PointList& polygonPointList,
|
||||
PlatformGraphicsDrawStyle drawStyle) const = 0;
|
||||
virtual bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const = 0;
|
||||
virtual bool drawArc (CRect rect, double startAngle1, double endAngle2,
|
||||
PlatformGraphicsDrawStyle drawStyle) const = 0;
|
||||
virtual bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const = 0;
|
||||
virtual bool drawPoint (CPoint point, CColor color) const = 0;
|
||||
virtual bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha,
|
||||
BitmapInterpolationQuality quality) const = 0;
|
||||
virtual bool clearRect (CRect rect) const = 0;
|
||||
virtual bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode,
|
||||
TransformMatrix* transformation) const = 0;
|
||||
virtual bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient,
|
||||
CPoint startPoint, CPoint endPoint, bool evenOdd,
|
||||
TransformMatrix* transformation) const = 0;
|
||||
virtual bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient,
|
||||
CPoint center, CCoord radius, CPoint originOffset,
|
||||
bool evenOdd, TransformMatrix* transformation) const = 0;
|
||||
// state
|
||||
virtual void saveGlobalState () const = 0;
|
||||
virtual void restoreGlobalState () const = 0;
|
||||
virtual void setLineStyle (const CLineStyle& style) const = 0;
|
||||
virtual void setLineWidth (CCoord width) const = 0;
|
||||
virtual void setDrawMode (CDrawMode mode) const = 0;
|
||||
virtual void setClipRect (CRect clip) const = 0;
|
||||
virtual void setFillColor (CColor color) const = 0;
|
||||
virtual void setFrameColor (CColor color) const = 0;
|
||||
virtual void setGlobalAlpha (double newAlpha) const = 0;
|
||||
virtual void setTransformMatrix (const TransformMatrix& tm) const = 0;
|
||||
|
||||
// extension
|
||||
virtual const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformGraphicsDeviceContextBitmapExt
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformGraphicsDeviceContextBitmapExt () noexcept = default;
|
||||
|
||||
virtual bool drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dest,
|
||||
const CNinePartTiledDescription& desc, double alpha,
|
||||
BitmapInterpolationQuality quality) const = 0;
|
||||
virtual bool fillRectWithBitmap (IPlatformBitmap& bitmap, CRect srcRect, CRect dstRect,
|
||||
double alpha, BitmapInterpolationQuality quality) const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
enum class LineCap : uint32_t
|
||||
{
|
||||
Butt = 0,
|
||||
Round,
|
||||
Square
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
enum class LineJoin : uint32_t
|
||||
{
|
||||
Miter = 0,
|
||||
Round,
|
||||
Bevel
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformGraphicsDeviceContextGradientExt
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformGraphicsDeviceContextGradientExt () noexcept = default;
|
||||
|
||||
virtual bool drawLinearGradientLine (const PointList& line, const IPlatformGradient& gradient,
|
||||
CCoord lineWidth, LineCap lineCap,
|
||||
LineJoin lineJoin) const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,64 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ccolor.h"
|
||||
#include "../cgraphicstransform.h"
|
||||
#include "../cpoint.h"
|
||||
#include "../crect.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
enum class PlatformGraphicsPathFillMode : int32_t
|
||||
{
|
||||
Winding,
|
||||
Alternate,
|
||||
Ignored
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformGraphicsPathFactory
|
||||
{
|
||||
public:
|
||||
virtual PlatformGraphicsPathPtr createPath (
|
||||
PlatformGraphicsPathFillMode fillMode = PlatformGraphicsPathFillMode::Winding) = 0;
|
||||
virtual PlatformGraphicsPathPtr createTextPath (const PlatformFontPtr& font,
|
||||
UTF8StringPtr text) = 0;
|
||||
|
||||
virtual ~IPlatformGraphicsPathFactory () noexcept = default;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformGraphicsPath
|
||||
{
|
||||
public:
|
||||
/** add an arc to the path. Begins a new subpath if no elements were added before. */
|
||||
virtual void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) = 0;
|
||||
/** add an ellipse to the path. Begins a new subpath if no elements were added before. */
|
||||
virtual void addEllipse (const CRect& rect) = 0;
|
||||
/** add a rectangle to the path. Begins a new subpath if no elements were added before. */
|
||||
virtual void addRect (const CRect& rect) = 0;
|
||||
/** add a line to the path. A subpath must begin before */
|
||||
virtual void addLine (const CPoint& to) = 0;
|
||||
/** add a bezier curve to the path. A subpath must begin before */
|
||||
virtual void addBezierCurve (const CPoint& control1, const CPoint& control2,
|
||||
const CPoint& end) = 0;
|
||||
/** begin a new subpath. */
|
||||
virtual void beginSubpath (const CPoint& start) = 0;
|
||||
/** close a subpath. A straight line will be added from the current point to the start point. */
|
||||
virtual void closeSubpath () = 0;
|
||||
virtual void finishBuilding () = 0;
|
||||
|
||||
virtual bool hitTest (const CPoint& p, bool evenOddFilled = false,
|
||||
CGraphicsTransform* transform = nullptr) const = 0;
|
||||
virtual CRect getBoundingBox () const = 0;
|
||||
|
||||
virtual PlatformGraphicsPathFillMode getFillMode () const = 0;
|
||||
|
||||
virtual ~IPlatformGraphicsPath () noexcept = default;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,71 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
|
||||
namespace VSTGUI {
|
||||
class IPlatformFrame;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PixelFormat
|
||||
{
|
||||
// TODO: add more if we need more...
|
||||
|
||||
enum {
|
||||
kDoubleBuffered = 1 << 0,
|
||||
kMultiSample = 1 << 2,
|
||||
kModernOpenGL = 1 << 3 // Mac only. Indicates to use the NSOpenGLProfileVersion3_2Core.
|
||||
};
|
||||
|
||||
uint32_t depthBufferSize {32};
|
||||
uint32_t stencilBufferSize {0};
|
||||
/** only used when kMultiSample is set */
|
||||
uint32_t samples {0};
|
||||
uint32_t flags {kDoubleBuffered};
|
||||
|
||||
PixelFormat () = default;
|
||||
PixelFormat (const PixelFormat&) = default;
|
||||
PixelFormat& operator= (const PixelFormat&) = default;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IOpenGLView
|
||||
{
|
||||
public:
|
||||
virtual void drawOpenGL (const CRect& updateRect) = 0;
|
||||
virtual void reshape () = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformOpenGLView : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
|
||||
virtual bool init (IOpenGLView* view, PixelFormat* pixelFormat = nullptr) = 0;
|
||||
virtual void remove () = 0;
|
||||
|
||||
virtual void invalidRect (const CRect& rect) = 0;
|
||||
/** visibleSize is cframe relative */
|
||||
virtual void viewSizeChanged (const CRect& visibleSize) = 0;
|
||||
/** make OpenGL context active */
|
||||
virtual bool makeContextCurrent () = 0;
|
||||
/** lock changes to context */
|
||||
virtual bool lockContext () = 0;
|
||||
/** unlock changes to context */
|
||||
virtual bool unlockContext () = 0;
|
||||
/** swap buffers and clear active OpenGL context */
|
||||
virtual void swapBuffers () = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // VSTGUI_OPENGL_SUPPORT
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PlatformOptionMenuResult
|
||||
{
|
||||
COptionMenu* menu;
|
||||
int32_t index;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformOptionMenu : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void (COptionMenu* optionMenu, PlatformOptionMenuResult result)>;
|
||||
virtual void popup (COptionMenu* optionMenu, const Callback& callback) = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
/// @endcond
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformResourceInputStream
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformResourceInputStream () noexcept = default;
|
||||
|
||||
virtual uint32_t readRaw (void* buffer, uint32_t size) = 0;
|
||||
virtual int64_t seek (int64_t pos, SeekMode mode) = 0;
|
||||
virtual int64_t tell () = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,22 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguibase.h"
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformString : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
virtual void setUTF8String (UTF8StringPtr utf8String) = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Tasks {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Queue final
|
||||
{
|
||||
const uint64_t identifier;
|
||||
};
|
||||
static constexpr Queue InvalidQueue = Queue {std::numeric_limits<uint64_t>::max ()};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline bool operator== (const Queue& q1, const Queue& q2) noexcept
|
||||
{
|
||||
return q1.identifier == q2.identifier;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline bool operator!= (const Queue& q1, const Queue& q2) noexcept
|
||||
{
|
||||
return q1.identifier != q2.identifier;
|
||||
}
|
||||
|
||||
using Task = std::function<void ()>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Tasks
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class IPlatformTaskExecutor
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformTaskExecutor () noexcept = default;
|
||||
|
||||
virtual const Tasks::Queue& getMainQueue () const = 0;
|
||||
virtual const Tasks::Queue& getBackgroundQueue () const = 0;
|
||||
virtual Tasks::Queue makeSerialQueue (const char* name) const = 0;
|
||||
virtual void releaseSerialQueue (const Tasks::Queue& queue) const = 0;
|
||||
virtual void schedule (const Tasks::Queue& queue, Tasks::Task&& task) const = 0;
|
||||
virtual void waitAllTasksExecuted (const Tasks::Queue& queue) const = 0;
|
||||
virtual void waitAllTasksExecuted () const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#include "../cfont.h"
|
||||
#include "../ccolor.h"
|
||||
#include "../crect.h"
|
||||
#include "../cdrawdefs.h"
|
||||
|
||||
struct VstKeyCode;
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformTextEditCallback
|
||||
{
|
||||
public:
|
||||
virtual CColor platformGetBackColor () const = 0;
|
||||
virtual CColor platformGetFontColor () const = 0;
|
||||
virtual CFontRef platformGetFont () const = 0;
|
||||
virtual CHoriTxtAlign platformGetHoriTxtAlign () const = 0;
|
||||
virtual const UTF8String& platformGetText () const = 0;
|
||||
virtual const UTF8String& platformGetPlaceholderText () const = 0;
|
||||
virtual CRect platformGetSize () const = 0;
|
||||
virtual CRect platformGetVisibleSize () const = 0;
|
||||
virtual CPoint platformGetTextInset () const = 0;
|
||||
virtual void platformLooseFocus (bool returnPressed) = 0;
|
||||
virtual void platformOnKeyboardEvent (KeyboardEvent& event) = 0;
|
||||
virtual void platformTextDidChange () = 0;
|
||||
virtual bool platformIsSecureTextEdit () = 0;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformTextEdit : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
virtual UTF8String getText () = 0;
|
||||
virtual bool setText (const UTF8String& text) = 0;
|
||||
virtual bool updateSize () = 0;
|
||||
virtual bool drawsPlaceholder () const = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
explicit IPlatformTextEdit (IPlatformTextEditCallback* textEdit) : textEdit (textEdit) {}
|
||||
IPlatformTextEditCallback* textEdit;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,61 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
using TextInputClientCancelCallback = std::function<void ()>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ICocoaTextInputClient
|
||||
{
|
||||
struct TextRange
|
||||
{
|
||||
size_t position;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
virtual void insertText (const std::u32string& string, TextRange range) = 0;
|
||||
virtual void setMarkedText (const std::u32string& string, TextRange selectedRange,
|
||||
TextRange replacementRange) = 0;
|
||||
virtual bool hasMarkedText () = 0;
|
||||
virtual void unmarkText () = 0;
|
||||
virtual TextRange getMarkedRange () = 0;
|
||||
virtual TextRange getSelectedRange () = 0;
|
||||
virtual CRect firstRectForCharacterRange (TextRange range, TextRange& actualRange) = 0;
|
||||
virtual std::u32string substringForRange (TextRange range, TextRange& actualRange) = 0;
|
||||
virtual size_t characterIndexForPoint (CPoint pos) = 0;
|
||||
|
||||
virtual void setCancelCallback (const TextInputClientCancelCallback& callback) = 0;
|
||||
|
||||
virtual ~ICocoaTextInputClient () noexcept = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct IIMETextInputClient
|
||||
{
|
||||
struct CharPosition
|
||||
{
|
||||
uint32_t characterPosition;
|
||||
CPoint position;
|
||||
double lineHeight;
|
||||
CRect documentRect;
|
||||
};
|
||||
|
||||
virtual bool ime_queryCharacterPosition (CharPosition& cp) = 0;
|
||||
virtual void ime_setMarkedText (const std::u32string& string) = 0;
|
||||
virtual void ime_insertText (const std::u32string& string) = 0;
|
||||
virtual void ime_unmarkText () = 0;
|
||||
|
||||
virtual ~IIMETextInputClient () noexcept = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguibase.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformTimerCallback
|
||||
{
|
||||
public:
|
||||
virtual void fire () = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformTimer : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
virtual bool start (uint32_t fireTime) = 0;
|
||||
virtual bool stop () = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,35 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformViewLayerDelegate
|
||||
{
|
||||
public:
|
||||
virtual ~IPlatformViewLayerDelegate () noexcept = default;
|
||||
|
||||
/** rects are in client coordinates (top-left is 0, 0) */
|
||||
virtual void drawViewLayerRects (const PlatformGraphicsDeviceContextPtr& context,
|
||||
double scaleFactor, const std::vector<CRect>& rects) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IPlatformViewLayer : public AtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
/** size must be zero based */
|
||||
virtual void invalidRect (const CRect& size) = 0;
|
||||
/** size is relative to platformParent */
|
||||
virtual void setSize (const CRect& size) = 0;
|
||||
virtual void setZIndex (uint32_t zIndex) = 0;
|
||||
virtual void setAlpha (float alpha) = 0;
|
||||
virtual void onScaleFactorChanged (double newScaleFactor) = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,285 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "../../cpoint.h"
|
||||
#include "../../cresourcedescription.h"
|
||||
#include "linuxfactory.h"
|
||||
#include "cairobitmap.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
namespace CairoBitmapPrivate {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PNGMemoryReader
|
||||
{
|
||||
PNGMemoryReader (const uint8_t* ptr, size_t size) : ptr (ptr), size (size) {}
|
||||
|
||||
cairo_surface_t* create () { return cairo_image_surface_create_from_png_stream (read, this); }
|
||||
|
||||
private:
|
||||
static cairo_status_t read (void* closure, unsigned char* data, unsigned int length)
|
||||
{
|
||||
auto self = reinterpret_cast<PNGMemoryReader*> (closure);
|
||||
auto numBytes = std::min<size_t> (length, self->size);
|
||||
if (numBytes)
|
||||
{
|
||||
memcpy (data, self->ptr, numBytes);
|
||||
self->ptr += numBytes;
|
||||
self->size -= numBytes;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
return CAIRO_STATUS_READ_ERROR;
|
||||
}
|
||||
|
||||
const uint8_t* ptr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PNGMemoryWriter
|
||||
{
|
||||
using Buffer = PNGBitmapBuffer;
|
||||
|
||||
Buffer create (cairo_surface_t* image)
|
||||
{
|
||||
Buffer buffer;
|
||||
cairo_surface_write_to_png_stream (image, write, &buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
static cairo_status_t write (void* closure, const unsigned char* data, unsigned int length)
|
||||
{
|
||||
auto buffer = reinterpret_cast<Buffer*> (closure);
|
||||
if (!buffer)
|
||||
return CAIRO_STATUS_WRITE_ERROR;
|
||||
buffer->reserve (buffer->size () + length);
|
||||
std::copy_n (data, length, std::back_inserter (*buffer));
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static SurfaceHandle createImageFromPath (const char* path)
|
||||
{
|
||||
if (auto surface = cairo_image_surface_create_from_png (path))
|
||||
{
|
||||
if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
cairo_surface_destroy (surface);
|
||||
return {};
|
||||
}
|
||||
if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32)
|
||||
return SurfaceHandle {surface};
|
||||
|
||||
// vstgui always works with 32 bit images
|
||||
auto x = cairo_image_surface_get_width (surface);
|
||||
auto y = cairo_image_surface_get_height (surface);
|
||||
auto surface32 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, x, y);
|
||||
vstgui_assert (cairo_surface_status (surface32) == CAIRO_STATUS_SUCCESS);
|
||||
auto context = cairo_create (surface32);
|
||||
vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
|
||||
cairo_set_source_surface (context, surface, 0, 0);
|
||||
vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
|
||||
cairo_paint (context);
|
||||
vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
|
||||
cairo_surface_flush (surface32);
|
||||
vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
|
||||
cairo_destroy (context);
|
||||
cairo_surface_destroy (surface);
|
||||
return SurfaceHandle {surface32};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class PixelAccess : public IPlatformBitmapPixelAccess
|
||||
{
|
||||
public:
|
||||
~PixelAccess () override;
|
||||
|
||||
bool init (Bitmap* bitmap, const SurfaceHandle& surface);
|
||||
|
||||
private:
|
||||
uint8_t* address {nullptr};
|
||||
uint32_t bytesPerRow {0};
|
||||
|
||||
uint8_t* getAddress () const override { return address; }
|
||||
uint32_t getBytesPerRow () const override { return bytesPerRow; }
|
||||
PixelFormat getPixelFormat () const override
|
||||
{
|
||||
#if __LITTLE_ENDIAN
|
||||
return kBGRA;
|
||||
#else
|
||||
return kARGB;
|
||||
#endif
|
||||
}
|
||||
|
||||
SharedPointer<Bitmap> bitmap;
|
||||
SurfaceHandle surface;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // CairoBitmapPrivate
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<Bitmap> Bitmap::create (UTF8StringPtr absolutePath)
|
||||
{
|
||||
if (auto surface = Cairo::CairoBitmapPrivate::createImageFromPath (absolutePath))
|
||||
{
|
||||
if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
cairo_surface_destroy (surface);
|
||||
return nullptr;
|
||||
}
|
||||
return makeOwned<Bitmap> (surface);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<Bitmap> Bitmap::create (const void* ptr, uint32_t memSize)
|
||||
{
|
||||
Cairo::CairoBitmapPrivate::PNGMemoryReader reader (reinterpret_cast<const uint8_t*> (ptr),
|
||||
memSize);
|
||||
if (auto surface = reader.create ())
|
||||
{
|
||||
return makeOwned<Bitmap> (Cairo::SurfaceHandle {surface});
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bitmap::Bitmap (const CPoint& _size)
|
||||
{
|
||||
size = _size;
|
||||
surface = SurfaceHandle (cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size.x, size.y));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bitmap::Bitmap () = default;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bitmap::Bitmap (const SurfaceHandle& surface) : surface (surface)
|
||||
{
|
||||
size.x = cairo_image_surface_get_width (surface);
|
||||
size.y = cairo_image_surface_get_height (surface);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bitmap::~Bitmap () {}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Bitmap::load (const CResourceDescription& desc)
|
||||
{
|
||||
auto linuxFactory = getPlatformFactory ().asLinuxFactory ();
|
||||
if (!linuxFactory)
|
||||
return false;
|
||||
auto path = linuxFactory->getResourcePath ();
|
||||
if (!path.empty ())
|
||||
{
|
||||
if (desc.type == CResourceDescription::kIntegerType)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
snprintf (filename, PATH_MAX, "bmp%05d.png", (int32_t)desc.u.id);
|
||||
path += filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
path += desc.u.name;
|
||||
}
|
||||
if (auto s = CairoBitmapPrivate::createImageFromPath (path.data ()))
|
||||
{
|
||||
if (cairo_surface_status (s) != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
cairo_surface_destroy (s);
|
||||
return false;
|
||||
}
|
||||
surface = s;
|
||||
size.x = cairo_image_surface_get_width (surface);
|
||||
size.y = cairo_image_surface_get_height (surface);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const CPoint& Bitmap::getSize () const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformBitmapPixelAccess> Bitmap::lockPixels (bool alphaPremultiplied)
|
||||
{
|
||||
if (locked)
|
||||
return nullptr;
|
||||
#warning TODO: alphaPremultiplied is currently ignored, always treated as true
|
||||
locked = true;
|
||||
auto pixelAccess = owned (new CairoBitmapPrivate::PixelAccess ());
|
||||
if (pixelAccess->init (this, surface))
|
||||
return pixelAccess;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Bitmap::setScaleFactor (double factor)
|
||||
{
|
||||
scaleFactor = factor;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
double Bitmap::getScaleFactor () const
|
||||
{
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PNGBitmapBuffer Bitmap::createMemoryPNGRepresentation () const
|
||||
{
|
||||
Cairo::CairoBitmapPrivate::PNGMemoryWriter writer;
|
||||
return writer.create (getSurface ());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace CairoBitmapPrivate {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PixelAccess::init (Bitmap* inBitmap, const SurfaceHandle& inSurface)
|
||||
{
|
||||
cairo_surface_flush (inSurface);
|
||||
address = cairo_image_surface_get_data (inSurface);
|
||||
if (!address)
|
||||
{
|
||||
#if DEBUG
|
||||
auto status = cairo_surface_status (inSurface);
|
||||
if (status != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
auto msg = cairo_status_to_string (status);
|
||||
DebugPrint ("%s\n", msg);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
surface = inSurface;
|
||||
bitmap = inBitmap;
|
||||
bytesPerRow = cairo_image_surface_get_stride (surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PixelAccess::~PixelAccess ()
|
||||
{
|
||||
cairo_surface_mark_dirty (surface);
|
||||
bitmap->unlock ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // CairoBitmapPrivate
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,62 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include "../../cpoint.h"
|
||||
#include "../../vstguidebug.h"
|
||||
#include "../iplatformbitmap.h"
|
||||
#include "../platformfwd.h"
|
||||
#include "cairoutils.h"
|
||||
#include <functional>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class Bitmap : public IPlatformBitmap
|
||||
{
|
||||
public:
|
||||
static SharedPointer<Bitmap> create (UTF8StringPtr absolutePath);
|
||||
static SharedPointer<Bitmap> create (const void* ptr, uint32_t memSize);
|
||||
|
||||
Bitmap ();
|
||||
explicit Bitmap (const CPoint& size);
|
||||
explicit Bitmap (const SurfaceHandle& surface);
|
||||
~Bitmap () override;
|
||||
|
||||
bool load (const CResourceDescription& desc);
|
||||
const CPoint& getSize () const override;
|
||||
SharedPointer<IPlatformBitmapPixelAccess> lockPixels (bool alphaPremultiplied) override;
|
||||
void setScaleFactor (double factor) override;
|
||||
double getScaleFactor () const override;
|
||||
|
||||
PNGBitmapBuffer createMemoryPNGRepresentation () const;
|
||||
|
||||
const SurfaceHandle& getSurface () const
|
||||
{
|
||||
vstgui_assert (!locked, "Bitmap is locked");
|
||||
if (locked)
|
||||
{
|
||||
static SurfaceHandle empty;
|
||||
return empty;
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
void unlock () { locked = false; }
|
||||
|
||||
private:
|
||||
double scaleFactor {1.0};
|
||||
SurfaceHandle surface;
|
||||
CPoint size;
|
||||
bool locked {false};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,308 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cairofont.h"
|
||||
#include "../../cstring.h"
|
||||
#include "../../cfont.h"
|
||||
#include "../../cpoint.h"
|
||||
#include "../../ccolor.h"
|
||||
#include "cairographicscontext.h"
|
||||
#include "linuxstring.h"
|
||||
#include "linuxfactory.h"
|
||||
#include <pango/pangocairo.h>
|
||||
#include <pango/pango-features.h>
|
||||
#include <pango/pangofc-fontmap.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
namespace {
|
||||
|
||||
using PangoFontHandle = Handle<PangoFont*, decltype (&g_object_ref), g_object_ref,
|
||||
decltype (&g_object_unref), g_object_unref>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class FontList
|
||||
{
|
||||
public:
|
||||
static FontList& instance ()
|
||||
{
|
||||
static FontList gInstance;
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
FcConfig* getFontConfig () { return fcConfig; }
|
||||
|
||||
PangoFontMap* getFontMap () { return fontMap; }
|
||||
|
||||
PangoContext* getFontContext () { return fontContext; }
|
||||
|
||||
bool queryFont (UTF8StringPtr name, CCoord size, int32_t style, PangoFontHandle& fontHandle)
|
||||
{
|
||||
PangoFontDescription* desc = pango_font_description_new ();
|
||||
pango_font_description_set_family_static (desc, name);
|
||||
pango_font_description_set_absolute_size (desc, pango_units_from_double (size));
|
||||
if (style & kItalicFace)
|
||||
pango_font_description_set_style (desc, PANGO_STYLE_ITALIC);
|
||||
if (style & kBoldFace)
|
||||
pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
|
||||
PangoFont* font = pango_font_map_load_font (fontMap, fontContext, desc);
|
||||
pango_font_description_free (desc);
|
||||
if (font)
|
||||
fontHandle.assign (font);
|
||||
return font != nullptr;
|
||||
}
|
||||
|
||||
bool getAllFontFamilies (const FontFamilyCallback& callback)
|
||||
{
|
||||
if (!fontContext)
|
||||
return false;
|
||||
PangoFontFamily** families = nullptr;
|
||||
int numFamilies = 0;
|
||||
pango_context_list_families (fontContext, &families, &numFamilies);
|
||||
for (int i = 0; i < numFamilies; ++i)
|
||||
{
|
||||
PangoFontFamily* family = families[i];
|
||||
if (!callback (pango_font_family_get_name (family)))
|
||||
break;
|
||||
}
|
||||
g_free (families);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
FontList ()
|
||||
{
|
||||
fontMap = pango_cairo_font_map_new ();
|
||||
fontContext = pango_font_map_create_context (fontMap);
|
||||
|
||||
PangoFcFontMap* fcMap =
|
||||
G_TYPE_CHECK_INSTANCE_CAST (fontMap, PANGO_TYPE_FC_FONT_MAP, PangoFcFontMap);
|
||||
if (fcMap && FcInit () && (fcConfig = FcInitLoadConfigAndFonts ()))
|
||||
{
|
||||
if (auto linuxFactory = getPlatformFactory ().asLinuxFactory ())
|
||||
{
|
||||
const UTF8String& resourcePath = linuxFactory->getResourcePath ();
|
||||
if (!resourcePath.empty ())
|
||||
{
|
||||
auto fontDir = resourcePath + "Fonts/";
|
||||
FcConfigAppFontAddDir (fcConfig,
|
||||
reinterpret_cast<const FcChar8*> (fontDir.data ()));
|
||||
}
|
||||
pango_fc_font_map_set_config (fcMap, fcConfig);
|
||||
FcConfigDestroy (fcConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~FontList ()
|
||||
{
|
||||
if (fontMap)
|
||||
g_object_unref (fontMap);
|
||||
if (fontContext)
|
||||
g_object_unref (fontContext);
|
||||
}
|
||||
|
||||
FontList (const FontList&) = delete;
|
||||
FontList& operator= (const FontList&) = delete;
|
||||
|
||||
FcConfig* fcConfig = nullptr;
|
||||
PangoFontMap* fontMap = nullptr;
|
||||
PangoContext* fontContext = nullptr;
|
||||
|
||||
static int slantFromStyle (int32_t style)
|
||||
{
|
||||
return (style & kItalicFace) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
|
||||
}
|
||||
|
||||
static int weightFromStyle (int32_t style)
|
||||
{
|
||||
return (style & kBoldFace) ? FC_WEIGHT_BOLD : FC_WEIGHT_REGULAR;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Font::Impl
|
||||
{
|
||||
PangoFontHandle font;
|
||||
int32_t style;
|
||||
CCoord ascent {-1.};
|
||||
CCoord descent {-1.};
|
||||
CCoord leading {-1.};
|
||||
CCoord capHeight {-1.};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Font::Font (UTF8StringPtr name, const CCoord& size, const int32_t& style)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
|
||||
auto& fontList = FontList::instance ();
|
||||
|
||||
if (fontList.queryFont (name, size, style, impl->font))
|
||||
{
|
||||
PangoFontMetrics* metrics = pango_font_get_metrics (impl->font, nullptr);
|
||||
if (metrics)
|
||||
{
|
||||
impl->ascent = pango_units_to_double (pango_font_metrics_get_ascent (metrics));
|
||||
impl->descent = pango_units_to_double (pango_font_metrics_get_descent (metrics));
|
||||
#if (PANGO_VERSION_MAJOR > 1) || ((PANGO_VERSION_MAJOR == 1) && PANGO_VERSION_MINOR >= 44)
|
||||
auto height = pango_units_to_double (pango_font_metrics_get_height (metrics));
|
||||
impl->leading = height - (impl->ascent + impl->descent);
|
||||
#else
|
||||
impl->leading = 0.;
|
||||
#endif
|
||||
pango_font_metrics_unref (metrics);
|
||||
}
|
||||
|
||||
PangoContext* context = fontList.getFontContext ();
|
||||
if (context)
|
||||
{
|
||||
PangoLayout* layout = pango_layout_new (context);
|
||||
if (layout)
|
||||
{
|
||||
PangoFontDescription* desc = pango_font_describe (impl->font);
|
||||
if (desc)
|
||||
{
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
}
|
||||
|
||||
pango_layout_set_text (layout, "M", -1);
|
||||
|
||||
PangoRectangle inkExtents {};
|
||||
pango_layout_get_pixel_extents (layout, &inkExtents, nullptr);
|
||||
impl->capHeight = inkExtents.height;
|
||||
|
||||
g_object_unref (layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl->style = style;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Font::~Font () {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Font::valid () const { return impl->font; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
double Font::getAscent () const { return impl->ascent; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
double Font::getDescent () const { return impl->descent; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
double Font::getLeading () const { return impl->leading; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
double Font::getCapHeight () const { return impl->capHeight; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const IFontPainter* Font::getPainter () const { return this; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Font::drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string,
|
||||
const CPoint& p, const CColor& color, bool antialias) const
|
||||
{
|
||||
auto cairoContext = std::dynamic_pointer_cast<CairoGraphicsDeviceContext> (context);
|
||||
if (!cairoContext)
|
||||
return;
|
||||
auto linuxString = dynamic_cast<LinuxString*> (string);
|
||||
if (!linuxString)
|
||||
return;
|
||||
PangoContext* pangoContext = FontList::instance ().getFontContext ();
|
||||
if (!pangoContext)
|
||||
return;
|
||||
PangoLayout* layout = pango_layout_new (pangoContext);
|
||||
if (!layout)
|
||||
return;
|
||||
|
||||
if (impl->font)
|
||||
{
|
||||
PangoFontDescription* desc = pango_font_describe (impl->font);
|
||||
if (desc)
|
||||
{
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
}
|
||||
}
|
||||
|
||||
PangoAttrList* attrs = pango_attr_list_new ();
|
||||
if (attrs)
|
||||
{
|
||||
if (impl->style & kUnderlineFace)
|
||||
pango_attr_list_insert (attrs, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE));
|
||||
if (impl->style & kStrikethroughFace)
|
||||
pango_attr_list_insert (attrs, pango_attr_strikethrough_new (true));
|
||||
pango_layout_set_attributes (layout, attrs);
|
||||
pango_attr_list_unref (attrs);
|
||||
}
|
||||
|
||||
pango_layout_set_text (layout, linuxString->get ().c_str (), -1);
|
||||
|
||||
PangoRectangle extents {};
|
||||
pango_layout_get_pixel_extents (layout, nullptr, &extents);
|
||||
|
||||
PangoLayoutIter* iter = pango_layout_get_iter (layout);
|
||||
CCoord baseline = 0.0;
|
||||
if (iter)
|
||||
{
|
||||
baseline = pango_units_to_double (pango_layout_iter_get_baseline (iter));
|
||||
pango_layout_iter_free (iter);
|
||||
}
|
||||
|
||||
cairoContext->drawPangoLayout (layout, {p.x + extents.x, p.y + extents.y - baseline}, color);
|
||||
|
||||
g_object_unref (layout);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CCoord Font::getStringWidth (const PlatformGraphicsDeviceContextPtr&, IPlatformString* string,
|
||||
bool antialias) const
|
||||
{
|
||||
if (auto linuxString = dynamic_cast<LinuxString*> (string))
|
||||
{
|
||||
int pangoWidth = 0;
|
||||
PangoContext* context = FontList::instance ().getFontContext ();
|
||||
if (context)
|
||||
{
|
||||
PangoLayout* layout = pango_layout_new (context);
|
||||
if (layout)
|
||||
{
|
||||
if (impl->font)
|
||||
{
|
||||
PangoFontDescription* desc = pango_font_describe (impl->font);
|
||||
if (desc)
|
||||
{
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
}
|
||||
}
|
||||
pango_layout_set_text (layout, linuxString->get ().c_str (), -1);
|
||||
pango_layout_get_pixel_size (layout, &pangoWidth, nullptr);
|
||||
g_object_unref (layout);
|
||||
}
|
||||
}
|
||||
|
||||
return pangoWidth;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Font::getAllFamilies (const FontFamilyCallback& callback)
|
||||
{
|
||||
return Cairo::FontList::instance ().getAllFontFamilies (callback);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,46 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformfont.h"
|
||||
#include "../platformfactory.h"
|
||||
#include <memory>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class Font
|
||||
: public IPlatformFont
|
||||
, public IFontPainter
|
||||
{
|
||||
public:
|
||||
Font (UTF8StringPtr name, const CCoord& size, const int32_t& style);
|
||||
~Font ();
|
||||
|
||||
bool valid () const;
|
||||
|
||||
double getAscent () const override;
|
||||
double getDescent () const override;
|
||||
double getLeading () const override;
|
||||
double getCapHeight () const override;
|
||||
const IFontPainter* getPainter () const override;
|
||||
|
||||
void drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string,
|
||||
const CPoint& p, const CColor& color, bool antialias = true) const override;
|
||||
CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string,
|
||||
bool antialias = true) const override;
|
||||
|
||||
static bool getAllFamilies (const FontFamilyCallback& callback);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cairogradient.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Gradient::~Gradient () noexcept
|
||||
{
|
||||
changed ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Gradient::changed ()
|
||||
{
|
||||
linearGradient.reset ();
|
||||
radialGradient.reset ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) const
|
||||
{
|
||||
if (!linearGradient || start != linearGradientStart || end != linearGradientEnd)
|
||||
{
|
||||
linearGradient.reset ();
|
||||
radialGradient.reset ();
|
||||
linearGradientStart = start;
|
||||
linearGradientEnd = end;
|
||||
linearGradient =
|
||||
PatternHandle (cairo_pattern_create_linear (start.x, start.y, end.x, end.y));
|
||||
for (auto& it : getColorStops ())
|
||||
{
|
||||
cairo_pattern_add_color_stop_rgba (
|
||||
linearGradient, it.first, it.second.normRed<double> (),
|
||||
it.second.normGreen<double> (), it.second.normBlue<double> (),
|
||||
it.second.normAlpha<double> ());
|
||||
}
|
||||
}
|
||||
return linearGradient;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const PatternHandle& Gradient::getRadialGradient (CPoint center, CCoord radius,
|
||||
CPoint originOffset) const
|
||||
{
|
||||
if (!radialGradient)
|
||||
{
|
||||
radialGradient = PatternHandle (
|
||||
cairo_pattern_create_radial (center.x, center.y, 0., center.x, center.y, radius));
|
||||
|
||||
for (auto& it : getColorStops ())
|
||||
{
|
||||
cairo_pattern_add_color_stop_rgba (
|
||||
radialGradient, it.first, it.second.normRed<double> (),
|
||||
it.second.normGreen<double> (), it.second.normBlue<double> (),
|
||||
it.second.normAlpha<double> ());
|
||||
}
|
||||
}
|
||||
return radialGradient;
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,39 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common/gradientbase.h"
|
||||
#include "../../cpoint.h"
|
||||
#include "cairoutils.h"
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class Gradient : public PlatformGradientBase
|
||||
{
|
||||
public:
|
||||
~Gradient () noexcept override;
|
||||
|
||||
const PatternHandle& getLinearGradient (CPoint start, CPoint end) const;
|
||||
const PatternHandle& getRadialGradient (CPoint center, CCoord radius,
|
||||
CPoint originOffset) const;
|
||||
|
||||
private:
|
||||
void changed () override;
|
||||
|
||||
/* we want to calculate a normalized linear and radial gradiant */
|
||||
mutable PatternHandle linearGradient;
|
||||
mutable PatternHandle radialGradient;
|
||||
|
||||
mutable CPoint linearGradientStart;
|
||||
mutable CPoint linearGradientEnd;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
+751
@@ -0,0 +1,751 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cairographicscontext.h"
|
||||
#include "cairobitmap.h"
|
||||
#include "cairopath.h"
|
||||
#include "cairogradient.h"
|
||||
#include "../../crect.h"
|
||||
#include "../../cgraphicstransform.h"
|
||||
#include "../../ccolor.h"
|
||||
#include "../../cdrawdefs.h"
|
||||
#include "../../clinestyle.h"
|
||||
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stack>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
using TransformMatrix = CGraphicsTransform;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline void checkCairoStatus (const Cairo::ContextHandle& handle)
|
||||
{
|
||||
#if DEBUG
|
||||
auto status = cairo_status (handle);
|
||||
if (status != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
auto msg = cairo_status_to_string (status);
|
||||
DebugPrint ("%s\n", msg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline cairo_matrix_t convert (const TransformMatrix& ct)
|
||||
{
|
||||
return {ct.m11, ct.m21, ct.m12, ct.m22, ct.dx, ct.dy};
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct CairoGraphicsDeviceFactory::Impl
|
||||
{
|
||||
std::vector<std::shared_ptr<CairoGraphicsDevice>> devices;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CairoGraphicsDeviceFactory::CairoGraphicsDeviceFactory ()
|
||||
{
|
||||
impl = std::make_unique<Impl> ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CairoGraphicsDeviceFactory::~CairoGraphicsDeviceFactory () noexcept = default;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGraphicsDevicePtr CairoGraphicsDeviceFactory::getDeviceForScreen (ScreenInfo::Identifier screen) const
|
||||
{
|
||||
if (impl->devices.empty ())
|
||||
{
|
||||
// just create a dummy device as we don't really need the cairo device at the moment
|
||||
impl->devices.push_back (std::make_shared<CairoGraphicsDevice> (nullptr));
|
||||
}
|
||||
return impl->devices.front ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGraphicsDevicePtr CairoGraphicsDeviceFactory::addDevice (cairo_device_t* device)
|
||||
{
|
||||
for (auto& dev : impl->devices)
|
||||
{
|
||||
if (dev->get () == device)
|
||||
return dev;
|
||||
}
|
||||
impl->devices.push_back (std::make_shared<CairoGraphicsDevice> (device));
|
||||
return impl->devices.back ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceFactory::removeDevice (cairo_device_t* device)
|
||||
{
|
||||
const auto citer = std::find_if (impl->devices.cbegin (), impl->devices.cend (),
|
||||
[device] (const auto& dev) { return dev->get () == device; });
|
||||
|
||||
if (citer == impl->devices.cend ())
|
||||
return;
|
||||
|
||||
impl->devices.erase (citer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct CairoGraphicsDevice::Impl
|
||||
{
|
||||
cairo_device_t* device;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CairoGraphicsDevice::CairoGraphicsDevice (cairo_device_t* device)
|
||||
{
|
||||
impl = std::make_unique<Impl> ();
|
||||
impl->device = device;
|
||||
if (device)
|
||||
cairo_device_reference (device);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CairoGraphicsDevice::~CairoGraphicsDevice () noexcept
|
||||
{
|
||||
if (impl->device)
|
||||
{
|
||||
cairo_device_destroy (impl->device);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
cairo_device_t* CairoGraphicsDevice::get () const { return impl->device; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGraphicsDeviceContextPtr
|
||||
CairoGraphicsDevice::createBitmapContext (const PlatformBitmapPtr& bitmap) const
|
||||
{
|
||||
auto cairoBitmap = bitmap.cast<Cairo::Bitmap> ();
|
||||
if (cairoBitmap)
|
||||
{
|
||||
return std::make_shared<CairoGraphicsDeviceContext> (*this, cairoBitmap->getSurface ());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CPoint pixelAlign (const CGraphicsTransform& tm, const CPoint& p)
|
||||
{
|
||||
auto obj = p;
|
||||
tm.transform (obj);
|
||||
obj.x = std::round (obj.x);
|
||||
obj.y = std::round (obj.y);
|
||||
tm.inverse ().transform (obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CRect pixelAlign (const CGraphicsTransform& tm, const CRect& r)
|
||||
{
|
||||
auto obj = r;
|
||||
tm.transform (obj);
|
||||
obj.left = std::round (obj.left);
|
||||
obj.right = std::round (obj.right);
|
||||
obj.top = std::round (obj.top);
|
||||
obj.bottom = std::round (obj.bottom);
|
||||
tm.inverse ().transform (obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct CairoGraphicsDeviceContext::Impl
|
||||
{
|
||||
const CairoGraphicsDevice& device;
|
||||
Cairo::ContextHandle context;
|
||||
Cairo::SurfaceHandle surface;
|
||||
|
||||
Impl (const CairoGraphicsDevice& device, const Cairo::SurfaceHandle& surface)
|
||||
: device (device), surface (surface)
|
||||
{
|
||||
context.assign (cairo_create (surface));
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
void doInContext (Proc p)
|
||||
{
|
||||
auto ct = state.tm;
|
||||
CRect clip = state.clip;
|
||||
if (clip.isEmpty ())
|
||||
return;
|
||||
cairo_save (context);
|
||||
cairo_rectangle (context, clip.left, clip.top, clip.getWidth (), clip.getHeight ());
|
||||
cairo_clip (context);
|
||||
auto matrix = convert (ct);
|
||||
cairo_set_matrix (context, &matrix);
|
||||
auto antialiasMode = state.drawMode.modeIgnoringIntegralMode () == kAntiAliasing
|
||||
? CAIRO_ANTIALIAS_BEST
|
||||
: CAIRO_ANTIALIAS_NONE;
|
||||
cairo_set_antialias (context, antialiasMode);
|
||||
p ();
|
||||
checkCairoStatus (context);
|
||||
cairo_restore (context);
|
||||
}
|
||||
|
||||
void applyLineWidthCTM ()
|
||||
{
|
||||
auto p = calcLineTranslate ();
|
||||
cairo_translate (context, p.x, p.y);
|
||||
}
|
||||
|
||||
CPoint calcLineTranslate () const
|
||||
{
|
||||
CPoint p {};
|
||||
int32_t lineWidthInt = static_cast<int32_t> (state.lineWidth);
|
||||
if (static_cast<CCoord> (lineWidthInt) == state.lineWidth && lineWidthInt % 2)
|
||||
p.x = p.y = 0.5;
|
||||
return p;
|
||||
}
|
||||
|
||||
void applyLineStyle ()
|
||||
{
|
||||
auto lineWidth = state.lineWidth;
|
||||
cairo_set_line_width (context, lineWidth);
|
||||
const auto& style = state.lineStyle;
|
||||
if (!style.getDashLengths ().empty ())
|
||||
{
|
||||
auto lengths = style.getDashLengths ();
|
||||
for (auto& l : lengths)
|
||||
l *= lineWidth;
|
||||
cairo_set_dash (context, lengths.data (), lengths.size (), style.getDashPhase ());
|
||||
}
|
||||
cairo_line_cap_t lineCap;
|
||||
switch (style.getLineCap ())
|
||||
{
|
||||
case CLineStyle::kLineCapButt:
|
||||
{
|
||||
lineCap = CAIRO_LINE_CAP_BUTT;
|
||||
break;
|
||||
}
|
||||
case CLineStyle::kLineCapRound:
|
||||
{
|
||||
lineCap = CAIRO_LINE_CAP_ROUND;
|
||||
break;
|
||||
}
|
||||
case CLineStyle::kLineCapSquare:
|
||||
{
|
||||
lineCap = CAIRO_LINE_CAP_SQUARE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cairo_set_line_cap (context, lineCap);
|
||||
cairo_line_join_t lineJoin;
|
||||
switch (style.getLineJoin ())
|
||||
{
|
||||
case CLineStyle::kLineJoinBevel:
|
||||
{
|
||||
lineJoin = CAIRO_LINE_JOIN_BEVEL;
|
||||
break;
|
||||
}
|
||||
case CLineStyle::kLineJoinMiter:
|
||||
{
|
||||
lineJoin = CAIRO_LINE_JOIN_MITER;
|
||||
break;
|
||||
}
|
||||
case CLineStyle::kLineJoinRound:
|
||||
{
|
||||
lineJoin = CAIRO_LINE_JOIN_ROUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_line_join (context, lineJoin);
|
||||
}
|
||||
|
||||
void setupSourceColor (CColor color)
|
||||
{
|
||||
auto alpha = color.normAlpha<double> ();
|
||||
alpha *= state.globalAlpha;
|
||||
cairo_set_source_rgba (context, color.normRed<double> (), color.normGreen<double> (),
|
||||
color.normBlue<double> (), alpha);
|
||||
checkCairoStatus (context);
|
||||
}
|
||||
void applyFillColor () { setupSourceColor (state.fillColor); }
|
||||
void applyFrameColor () { setupSourceColor (state.frameColor); }
|
||||
void applyFontColor (CColor color) { setupSourceColor (color); }
|
||||
|
||||
void draw (PlatformGraphicsDrawStyle drawStyle)
|
||||
{
|
||||
switch (drawStyle)
|
||||
{
|
||||
case PlatformGraphicsDrawStyle::Stroked:
|
||||
{
|
||||
applyLineStyle ();
|
||||
applyFrameColor ();
|
||||
cairo_stroke (context);
|
||||
break;
|
||||
}
|
||||
case PlatformGraphicsDrawStyle::Filled:
|
||||
{
|
||||
applyFillColor ();
|
||||
cairo_fill (context);
|
||||
break;
|
||||
}
|
||||
case PlatformGraphicsDrawStyle::FilledAndStroked:
|
||||
{
|
||||
applyFillColor ();
|
||||
cairo_fill_preserve (context);
|
||||
applyLineStyle ();
|
||||
applyFrameColor ();
|
||||
cairo_stroke (context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkCairoStatus (context);
|
||||
}
|
||||
|
||||
struct State
|
||||
{
|
||||
CRect clip {};
|
||||
CLineStyle lineStyle {kLineSolid};
|
||||
CDrawMode drawMode {};
|
||||
CColor fillColor {kTransparentCColor};
|
||||
CColor frameColor {kTransparentCColor};
|
||||
CCoord lineWidth {1.};
|
||||
double globalAlpha {1.};
|
||||
TransformMatrix tm {};
|
||||
};
|
||||
State state;
|
||||
std::stack<State> stateStack;
|
||||
double scaleFactor {1.};
|
||||
|
||||
PlatformGraphicsPathFactoryPtr pathFactory;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CairoGraphicsDeviceContext::CairoGraphicsDeviceContext (const CairoGraphicsDevice& device,
|
||||
const Cairo::SurfaceHandle& handle)
|
||||
{
|
||||
impl = std::make_unique<Impl> (device, handle);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CairoGraphicsDeviceContext::~CairoGraphicsDeviceContext () noexcept {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const IPlatformGraphicsDevice& CairoGraphicsDeviceContext::getDevice () const
|
||||
{
|
||||
return impl->device;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
PlatformGraphicsPathFactoryPtr CairoGraphicsDeviceContext::getGraphicsPathFactory () const
|
||||
{
|
||||
if (!impl->pathFactory)
|
||||
impl->pathFactory = std::make_shared<Cairo::GraphicsPathFactory> (impl->context);
|
||||
return impl->pathFactory;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::beginDraw () const
|
||||
{
|
||||
if (impl->context)
|
||||
cairo_save (impl->context);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::endDraw () const
|
||||
{
|
||||
if (impl->context)
|
||||
cairo_restore (impl->context);
|
||||
if (impl->surface)
|
||||
cairo_surface_flush (impl->surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawLine (LinePair line) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
impl->applyLineStyle ();
|
||||
impl->applyFrameColor ();
|
||||
if (impl->state.drawMode.integralMode ())
|
||||
{
|
||||
CPoint start = pixelAlign (impl->state.tm, line.first);
|
||||
CPoint end = pixelAlign (impl->state.tm, line.second);
|
||||
impl->applyLineWidthCTM ();
|
||||
cairo_move_to (impl->context, start.x, start.y);
|
||||
cairo_line_to (impl->context, end.x, end.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_move_to (impl->context, line.first.x, line.first.y);
|
||||
cairo_line_to (impl->context, line.second.x, line.second.y);
|
||||
}
|
||||
cairo_stroke (impl->context);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawLines (const LineList& lines) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
impl->applyLineStyle ();
|
||||
impl->applyFrameColor ();
|
||||
if (impl->state.drawMode.integralMode ())
|
||||
{
|
||||
auto pt = impl->calcLineTranslate ();
|
||||
for (auto& line : lines)
|
||||
{
|
||||
CPoint start = pixelAlign (impl->state.tm, line.first);
|
||||
CPoint end = pixelAlign (impl->state.tm, line.second);
|
||||
cairo_move_to (impl->context, start.x + pt.x, start.y + pt.y);
|
||||
cairo_line_to (impl->context, end.x + pt.x, end.y + pt.y);
|
||||
cairo_stroke (impl->context);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& line : lines)
|
||||
{
|
||||
cairo_move_to (impl->context, line.first.x, line.first.y);
|
||||
cairo_line_to (impl->context, line.second.x, line.second.y);
|
||||
cairo_stroke (impl->context);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawPolygon (const PointList& polygonPointList,
|
||||
PlatformGraphicsDrawStyle drawStyle) const
|
||||
{
|
||||
vstgui_assert (polygonPointList.empty () == false);
|
||||
impl->doInContext ([&] () {
|
||||
bool doPixelAlign = impl->state.drawMode.integralMode ();
|
||||
auto last = polygonPointList.back ();
|
||||
if (doPixelAlign)
|
||||
last = pixelAlign (impl->state.tm, last);
|
||||
cairo_move_to (impl->context, last.x, last.y);
|
||||
for (auto p : polygonPointList)
|
||||
{
|
||||
if (doPixelAlign)
|
||||
p = pixelAlign (impl->state.tm, p);
|
||||
cairo_line_to (impl->context, p.x, p.y);
|
||||
}
|
||||
impl->draw (drawStyle);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
if (drawStyle != PlatformGraphicsDrawStyle::Filled)
|
||||
{
|
||||
rect.right -= 1.;
|
||||
rect.bottom -= 1.;
|
||||
}
|
||||
if (impl->state.drawMode.integralMode ())
|
||||
{
|
||||
rect = pixelAlign (impl->state.tm, rect);
|
||||
if (drawStyle != PlatformGraphicsDrawStyle::Filled)
|
||||
impl->applyLineWidthCTM ();
|
||||
cairo_rectangle (impl->context, rect.left, rect.top, rect.getWidth (),
|
||||
rect.getHeight ());
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_rectangle (impl->context, rect.left + 0.5, rect.top + 0.5, rect.getWidth () - 0.5,
|
||||
rect.getHeight () - 0.5);
|
||||
}
|
||||
impl->draw (drawStyle);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawArc (CRect rect, double startAngle1, double endAngle2,
|
||||
PlatformGraphicsDrawStyle drawStyle) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
CPoint center = rect.getCenter ();
|
||||
cairo_translate (impl->context, center.x, center.y);
|
||||
cairo_scale (impl->context, 2.0 / rect.getWidth (), 2.0 / rect.getHeight ());
|
||||
cairo_arc (impl->context, 0, 0, 1, startAngle1, endAngle2);
|
||||
impl->draw (drawStyle);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
CPoint center = rect.getCenter ();
|
||||
cairo_translate (impl->context, center.x, center.y);
|
||||
cairo_scale (impl->context, 2.0 / rect.getWidth (), 2.0 / rect.getHeight ());
|
||||
cairo_arc (impl->context, 0, 0, 1, 0, 2 * M_PI);
|
||||
impl->draw (drawStyle);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawPoint (CPoint point, CColor color) const { return false; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset,
|
||||
double alpha, BitmapInterpolationQuality quality) const
|
||||
{
|
||||
auto cairoBitmap = dynamic_cast<Cairo::Bitmap*> (&bitmap);
|
||||
if (!cairoBitmap)
|
||||
return false;
|
||||
impl->doInContext ([&] () {
|
||||
cairo_translate (impl->context, dest.left, dest.top);
|
||||
cairo_rectangle (impl->context, 0, 0, dest.getWidth (), dest.getHeight ());
|
||||
cairo_clip (impl->context);
|
||||
|
||||
// Setup a pattern for scaling bitmaps and take it as source afterwards.
|
||||
auto pattern = cairo_pattern_create_for_surface (cairoBitmap->getSurface ());
|
||||
cairo_matrix_t matrix;
|
||||
cairo_pattern_get_matrix (pattern, &matrix);
|
||||
cairo_matrix_init_scale (&matrix, cairoBitmap->getScaleFactor (),
|
||||
cairoBitmap->getScaleFactor ());
|
||||
cairo_matrix_translate (&matrix, offset.x, offset.y);
|
||||
cairo_pattern_set_matrix (pattern, &matrix);
|
||||
cairo_set_source (impl->context, pattern);
|
||||
|
||||
cairo_rectangle (impl->context, -offset.x, -offset.y, dest.getWidth () + offset.x,
|
||||
dest.getHeight () + offset.y);
|
||||
alpha *= impl->state.globalAlpha;
|
||||
if (alpha != 1.f)
|
||||
{
|
||||
cairo_paint_with_alpha (impl->context, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_fill (impl->context);
|
||||
}
|
||||
cairo_pattern_destroy (pattern);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::clearRect (CRect rect) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
cairo_set_operator (impl->context, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_rectangle (impl->context, rect.left, rect.top, rect.getWidth (), rect.getHeight ());
|
||||
cairo_fill (impl->context);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::drawGraphicsPath (IPlatformGraphicsPath& path,
|
||||
PlatformGraphicsPathDrawMode mode,
|
||||
TransformMatrix* transformation) const
|
||||
{
|
||||
auto cairoPath = dynamic_cast<Cairo::GraphicsPath*> (&path);
|
||||
if (!cairoPath)
|
||||
return false;
|
||||
impl->doInContext ([&] () {
|
||||
std::unique_ptr<Cairo::GraphicsPath> alignedPath;
|
||||
if (impl->state.drawMode.integralMode ())
|
||||
{
|
||||
alignedPath = cairoPath->copyPixelAlign ([&] (CPoint p) {
|
||||
p = pixelAlign (impl->state.tm, p);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
auto p = alignedPath ? alignedPath->getCairoPath () : cairoPath->getCairoPath ();
|
||||
if (transformation)
|
||||
{
|
||||
cairo_matrix_t currentMatrix;
|
||||
cairo_matrix_t resultMatrix;
|
||||
auto matrix = convert (*transformation);
|
||||
cairo_get_matrix (impl->context, ¤tMatrix);
|
||||
cairo_matrix_multiply (&resultMatrix, &matrix, ¤tMatrix);
|
||||
cairo_set_matrix (impl->context, &resultMatrix);
|
||||
}
|
||||
cairo_append_path (impl->context, p);
|
||||
switch (mode)
|
||||
{
|
||||
case PlatformGraphicsPathDrawMode::Filled:
|
||||
{
|
||||
impl->applyFillColor ();
|
||||
cairo_fill (impl->context);
|
||||
break;
|
||||
}
|
||||
case PlatformGraphicsPathDrawMode::FilledEvenOdd:
|
||||
{
|
||||
impl->applyFillColor ();
|
||||
cairo_set_fill_rule (impl->context, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
cairo_fill (impl->context);
|
||||
break;
|
||||
}
|
||||
case PlatformGraphicsPathDrawMode::Stroked:
|
||||
{
|
||||
impl->applyLineStyle ();
|
||||
impl->applyFrameColor ();
|
||||
cairo_stroke (impl->context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::fillLinearGradient (IPlatformGraphicsPath& path,
|
||||
const IPlatformGradient& gradient,
|
||||
CPoint startPoint, CPoint endPoint,
|
||||
bool evenOdd,
|
||||
TransformMatrix* transformation) const
|
||||
{
|
||||
auto cairoPath = dynamic_cast<Cairo::GraphicsPath*> (&path);
|
||||
if (!cairoPath)
|
||||
return false;
|
||||
auto cairoGradient = dynamic_cast<const Cairo::Gradient*> (&gradient);
|
||||
if (!cairoGradient)
|
||||
return false;
|
||||
impl->doInContext ([&] () {
|
||||
std::unique_ptr<Cairo::GraphicsPath> alignedPath;
|
||||
if (impl->state.drawMode.integralMode ())
|
||||
{
|
||||
alignedPath = cairoPath->copyPixelAlign ([&] (CPoint p) {
|
||||
p = pixelAlign (impl->state.tm, p);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
auto p = alignedPath ? alignedPath->getCairoPath () : cairoPath->getCairoPath ();
|
||||
cairo_append_path (impl->context, p);
|
||||
cairo_set_source (impl->context, cairoGradient->getLinearGradient (startPoint, endPoint));
|
||||
if (evenOdd)
|
||||
cairo_set_fill_rule (impl->context, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
cairo_fill (impl->context);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CairoGraphicsDeviceContext::fillRadialGradient (IPlatformGraphicsPath& path,
|
||||
const IPlatformGradient& gradient,
|
||||
CPoint center, CCoord radius,
|
||||
CPoint originOffset, bool evenOdd,
|
||||
TransformMatrix* transformation) const
|
||||
{
|
||||
auto cairoPath = dynamic_cast<Cairo::GraphicsPath*> (&path);
|
||||
if (!cairoPath)
|
||||
return false;
|
||||
|
||||
auto cairoGradient = dynamic_cast<const Cairo::Gradient*> (&gradient);
|
||||
if (!cairoGradient)
|
||||
return false;
|
||||
impl->doInContext ([&] () {
|
||||
std::unique_ptr<Cairo::GraphicsPath> alignedPath;
|
||||
if (impl->state.drawMode.integralMode ())
|
||||
{
|
||||
alignedPath = cairoPath->copyPixelAlign ([&] (CPoint p) {
|
||||
p = pixelAlign (impl->state.tm, p);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
auto p = alignedPath ? alignedPath->getCairoPath () : cairoPath->getCairoPath ();
|
||||
cairo_append_path (impl->context, p);
|
||||
|
||||
const auto& radialGradient =
|
||||
cairoGradient->getRadialGradient (center, radius, originOffset);
|
||||
cairo_set_source (impl->context, radialGradient);
|
||||
if (evenOdd)
|
||||
cairo_set_fill_rule (impl->context, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
|
||||
cairo_arc (impl->context, 0, 0, 0, 0., M_PI * 2.);
|
||||
cairo_fill (impl->context);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::saveGlobalState () const
|
||||
{
|
||||
cairo_save (impl->context);
|
||||
impl->stateStack.push (impl->state);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::restoreGlobalState () const
|
||||
{
|
||||
vstgui_assert (impl->stateStack.empty () == false,
|
||||
"Unbalanced calls to saveGlobalState and restoreGlobalState");
|
||||
#if NDEBUG
|
||||
if (impl->stateStack.empty ())
|
||||
return;
|
||||
#endif
|
||||
cairo_restore (impl->context);
|
||||
impl->state = impl->stateStack.top ();
|
||||
impl->stateStack.pop ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setLineStyle (const CLineStyle& style) const
|
||||
{
|
||||
impl->state.lineStyle = style;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setLineWidth (CCoord width) const
|
||||
{
|
||||
|
||||
impl->state.lineWidth = width;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setDrawMode (CDrawMode mode) const { impl->state.drawMode = mode; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setClipRect (CRect clip) const { impl->state.clip = clip; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setFillColor (CColor color) const
|
||||
{
|
||||
impl->state.fillColor = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setFrameColor (CColor color) const
|
||||
{
|
||||
impl->state.frameColor = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setGlobalAlpha (double newAlpha) const
|
||||
{
|
||||
impl->state.globalAlpha = newAlpha;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::setTransformMatrix (const TransformMatrix& tm) const
|
||||
{
|
||||
impl->state.tm = tm;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const IPlatformGraphicsDeviceContextBitmapExt* CairoGraphicsDeviceContext::asBitmapExt () const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CairoGraphicsDeviceContext::drawPangoLayout (void* layout, CPoint pos, CColor color) const
|
||||
{
|
||||
impl->doInContext ([&] () {
|
||||
impl->applyFontColor (color);
|
||||
cairo_move_to (impl->context, pos.x, pos.y);
|
||||
pango_cairo_show_layout (impl->context, reinterpret_cast<PangoLayout*> (layout));
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformgraphicsdevice.h"
|
||||
|
||||
#include "cairoutils.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
class CairoGraphicsDevice;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CairoGraphicsDeviceContext : public IPlatformGraphicsDeviceContext
|
||||
{
|
||||
public:
|
||||
CairoGraphicsDeviceContext (const CairoGraphicsDevice& device,
|
||||
const Cairo::SurfaceHandle& handle);
|
||||
~CairoGraphicsDeviceContext () noexcept;
|
||||
|
||||
const IPlatformGraphicsDevice& getDevice () const override;
|
||||
PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const override;
|
||||
|
||||
bool beginDraw () const override;
|
||||
bool endDraw () const override;
|
||||
// draw commands
|
||||
bool drawLine (LinePair line) const override;
|
||||
bool drawLines (const LineList& lines) const override;
|
||||
bool drawPolygon (const PointList& polygonPointList,
|
||||
PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawArc (CRect rect, double startAngle1, double endAngle2,
|
||||
PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawPoint (CPoint point, CColor color) const override;
|
||||
bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha,
|
||||
BitmapInterpolationQuality quality) const override;
|
||||
bool clearRect (CRect rect) const override;
|
||||
bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode,
|
||||
TransformMatrix* transformation) const override;
|
||||
bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient,
|
||||
CPoint startPoint, CPoint endPoint, bool evenOdd,
|
||||
TransformMatrix* transformation) const override;
|
||||
bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient,
|
||||
CPoint center, CCoord radius, CPoint originOffset, bool evenOdd,
|
||||
TransformMatrix* transformation) const override;
|
||||
// state
|
||||
void saveGlobalState () const override;
|
||||
void restoreGlobalState () const override;
|
||||
void setLineStyle (const CLineStyle& style) const override;
|
||||
void setLineWidth (CCoord width) const override;
|
||||
void setDrawMode (CDrawMode mode) const override;
|
||||
void setClipRect (CRect clip) const override;
|
||||
void setFillColor (CColor color) const override;
|
||||
void setFrameColor (CColor color) const override;
|
||||
void setGlobalAlpha (double newAlpha) const override;
|
||||
void setTransformMatrix (const TransformMatrix& tm) const override;
|
||||
|
||||
// extension
|
||||
const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const override;
|
||||
|
||||
// private
|
||||
void drawPangoLayout (void* layout, CPoint pos, CColor color) const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CairoGraphicsDevice : public IPlatformGraphicsDevice
|
||||
{
|
||||
public:
|
||||
CairoGraphicsDevice (cairo_device_t* device);
|
||||
~CairoGraphicsDevice () noexcept;
|
||||
|
||||
PlatformGraphicsDeviceContextPtr
|
||||
createBitmapContext (const PlatformBitmapPtr& bitmap) const override;
|
||||
|
||||
cairo_device_t* get () const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CairoGraphicsDeviceFactory : public IPlatformGraphicsDeviceFactory
|
||||
{
|
||||
public:
|
||||
CairoGraphicsDeviceFactory ();
|
||||
~CairoGraphicsDeviceFactory () noexcept;
|
||||
|
||||
PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const override;
|
||||
|
||||
PlatformGraphicsDevicePtr addDevice (cairo_device_t* device);
|
||||
void removeDevice (cairo_device_t* device);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,195 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "../../cgradient.h"
|
||||
#include "../../cgraphicstransform.h"
|
||||
#include "cairopath.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
GraphicsPathFactory::GraphicsPathFactory (const ContextHandle& cr) : context (cr) {}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGraphicsPathPtr
|
||||
GraphicsPathFactory::createPath ([[maybe_unused]] PlatformGraphicsPathFillMode fillMode)
|
||||
{
|
||||
return std::make_unique<GraphicsPath> (context);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGraphicsPathPtr GraphicsPathFactory::createTextPath (const PlatformFontPtr& font,
|
||||
UTF8StringPtr text)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
GraphicsPath::GraphicsPath (const ContextHandle& c) : context (c)
|
||||
{
|
||||
cairo_save (context);
|
||||
cairo_new_path (context);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
GraphicsPath::~GraphicsPath () noexcept
|
||||
{
|
||||
cairo_path_destroy (path);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::finishBuilding ()
|
||||
{
|
||||
path = cairo_copy_path (context);
|
||||
cairo_restore (context);
|
||||
cairo_new_path (context); // clear path in context
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise)
|
||||
{
|
||||
auto radiusX = (rect.right - rect.left) / 2.;
|
||||
auto radiusY = (rect.bottom - rect.top) / 2.;
|
||||
|
||||
auto centerX = static_cast<double> (rect.left + radiusX);
|
||||
auto centerY = static_cast<double> (rect.top + radiusY);
|
||||
|
||||
startAngle = radians (startAngle);
|
||||
endAngle = radians (endAngle);
|
||||
if (radiusX != radiusY)
|
||||
{
|
||||
startAngle = std::atan2 (std::sin (startAngle) * radiusX, std::cos (startAngle) * radiusY);
|
||||
endAngle = std::atan2 (std::sin (endAngle) * radiusX, std::cos (endAngle) * radiusY);
|
||||
}
|
||||
cairo_matrix_t matrix;
|
||||
cairo_get_matrix (context, &matrix);
|
||||
cairo_translate (context, centerX, centerY);
|
||||
cairo_scale (context, radiusX, radiusY);
|
||||
if (clockwise)
|
||||
{
|
||||
cairo_arc (context, 0, 0, 1, startAngle, endAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_arc_negative (context, 0, 0, 1, startAngle, endAngle);
|
||||
}
|
||||
cairo_set_matrix (context, &matrix);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::addEllipse (const CRect& rect) { addArc (rect, 0, 360, true); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::addRect (const CRect& rect)
|
||||
{
|
||||
cairo_rectangle (context, rect.left, rect.top, rect.getWidth (), rect.getHeight ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::addLine (const CPoint& to)
|
||||
{
|
||||
cairo_line_to (context, to.x, to.y);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2,
|
||||
const CPoint& end)
|
||||
{
|
||||
cairo_curve_to (context, control1.x, control1.y, control2.x, control2.y, end.x, end.y);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::beginSubpath (const CPoint& start)
|
||||
{
|
||||
cairo_new_sub_path (context);
|
||||
cairo_move_to (context, start.x, start.y);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void GraphicsPath::closeSubpath ()
|
||||
{
|
||||
cairo_close_path (context);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
std::unique_ptr<GraphicsPath> GraphicsPath::copyPixelAlign (const std::function<CPoint (CPoint)>& pixelAlignFunc)
|
||||
{
|
||||
auto result = std::make_unique<GraphicsPath> (context);
|
||||
cairo_append_path (context, path);
|
||||
result->finishBuilding ();
|
||||
auto rpath = result->path;
|
||||
|
||||
auto align = [&] (_cairo_path_data_t* data, int index) {
|
||||
CPoint input (data[index].point.x, data[index].point.y);
|
||||
auto output = pixelAlignFunc (input);
|
||||
data[index].point.x = output.x;
|
||||
data[index].point.y = output.y;
|
||||
};
|
||||
for (auto i = 0; i < rpath->num_data; i += rpath->data[i].header.length)
|
||||
{
|
||||
auto data = &rpath->data[i];
|
||||
switch (data->header.type)
|
||||
{
|
||||
case CAIRO_PATH_MOVE_TO:
|
||||
{
|
||||
align (data, 1);
|
||||
break;
|
||||
}
|
||||
case CAIRO_PATH_LINE_TO:
|
||||
{
|
||||
align (data, 1);
|
||||
break;
|
||||
}
|
||||
case CAIRO_PATH_CURVE_TO:
|
||||
{
|
||||
align (data, 1);
|
||||
align (data, 2);
|
||||
align (data, 3);
|
||||
break;
|
||||
}
|
||||
case CAIRO_PATH_CLOSE_PATH: { break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool GraphicsPath::hitTest (const CPoint& p, bool evenOddFilled,
|
||||
CGraphicsTransform* transform) const
|
||||
{
|
||||
auto tp = p;
|
||||
if (transform)
|
||||
transform->transform (tp);
|
||||
cairo_save (context);
|
||||
cairo_new_path (context);
|
||||
cairo_append_path (context, path);
|
||||
cairo_set_fill_rule (context,
|
||||
evenOddFilled ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
|
||||
cairo_clip (context);
|
||||
auto result = cairo_in_clip (context, tp.x, tp.y);
|
||||
cairo_restore (context);
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRect GraphicsPath::getBoundingBox () const
|
||||
{
|
||||
CRect r;
|
||||
cairo_save (context);
|
||||
cairo_new_path (context);
|
||||
cairo_append_path (context, path);
|
||||
CPoint p1, p2;
|
||||
cairo_path_extents (context, &p1.x, &p1.y, &p2.x, &p2.y);
|
||||
cairo_restore (context);
|
||||
r.setTopLeft (p1);
|
||||
r.setBottomRight (p2);
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,65 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../cgraphicspath.h"
|
||||
#include "../iplatformgraphicspath.h"
|
||||
#include "cairoutils.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class GraphicsPathFactory : public IPlatformGraphicsPathFactory
|
||||
{
|
||||
public:
|
||||
GraphicsPathFactory (const ContextHandle& cr);
|
||||
|
||||
PlatformGraphicsPathPtr createPath (PlatformGraphicsPathFillMode fillMode) override;
|
||||
PlatformGraphicsPathPtr createTextPath (const PlatformFontPtr& font,
|
||||
UTF8StringPtr text) override;
|
||||
|
||||
private:
|
||||
ContextHandle context;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class GraphicsPath : public IPlatformGraphicsPath
|
||||
{
|
||||
public:
|
||||
GraphicsPath (const ContextHandle& c);
|
||||
~GraphicsPath () noexcept;
|
||||
|
||||
cairo_path_t* getCairoPath () const { return path; }
|
||||
std::unique_ptr<GraphicsPath>
|
||||
copyPixelAlign (const std::function<CPoint (CPoint)>& pixelAlignFunc);
|
||||
|
||||
// IPlatformGraphicsPath
|
||||
void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) override;
|
||||
void addEllipse (const CRect& rect) override;
|
||||
void addRect (const CRect& rect) override;
|
||||
void addLine (const CPoint& to) override;
|
||||
void addBezierCurve (const CPoint& control1, const CPoint& control2,
|
||||
const CPoint& end) override;
|
||||
void beginSubpath (const CPoint& start) override;
|
||||
void closeSubpath () override;
|
||||
void finishBuilding () override;
|
||||
bool hitTest (const CPoint& p, bool evenOddFilled = false,
|
||||
CGraphicsTransform* transform = nullptr) const override;
|
||||
CRect getBoundingBox () const override;
|
||||
PlatformGraphicsPathFillMode getFillMode () const override
|
||||
{
|
||||
return PlatformGraphicsPathFillMode::Ignored;
|
||||
}
|
||||
|
||||
private:
|
||||
ContextHandle context;
|
||||
cairo_path_t* path {nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,81 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <utility>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Cairo {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename Type, typename RetainProcType, RetainProcType RetainProc,
|
||||
typename ReleaseProcType, ReleaseProcType ReleaseProc>
|
||||
class Handle
|
||||
{
|
||||
public:
|
||||
Handle () {}
|
||||
explicit Handle (Type h) : handle (h) {}
|
||||
~Handle () { reset (); }
|
||||
Handle (Handle&& o) { *this = std::move (o); }
|
||||
Handle& operator= (Handle&& o)
|
||||
{
|
||||
reset ();
|
||||
std::swap (handle, o.handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Handle (const Handle& o) { *this = o; }
|
||||
Handle& operator= (const Handle& o)
|
||||
{
|
||||
reset ();
|
||||
if (o.handle)
|
||||
{
|
||||
handle = RetainProc (o.handle);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void assign (Type h)
|
||||
{
|
||||
reset ();
|
||||
handle = h;
|
||||
}
|
||||
|
||||
void reset ()
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
ReleaseProc (handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
operator Type () const { return handle; }
|
||||
operator bool () const { return handle != nullptr; }
|
||||
|
||||
private:
|
||||
Type handle {nullptr};
|
||||
};
|
||||
|
||||
using ContextHandle = Handle<cairo_t*, decltype (&cairo_reference), cairo_reference,
|
||||
decltype (&cairo_destroy), cairo_destroy>;
|
||||
|
||||
using SurfaceHandle =
|
||||
Handle<cairo_surface_t*, decltype (&cairo_surface_reference), cairo_surface_reference,
|
||||
decltype (&cairo_surface_destroy), cairo_surface_destroy>;
|
||||
|
||||
using PatternHandle =
|
||||
Handle<cairo_pattern_t*, decltype (&cairo_pattern_reference), cairo_pattern_reference,
|
||||
decltype (&cairo_pattern_destroy), cairo_pattern_destroy>;
|
||||
|
||||
using ScaledFontHandle = Handle<cairo_scaled_font_t*, decltype (&cairo_scaled_font_reference),
|
||||
cairo_scaled_font_reference, decltype (&cairo_scaled_font_destroy),
|
||||
cairo_scaled_font_destroy>;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // Cairo
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,7 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../platform_x11.h"
|
||||
@@ -0,0 +1,332 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cairobitmap.h"
|
||||
#include "cairofont.h"
|
||||
#include "cairogradient.h"
|
||||
#include "cairographicscontext.h"
|
||||
#include "x11frame.h"
|
||||
#if VSTGUI_ENABLE_WAYLAND_SUPPORT
|
||||
#include "waylandframe.h"
|
||||
#include "waylandplatform.h"
|
||||
#endif
|
||||
#include "../iplatformframecallback.h"
|
||||
#include "../common/fileresourceinputstream.h"
|
||||
#include "../iplatformresourceinputstream.h"
|
||||
#include "../iplatformgraphicsdevice.h"
|
||||
#include "linuxstring.h"
|
||||
#include "x11timer.h"
|
||||
#include "x11fileselector.h"
|
||||
#include "linuxtaskexecutor.h"
|
||||
#include "linuxfactory.h"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <X11/X.h>
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
|
||||
struct wl_display;
|
||||
struct xdg_surface;
|
||||
struct xdg_toplevel;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct LinuxFactory::Impl
|
||||
{
|
||||
std::string resPath;
|
||||
std::unique_ptr<CairoGraphicsDeviceFactory> graphicsDeviceFactory {std::make_unique<CairoGraphicsDeviceFactory> ()};
|
||||
PlatformTaskExecutorPtr taskExecutor {std::make_unique<LinuxTaskExecutor> ()};
|
||||
SharedPointer<IRunLoop> runLoop {};
|
||||
|
||||
void setupResPath (void* handle)
|
||||
{
|
||||
if (handle && resPath.empty ())
|
||||
{
|
||||
struct link_map* map;
|
||||
if (dlinfo (handle, RTLD_DI_LINKMAP, &map) == 0)
|
||||
{
|
||||
auto path = std::string (map->l_name);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
int delPos = path.find_last_of ('/');
|
||||
if (delPos == -1)
|
||||
{
|
||||
fprintf (stderr, "Could not determine bundle location.\n");
|
||||
return; // unexpected
|
||||
}
|
||||
path.erase (delPos, path.length () - delPos);
|
||||
}
|
||||
auto rp = realpath (path.data (), nullptr);
|
||||
path = rp;
|
||||
free (rp);
|
||||
path += "/Contents/Resources/";
|
||||
std::swap (resPath, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
LinuxFactory::LinuxFactory (void* soHandle)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
impl->setupResPath (soHandle);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void LinuxFactory::finalize () noexcept { impl->taskExecutor->waitAllTasksExecuted (); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void LinuxFactory::setResourcePath (const std::string& path) const noexcept
|
||||
{
|
||||
impl->resPath = path;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string LinuxFactory::getResourcePath () const noexcept
|
||||
{
|
||||
return impl->resPath;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void LinuxFactory::setScheduleMainQueueTaskFunc (
|
||||
LinuxTaskExecutor::ScheduleMainQueueTaskFunc&& func) const noexcept
|
||||
{
|
||||
if (auto lte = dynamic_cast<LinuxTaskExecutor*> (impl->taskExecutor.get ()))
|
||||
{
|
||||
lte->setScheduleMainQueueTaskFunc (std::move (func));
|
||||
}
|
||||
else
|
||||
{
|
||||
vstgui_assert (false, "cannot set the func on a custom task executor");
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void LinuxFactory::setRunLoop (const SharedPointer<IRunLoop>& runLoop) const noexcept
|
||||
{
|
||||
impl->runLoop = runLoop;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const SharedPointer<IRunLoop>& LinuxFactory::getRunLoop () const noexcept { return impl->runLoop; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint64_t LinuxFactory::getTicks () const noexcept
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return duration_cast<milliseconds> (steady_clock::now ().time_since_epoch ()).count ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFramePtr LinuxFactory::createFrame (IPlatformFrameCallback* frame, const CRect& size,
|
||||
void* parent, PlatformType parentType,
|
||||
IPlatformFrameConfig* config) const noexcept
|
||||
{
|
||||
if (parentType == PlatformType::kDefaultNative || parentType == PlatformType::kX11EmbedWindowID)
|
||||
{
|
||||
auto x11Parent = reinterpret_cast<XID> (parent);
|
||||
return makeOwned<X11::Frame> (frame, size, x11Parent, config);
|
||||
}
|
||||
#if VSTGUI_ENABLE_WAYLAND_SUPPORT
|
||||
if (parentType == PlatformType::kWaylandSurfaceID)
|
||||
{
|
||||
// auto surface = reinterpret_cast<xdg_surface*> (parent);
|
||||
return makeOwned<Wayland::Frame> (frame, size, config);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFontPtr LinuxFactory::createFont (const UTF8String& name, const CCoord& size,
|
||||
const int32_t& style) const noexcept
|
||||
{
|
||||
return makeOwned<Cairo::Font> (name, size, style);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool LinuxFactory::getAllFontFamilies (const FontFamilyCallback& callback) const noexcept
|
||||
{
|
||||
return Cairo::Font::getAllFamilies (callback);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr LinuxFactory::createBitmap (const CPoint& size) const noexcept
|
||||
{
|
||||
return makeOwned<Cairo::Bitmap> (size);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr LinuxFactory::createBitmap (const CResourceDescription& desc) const noexcept
|
||||
{
|
||||
if (auto bitmap = makeOwned<Cairo::Bitmap> ())
|
||||
{
|
||||
if (bitmap->load (desc))
|
||||
return bitmap;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr LinuxFactory::createBitmapFromPath (UTF8StringPtr absolutePath) const noexcept
|
||||
{
|
||||
return Cairo::Bitmap::create (absolutePath);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr LinuxFactory::createBitmapFromMemory (const void* ptr,
|
||||
uint32_t memSize) const noexcept
|
||||
{
|
||||
return Cairo::Bitmap::create (ptr, memSize);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PNGBitmapBuffer LinuxFactory::createBitmapMemoryPNGRepresentation (
|
||||
const PlatformBitmapPtr& bitmap) const noexcept
|
||||
{
|
||||
if (auto cairoBitmap = dynamic_cast<Cairo::Bitmap*> (bitmap.get ()))
|
||||
{
|
||||
return cairoBitmap->createMemoryPNGRepresentation ();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformResourceInputStreamPtr
|
||||
LinuxFactory::createResourceInputStream (const CResourceDescription& desc) const noexcept
|
||||
{
|
||||
if (desc.type == CResourceDescription::kIntegerType)
|
||||
return {};
|
||||
auto path = impl->resPath;
|
||||
path += desc.u.name;
|
||||
return FileResourceInputStream::create (path);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformStringPtr LinuxFactory::createString (UTF8StringPtr utf8String) const noexcept
|
||||
{
|
||||
return makeOwned<LinuxString> (utf8String);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformTimerPtr LinuxFactory::createTimer (IPlatformTimerCallback* callback) const noexcept
|
||||
{
|
||||
#if VSTGUI_ENABLE_WAYLAND_SUPPORT
|
||||
if (auto runLoop = Wayland::RunLoop::instance ().get ())
|
||||
{
|
||||
struct Timer : public IPlatformTimer,
|
||||
public VSTGUI::ITimerHandler
|
||||
{
|
||||
Timer (IPlatformTimerCallback* callback) : callback (callback) {}
|
||||
~Timer () noexcept { stop (); }
|
||||
|
||||
bool start (uint32_t periodMs) override
|
||||
{
|
||||
if (auto runLoop = Wayland::RunLoop::instance ().get ())
|
||||
{
|
||||
runLoop->registerTimer (periodMs, this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool stop () override
|
||||
{
|
||||
if (auto runLoop = Wayland::RunLoop::instance ().get ())
|
||||
{
|
||||
runLoop->unregisterTimer (this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void onTimer () override { callback->fire (); }
|
||||
|
||||
IPlatformTimerCallback* callback;
|
||||
};
|
||||
auto timer = makeOwned<Timer> (callback);
|
||||
return timer;
|
||||
}
|
||||
#endif
|
||||
return makeOwned<X11::Timer> (callback);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool LinuxFactory::setClipboard (const DataPackagePtr& data) const noexcept
|
||||
{
|
||||
// TODO: Linux Clipboard Implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
auto LinuxFactory::getClipboard () const noexcept -> DataPackagePtr
|
||||
{
|
||||
// TODO: Linux Clipboard Implementation
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGradientPtr LinuxFactory::createGradient () const noexcept
|
||||
{
|
||||
return std::make_unique<Cairo::Gradient> ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFileSelectorPtr LinuxFactory::createFileSelector (PlatformFileSelectorStyle style,
|
||||
IPlatformFrame* frame) const noexcept
|
||||
{
|
||||
auto x11Frame = dynamic_cast<X11::Frame*> (frame);
|
||||
return X11::createFileSelector (style, x11Frame);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const IPlatformGraphicsDeviceFactory& LinuxFactory::getGraphicsDeviceFactory () const noexcept
|
||||
{
|
||||
return *impl->graphicsDeviceFactory.get ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const IPlatformTaskExecutor& LinuxFactory::getTaskExecutor () const noexcept
|
||||
{
|
||||
return *impl->taskExecutor;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool LinuxFactory::replaceTaskExecutor (const ReplaceTaskExecFunc& replaceFunc) const noexcept
|
||||
{
|
||||
if (!replaceFunc)
|
||||
return false;
|
||||
impl->taskExecutor = replaceFunc (std::move (impl->taskExecutor));
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const LinuxFactory* LinuxFactory::asLinuxFactory () const noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const MacFactory* LinuxFactory::asMacFactory () const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Win32Factory* LinuxFactory::asWin32Factory () const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CairoGraphicsDeviceFactory& LinuxFactory::getCairoGraphicsDeviceFactory () const noexcept
|
||||
{
|
||||
return *impl->graphicsDeviceFactory.get ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,155 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../platformfactory.h"
|
||||
#include "../platform_linux.h"
|
||||
#include "linuxtaskexecutor.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
class CairoGraphicsDeviceFactory;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class LinuxFactory final : public IPlatformFactory
|
||||
{
|
||||
public:
|
||||
LinuxFactory (void* soHandle);
|
||||
|
||||
void setResourcePath (const std::string& path) const noexcept;
|
||||
std::string getResourcePath () const noexcept;
|
||||
|
||||
void setScheduleMainQueueTaskFunc (
|
||||
LinuxTaskExecutor::ScheduleMainQueueTaskFunc&& func) const noexcept;
|
||||
|
||||
void setRunLoop (const SharedPointer<IRunLoop>& runLoop) const noexcept;
|
||||
const SharedPointer<IRunLoop>& getRunLoop () const noexcept;
|
||||
|
||||
void finalize () noexcept final;
|
||||
|
||||
/** Return platform ticks (millisecond resolution)
|
||||
* @return ticks
|
||||
*/
|
||||
uint64_t getTicks () const noexcept final;
|
||||
|
||||
/** Create a platform frame object
|
||||
* @param frame callback
|
||||
* @param size size
|
||||
* @param parent platform parent object
|
||||
* @param parentType type of platform parent object
|
||||
* @param config optional config object
|
||||
* @return platform frame or nullptr on failure
|
||||
*/
|
||||
PlatformFramePtr createFrame (IPlatformFrameCallback* frame, const CRect& size, void* parent,
|
||||
PlatformType parentType,
|
||||
IPlatformFrameConfig* config = nullptr) const noexcept final;
|
||||
|
||||
/** Create a platform font object
|
||||
* @param name name of the font
|
||||
* @param size font size
|
||||
* @param style font style
|
||||
* @return platform font or nullptr on failure
|
||||
*/
|
||||
PlatformFontPtr createFont (const UTF8String& name, const CCoord& size,
|
||||
const int32_t& style) const noexcept final;
|
||||
/** Query all platform font families
|
||||
* @param callback callback called for every font
|
||||
* @return true on success
|
||||
*/
|
||||
bool getAllFontFamilies (const FontFamilyCallback& callback) const noexcept final;
|
||||
|
||||
/** Create an empty platform bitmap object
|
||||
* @param size size of the bitmap
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmap (const CPoint& size) const noexcept final;
|
||||
/** Create a platform bitmap object from a resource description
|
||||
* @param desc description where to find the bitmap
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmap (const CResourceDescription& desc) const noexcept final;
|
||||
/** Create a platform bitmap object from a file
|
||||
* @param absolutePath the absolute path of the bitmap file location
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmapFromPath (UTF8StringPtr absolutePath) const noexcept final;
|
||||
/** Create a platform bitmap object from memory
|
||||
* @param ptr memory location
|
||||
* @param memSize memory size
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmapFromMemory (const void* ptr,
|
||||
uint32_t memSize) const noexcept final;
|
||||
/** Create a memory representation of the platform bitmap in PNG format.
|
||||
* @param bitmap the platform bitmap object
|
||||
* @return memory buffer containing the PNG representation of the bitmap
|
||||
*/
|
||||
PNGBitmapBuffer
|
||||
createBitmapMemoryPNGRepresentation (const PlatformBitmapPtr& bitmap) const noexcept final;
|
||||
|
||||
/** Create a platform resource input stream
|
||||
* @param desc description where to find the file to open
|
||||
* @return platform resource input stream or nullptr if not found
|
||||
*/
|
||||
PlatformResourceInputStreamPtr
|
||||
createResourceInputStream (const CResourceDescription& desc) const noexcept final;
|
||||
|
||||
/** Create a platform string object
|
||||
* @param utf8String optional initial UTF-8 encoded string
|
||||
* @return platform string object or nullptr on failure
|
||||
*/
|
||||
PlatformStringPtr createString (UTF8StringPtr utf8String = nullptr) const noexcept final;
|
||||
|
||||
/** Create a platform timer object
|
||||
* @param callback timer callback object
|
||||
* @return platform timer object or nullptr on failure
|
||||
*/
|
||||
PlatformTimerPtr createTimer (IPlatformTimerCallback* callback) const noexcept final;
|
||||
|
||||
/** Set clipboard data
|
||||
* @param data data to put on the clipboard
|
||||
* @return true on success
|
||||
*/
|
||||
bool setClipboard (const DataPackagePtr& data) const noexcept final;
|
||||
|
||||
/** Get clipboard data
|
||||
* @return data package pointer
|
||||
*/
|
||||
DataPackagePtr getClipboard () const noexcept final;
|
||||
|
||||
/** Create a platform gradient object
|
||||
* @return platform gradient object or nullptr on failure
|
||||
*/
|
||||
PlatformGradientPtr createGradient () const noexcept final;
|
||||
|
||||
/** Create a platform file selector
|
||||
* @param style file selector style
|
||||
* @param frame frame
|
||||
* @return platform file selector or nullptr on failure
|
||||
*/
|
||||
PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style,
|
||||
IPlatformFrame* frame) const noexcept final;
|
||||
|
||||
/** Get the graphics device factory
|
||||
*
|
||||
* @return platform graphics device factory
|
||||
*/
|
||||
const IPlatformGraphicsDeviceFactory& getGraphicsDeviceFactory () const noexcept final;
|
||||
|
||||
const IPlatformTaskExecutor& getTaskExecutor () const noexcept final;
|
||||
bool replaceTaskExecutor (const ReplaceTaskExecFunc& replaceFunc) const noexcept final;
|
||||
|
||||
const LinuxFactory* asLinuxFactory () const noexcept final;
|
||||
const MacFactory* asMacFactory () const noexcept final;
|
||||
const Win32Factory* asWin32Factory () const noexcept final;
|
||||
|
||||
CairoGraphicsDeviceFactory& getCairoGraphicsDeviceFactory () const noexcept;
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,20 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "linuxstring.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
LinuxString::LinuxString (UTF8StringPtr utf8String) : str (utf8String) {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void LinuxString::setUTF8String (UTF8StringPtr utf8String)
|
||||
{
|
||||
str = utf8String ? utf8String : "";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformstring.h"
|
||||
#include <string>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class LinuxString : public IPlatformString
|
||||
{
|
||||
public:
|
||||
LinuxString (UTF8StringPtr utf8String);
|
||||
virtual void setUTF8String (UTF8StringPtr utf8String) override;
|
||||
|
||||
const std::string& get () const { return str; }
|
||||
private:
|
||||
std::string str;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "linuxtaskexecutor.h"
|
||||
#include "../common/threadpooltaskexecutor.h"
|
||||
#include "../../vstguidebug.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
struct LinuxTaskExecutor::Impl
|
||||
{
|
||||
using SerialQueueVector = std::vector<std::unique_ptr<Tasks::Detail::SerialQueue>>;
|
||||
|
||||
Tasks::Detail::ThreadPool threadPool {std::thread::hardware_concurrency ()};
|
||||
uint64_t queueIdentifierCounter {};
|
||||
SerialQueueVector serialQueues;
|
||||
std::mutex serialQueueMutex;
|
||||
ScheduleMainQueueTaskFunc scheduleMainQueueTaskFunc;
|
||||
Tasks::Queue mainQueue {0u};
|
||||
Tasks::Queue backgroundQueue {1u};
|
||||
|
||||
void waitAllTasksExecuted (SerialQueueVector::const_iterator it) const
|
||||
{
|
||||
while ((*it)->empty () == false)
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (1));
|
||||
}
|
||||
|
||||
SerialQueueVector::const_iterator findQueue (uint64_t identifier) const
|
||||
{
|
||||
return std::find_if (serialQueues.begin (), serialQueues.end (),
|
||||
[&] (const auto& el) { return el->getIdentifier (); });
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
LinuxTaskExecutor::LinuxTaskExecutor () { impl = std::make_unique<Impl> (); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
LinuxTaskExecutor::~LinuxTaskExecutor () noexcept {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void LinuxTaskExecutor::setScheduleMainQueueTaskFunc (ScheduleMainQueueTaskFunc&& func)
|
||||
{
|
||||
impl->scheduleMainQueueTaskFunc = std::move (func);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const Tasks::Queue& LinuxTaskExecutor::getMainQueue () const { return impl->mainQueue; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const Tasks::Queue& LinuxTaskExecutor::getBackgroundQueue () const { return impl->backgroundQueue; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Tasks::Queue LinuxTaskExecutor::makeSerialQueue (const char* name) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
|
||||
impl->serialQueues.emplace_back (std::make_unique<Tasks::Detail::SerialQueue> (
|
||||
impl->threadPool, ++impl->queueIdentifierCounter, name));
|
||||
return {impl->queueIdentifierCounter};
|
||||
}
|
||||
|
||||
void LinuxTaskExecutor::releaseSerialQueue (const Tasks::Queue& queue) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
|
||||
auto it = impl->findQueue (queue.identifier);
|
||||
if (it != impl->serialQueues.end ())
|
||||
{
|
||||
impl->waitAllTasksExecuted (it);
|
||||
impl->serialQueues.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void LinuxTaskExecutor::schedule (const Tasks::Queue& queue, Tasks::Task&& task) const
|
||||
{
|
||||
if (queue == getMainQueue ())
|
||||
{
|
||||
if (impl->scheduleMainQueueTaskFunc)
|
||||
impl->scheduleMainQueueTaskFunc (std::move (task));
|
||||
}
|
||||
else if (queue == getBackgroundQueue ())
|
||||
{
|
||||
impl->threadPool.enqueue (std::move (task));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
|
||||
auto it = impl->findQueue (queue.identifier);
|
||||
if (it != impl->serialQueues.end ())
|
||||
(*it)->schedule (std::move (task));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void LinuxTaskExecutor::waitAllTasksExecuted (const Tasks::Queue& queue) const
|
||||
{
|
||||
if (queue == impl->backgroundQueue)
|
||||
{
|
||||
while (!impl->threadPool.empty ())
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (1));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
|
||||
auto it = impl->findQueue (queue.identifier);
|
||||
if (it != impl->serialQueues.end ())
|
||||
impl->waitAllTasksExecuted (it);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void LinuxTaskExecutor::waitAllTasksExecuted () const
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
|
||||
for (auto it = impl->serialQueues.begin (); it != impl->serialQueues.end (); ++it)
|
||||
impl->waitAllTasksExecuted (it);
|
||||
}
|
||||
waitAllTasksExecuted (impl->backgroundQueue);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformtaskexecutor.h"
|
||||
#include <functional>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct LinuxTaskExecutor final : IPlatformTaskExecutor
|
||||
{
|
||||
LinuxTaskExecutor ();
|
||||
~LinuxTaskExecutor () noexcept override;
|
||||
|
||||
const Tasks::Queue& getMainQueue () const final;
|
||||
const Tasks::Queue& getBackgroundQueue () const final;
|
||||
Tasks::Queue makeSerialQueue (const char* name) const final;
|
||||
void releaseSerialQueue (const Tasks::Queue& queue) const final;
|
||||
void schedule (const Tasks::Queue& queue, Tasks::Task&& task) const final;
|
||||
void waitAllTasksExecuted (const Tasks::Queue& queue) const final;
|
||||
void waitAllTasksExecuted () const final;
|
||||
|
||||
using ScheduleMainQueueTaskFunc = std::function<void (Tasks::Task&&)>;
|
||||
void setScheduleMainQueueTaskFunc (ScheduleMainQueueTaskFunc&& func);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "waylandclientcontext.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
// Wayland globals
|
||||
#include "wayland-client-protocol.h"
|
||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "linux-dmabuf-v1-server-protocol.h"
|
||||
#include "linux-dmabuf-v1-client-protocol.h"
|
||||
|
||||
using namespace WaylandServerDelegate;
|
||||
|
||||
namespace VSTGUI::Wayland {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
using ContextListeners = std::vector<IContextListener*>;
|
||||
using NamedWaylandOutput = std::pair<uint32_t, WaylandOutput>;
|
||||
using WaylandOutputs = std::vector<NamedWaylandOutput>;
|
||||
using StringType = std::string;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// WaylandGlobals
|
||||
// https://wayland.freedesktop.org/docs/html/
|
||||
// https://wayland-book.com/introduction.html
|
||||
//------------------------------------------------------------------------
|
||||
struct WaylandGlobals
|
||||
{
|
||||
using WlInterfaceName = std::string;
|
||||
using Globals = std::unordered_map<uint32_t, WlInterfaceName>;
|
||||
|
||||
wl_compositor* compositor {nullptr};
|
||||
wl_seat* seat {nullptr};
|
||||
wl_shm* shm {nullptr};
|
||||
wl_subcompositor* subcompositor {nullptr};
|
||||
xdg_wm_base* wm_base {nullptr};
|
||||
zwp_linux_dmabuf_v1* dmabuf {nullptr};
|
||||
|
||||
Globals objects; // Hold a map of all globals' interface names for better tracking!
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static void handlePing (void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial)
|
||||
{
|
||||
auto globals = reinterpret_cast<WaylandGlobals*> (data);
|
||||
xdg_wm_base_pong (globals->wm_base, serial);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static void addXdgWmBaseListener (WaylandGlobals& globals)
|
||||
{
|
||||
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
||||
.ping = handlePing,
|
||||
};
|
||||
|
||||
xdg_wm_base_add_listener (globals.wm_base, &xdgWmBaseListener, &globals);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static void handleGlobal (void* data, struct wl_registry* registry, uint32_t name, const char* interface,
|
||||
uint32_t version)
|
||||
{
|
||||
printf ("interface: '%s', version: %d, name: %d\n", interface, version, name);
|
||||
|
||||
auto& globals = *reinterpret_cast<WaylandGlobals*> (data);
|
||||
globals.objects.insert ({name, {interface}});
|
||||
if (std::string_view (interface) == wl_compositor_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 6;
|
||||
void* object =
|
||||
wl_registry_bind (registry, name, &wl_compositor_interface, std::min (kVersion, version));
|
||||
auto compositor = reinterpret_cast<wl_compositor*> (object);
|
||||
globals.compositor = compositor;
|
||||
return;
|
||||
}
|
||||
if (std::string_view (interface) == wl_subcompositor_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 1;
|
||||
void* object =
|
||||
wl_registry_bind (registry, name, &wl_subcompositor_interface, std::min (kVersion, version));
|
||||
auto subcompositor = reinterpret_cast<wl_subcompositor*> (object);
|
||||
globals.subcompositor = subcompositor;
|
||||
return;
|
||||
}
|
||||
if (std::string_view (interface) == wl_shm_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 1;
|
||||
void* object = wl_registry_bind (registry, name, &wl_shm_interface, std::min (kVersion, version));
|
||||
auto shm = reinterpret_cast<wl_shm*> (object);
|
||||
globals.shm = shm;
|
||||
return;
|
||||
}
|
||||
if (std::string_view (interface) == xdg_wm_base_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 6;
|
||||
void* object =
|
||||
wl_registry_bind (registry, name, &xdg_wm_base_interface, std::min (kVersion, version));
|
||||
auto wm_base = reinterpret_cast<xdg_wm_base*> (object);
|
||||
globals.wm_base = wm_base;
|
||||
return;
|
||||
}
|
||||
if (std::string_view (interface) == wl_seat_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 8;
|
||||
void* object = wl_registry_bind (registry, name, &wl_seat_interface, std::min (kVersion, version));
|
||||
auto seat = reinterpret_cast<wl_seat*> (object);
|
||||
globals.seat = seat;
|
||||
return;
|
||||
}
|
||||
if (std::string_view (interface) == wl_output_interface.name)
|
||||
{
|
||||
// We bind outputs in the WaylandClientContext
|
||||
/*
|
||||
static constexpr uint32_t kVersion = 3;
|
||||
void* object =
|
||||
wl_registry_bind (registry, name, &wl_output_interface, std::min (kVersion, version));
|
||||
auto output = reinterpret_cast<wl_output*> (object);
|
||||
return;
|
||||
*/
|
||||
}
|
||||
if (std::string_view (interface) == zwp_linux_dmabuf_v1_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 4;
|
||||
void* object = wl_registry_bind (
|
||||
registry, name, &zwp_linux_dmabuf_v1_interface, std::min (kVersion, version));
|
||||
auto dmabuf = reinterpret_cast<zwp_linux_dmabuf_v1*> (object);
|
||||
globals.dmabuf = dmabuf;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static void handleGlobalRemove (void* data, struct wl_registry* registry, uint32_t name)
|
||||
{
|
||||
auto globals = reinterpret_cast<WaylandGlobals*> (data);
|
||||
const auto iter = globals->objects.find (name);
|
||||
if (iter == globals->objects.end ())
|
||||
return;
|
||||
|
||||
globals->objects.erase (iter);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static bool bindGlobals (wl_display* display, WaylandGlobals& globals)
|
||||
{
|
||||
if (!display)
|
||||
return false;
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = handleGlobal,
|
||||
.global_remove = handleGlobalRemove,
|
||||
};
|
||||
|
||||
auto registry = wl_display_get_registry (display);
|
||||
wl_registry_add_listener (registry, ®istry_listener, &globals);
|
||||
|
||||
// Roundtrip to call the listener's callbacks.
|
||||
wl_display_roundtrip (display);
|
||||
|
||||
addXdgWmBaseListener (globals);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// WaylandClientContext::Impl
|
||||
//------------------------------------------------------------------------
|
||||
struct WaylandClientContext::Impl
|
||||
{
|
||||
wl_display* display = nullptr;
|
||||
WaylandGlobals globals;
|
||||
WaylandOutputs outputs;
|
||||
ContextListeners contextListeners;
|
||||
uint32_t seatCapabilities = 0;
|
||||
StringType seatName;
|
||||
|
||||
void setSeatCapabilities (int capabilities);
|
||||
void notifyListeners (IContextListener::ChangeType changeType);
|
||||
|
||||
void addWaylandOutput (uint32_t name, wl_output* out);
|
||||
void removeWaylandOutput (uint32_t name);
|
||||
void addRegistryListener ();
|
||||
void addSeatListener ();
|
||||
void addOutputsListener ();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::setSeatCapabilities (int capabilities)
|
||||
{
|
||||
seatCapabilities = capabilities;
|
||||
// seatCapabilities &= ~(WL_SEAT_CAPABILITY_KEYBOARD);
|
||||
notifyListeners (IContextListener::kSeatCapabilitiesChanged);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::notifyListeners (IContextListener::ChangeType changeType)
|
||||
{
|
||||
for (auto& el : contextListeners)
|
||||
el->contextChanged (changeType);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::addWaylandOutput (uint32_t name, wl_output* output)
|
||||
{
|
||||
outputs.push_back ({name, {output}});
|
||||
notifyListeners (IContextListener::kOutputsChanged);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::removeWaylandOutput (uint32_t name)
|
||||
{
|
||||
auto iter = std::find_if (outputs.begin (), outputs.end (),
|
||||
[name] (const auto& el) { return el.first == name; });
|
||||
|
||||
if (iter == outputs.end ())
|
||||
return;
|
||||
|
||||
outputs.erase (iter);
|
||||
notifyListeners (IContextListener::kOutputsChanged);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::addRegistryListener ()
|
||||
{
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global =
|
||||
[] (void* data, struct wl_registry* registry, uint32_t name, const char* interface,
|
||||
uint32_t version) {
|
||||
// We only track outputs here for now
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
if (std::string_view (interface) == wl_output_interface.name)
|
||||
{
|
||||
static constexpr uint32_t kVersion = 3;
|
||||
void* object =
|
||||
wl_registry_bind (registry, name, &wl_output_interface, std::min (kVersion, version));
|
||||
auto output = reinterpret_cast<wl_output*> (object);
|
||||
self->addWaylandOutput (name, output);
|
||||
return;
|
||||
}
|
||||
|
||||
},
|
||||
.global_remove =
|
||||
[] (void* data, struct wl_registry* registry, uint32_t name) {
|
||||
// We only track outputs here for now
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
self->removeWaylandOutput (name);
|
||||
},
|
||||
};
|
||||
|
||||
auto registry = wl_display_get_registry (display);
|
||||
wl_registry_add_listener (registry, ®istry_listener, this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::addSeatListener ()
|
||||
{
|
||||
static const struct wl_seat_listener listener = {
|
||||
.capabilities =
|
||||
[] (void* data, struct wl_seat* wl_seat, uint32_t capabilities) {
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
self->setSeatCapabilities (capabilities);
|
||||
},
|
||||
.name =
|
||||
[] (void* data, struct wl_seat* wl_seat, const char* name) {
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
if (name)
|
||||
self->seatName = name;
|
||||
}};
|
||||
|
||||
wl_seat_add_listener (globals.seat, &listener, this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void WaylandClientContext::Impl::addOutputsListener ()
|
||||
{
|
||||
static const wl_output_listener listener = {
|
||||
.geometry =
|
||||
[] (void* data, wl_output* wl_output, int32_t x, int32_t y, int32_t physical_width,
|
||||
int32_t physical_height, int32_t subpixel, const char* make, const char* model,
|
||||
int32_t transform) {
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
auto iter = std::find_if (
|
||||
self->outputs.begin (), self->outputs.end (),
|
||||
[wl_output] (const auto& el) { return el.second.handle == wl_output; });
|
||||
|
||||
if (iter != self->outputs.end ())
|
||||
{
|
||||
WaylandOutput& output = iter->second;
|
||||
output.handle = wl_output;
|
||||
output.x = x;
|
||||
output.y = y;
|
||||
output.physicalWidth = physical_width;
|
||||
output.physicalHeight = physical_height;
|
||||
output.subPixelOrientation = subpixel;
|
||||
output.transformType = transform;
|
||||
const std::string_view makeStr {make, sizeof (output.manufacturer)};
|
||||
std::copy (makeStr.begin (), makeStr.end (), output.manufacturer);
|
||||
const std::string_view modelStr {model, sizeof (output.model)};
|
||||
std::copy (modelStr.begin (), modelStr.end (), output.model);
|
||||
}
|
||||
},
|
||||
|
||||
.mode =
|
||||
[] (void* data, wl_output* wl_output, uint32_t flags, int32_t width, int32_t height,
|
||||
int32_t refresh) {
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
auto iter = std::find_if (
|
||||
self->outputs.begin (), self->outputs.end (),
|
||||
[wl_output] (const auto& el) { return el.second.handle == wl_output; });
|
||||
|
||||
if (iter != self->outputs.end ())
|
||||
{
|
||||
auto& output = iter->second;
|
||||
output.width = width;
|
||||
output.height = height;
|
||||
output.refreshRate = refresh;
|
||||
}
|
||||
},
|
||||
|
||||
.done =
|
||||
[] (void* data, wl_output* wl_output) {
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
self->notifyListeners (
|
||||
IContextListener::kOutputsChanged);
|
||||
},
|
||||
|
||||
.scale =
|
||||
[] (void* data, wl_output* wl_output, int32_t factor) {
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
auto iter = std::find_if (
|
||||
self->outputs.begin (), self->outputs.end (),
|
||||
[wl_output] (const auto& el) { return el.second.handle == wl_output; });
|
||||
|
||||
if (iter != self->outputs.end ())
|
||||
{
|
||||
auto& output = iter->second;
|
||||
output.scaleFactor = factor;
|
||||
}
|
||||
},
|
||||
|
||||
.name =
|
||||
[] (void* data, wl_output* wl_output, const char* name) {
|
||||
// Hmm, was never called.
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
},
|
||||
|
||||
.description =
|
||||
[] (void* data, wl_output* wl_output, const char* description) {
|
||||
// Hmm, was never called.
|
||||
auto self = reinterpret_cast<WaylandClientContext::Impl*> (data);
|
||||
},
|
||||
};
|
||||
|
||||
for (auto& output : outputs)
|
||||
wl_output_add_listener (output.second.handle, &listener, this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// WaylandClientContext
|
||||
//------------------------------------------------------------------------
|
||||
WaylandClientContext::WaylandClientContext ()
|
||||
{
|
||||
impl = std::make_unique<Impl> ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
WaylandClientContext::~WaylandClientContext () {}
|
||||
//------------------------------------------------------------------------
|
||||
bool WaylandClientContext::initWayland (wl_display* display)
|
||||
{
|
||||
impl->display = display;
|
||||
bool done = bindGlobals (display, impl->globals);
|
||||
|
||||
impl->addRegistryListener ();
|
||||
impl->addSeatListener ();
|
||||
|
||||
// Initializes seat capabilities, outputs etc.
|
||||
wl_display_roundtrip (impl->display);
|
||||
|
||||
impl->addOutputsListener ();
|
||||
return done;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool WaylandClientContext::addListener (IContextListener* listener)
|
||||
{
|
||||
if (!listener)
|
||||
return false;
|
||||
|
||||
impl->contextListeners.push_back (listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool WaylandClientContext::removeListener (IContextListener* listener)
|
||||
{
|
||||
const auto found = [listener] (const auto el) { return el == listener; };
|
||||
|
||||
auto iter = std::find_if (impl->contextListeners.begin (), impl->contextListeners.end (), found);
|
||||
if (iter == impl->contextListeners.end ())
|
||||
return false;
|
||||
|
||||
impl->contextListeners.erase (iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
wl_compositor* WaylandClientContext::getCompositor () const
|
||||
{
|
||||
return impl->globals.compositor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
wl_subcompositor* WaylandClientContext::getSubCompositor () const
|
||||
{
|
||||
return impl->globals.subcompositor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
wl_shm* WaylandClientContext::getSharedMemory () const
|
||||
{
|
||||
return impl->globals.shm;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
wl_seat* WaylandClientContext::getSeat () const
|
||||
{
|
||||
return impl->globals.seat;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
xdg_wm_base* WaylandClientContext::getWindowManager () const
|
||||
{
|
||||
return impl->globals.wm_base;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
uint32_t WaylandClientContext::getSeatCapabilities () const
|
||||
{
|
||||
return impl->seatCapabilities;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const char* WaylandClientContext::getSeatName () const
|
||||
{
|
||||
return impl->seatName.data ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int WaylandClientContext::countOutputs () const
|
||||
{
|
||||
return impl->outputs.size ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
zwp_linux_dmabuf_v1* WaylandClientContext::getDmaBuffer () const
|
||||
{
|
||||
return impl->globals.dmabuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const WaylandOutput& WaylandClientContext::getOutput (int index) const
|
||||
{
|
||||
if (index < impl->outputs.size ())
|
||||
return impl->outputs[index].second;
|
||||
|
||||
static WaylandOutput output {};
|
||||
return output;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace VSTGUI::Wayland
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "iwaylandclientcontext.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VSTGUI::Wayland {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// WaylandClientContext
|
||||
//------------------------------------------------------------------------
|
||||
class WaylandClientContext final : public WaylandServerDelegate::IWaylandClientContext
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------
|
||||
// WaylandServerDelegate
|
||||
using WaylandOutput = WaylandServerDelegate::WaylandOutput;
|
||||
using IContextListener = WaylandServerDelegate::IContextListener;
|
||||
|
||||
WaylandClientContext ();
|
||||
~WaylandClientContext ();
|
||||
|
||||
bool initWayland (wl_display* display);
|
||||
|
||||
// IWaylandClientContext
|
||||
bool addListener (IContextListener* listener) override;
|
||||
bool removeListener (IContextListener* listener) override;
|
||||
wl_compositor* getCompositor () const override;
|
||||
wl_subcompositor* getSubCompositor () const override;
|
||||
wl_shm* getSharedMemory () const override;
|
||||
wl_seat* getSeat () const override;
|
||||
xdg_wm_base* getWindowManager () const override;
|
||||
uint32_t getSeatCapabilities () const override;
|
||||
const char* getSeatName () const override;
|
||||
int countOutputs () const override;
|
||||
zwp_linux_dmabuf_v1* getDmaBuffer () const override;
|
||||
const WaylandOutput& getOutput (int index) const override;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace VSTGUI::Wayland
|
||||
@@ -0,0 +1,393 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
// Originally written and contributed to VSTGUI by PreSonus Software Ltd.
|
||||
|
||||
#include "waylandframe.h"
|
||||
// #include "waylanddragging.h"
|
||||
#include "waylandutils.h"
|
||||
#include "../../cbuttonstate.h"
|
||||
#include "../../cframe.h"
|
||||
#include "../../crect.h"
|
||||
#include "../../dragging.h"
|
||||
#include "../../vstkeycode.h"
|
||||
#include "../../cinvalidrectlist.h"
|
||||
#include "../iplatformopenglview.h"
|
||||
#include "../iplatformviewlayer.h"
|
||||
#include "../iplatformtextedit.h"
|
||||
#include "../iplatformoptionmenu.h"
|
||||
#include "../common/fileresourceinputstream.h"
|
||||
#include "../common/generictextedit.h"
|
||||
#include "../common/genericoptionmenu.h"
|
||||
#include "cairobitmap.h"
|
||||
#include "linuxfactory.h"
|
||||
#include "cairographicscontext.h"
|
||||
#include "waylandplatform.h"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
|
||||
#include "../../events.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Wayland {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct RedrawTimerHandler : ITimerHandler,
|
||||
NonAtomicReferenceCounted
|
||||
{
|
||||
using RedrawCallback = std::function<void ()>;
|
||||
|
||||
RedrawTimerHandler (uint64_t delay, RedrawCallback&& redrawCallback)
|
||||
: redrawCallback (std::move (redrawCallback))
|
||||
{
|
||||
RunLoop::instance ().get ()->registerTimer (delay, this);
|
||||
}
|
||||
~RedrawTimerHandler () noexcept { RunLoop::instance ().get ()->unregisterTimer (this); }
|
||||
|
||||
void onTimer () override
|
||||
{
|
||||
SharedPointer<RedrawTimerHandler> Self (this);
|
||||
Self->redrawCallback ();
|
||||
}
|
||||
|
||||
RedrawCallback redrawCallback;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct DrawHandler
|
||||
{
|
||||
DrawHandler (ChildWindow& window)
|
||||
: childWindow (window), windowSurface (nullptr), device (nullptr)
|
||||
{
|
||||
onSizeChanged (window.getSize ());
|
||||
}
|
||||
|
||||
void onSizeChanged (const CPoint& size)
|
||||
{
|
||||
drawContext = nullptr;
|
||||
device.reset ();
|
||||
windowSurface.reset ();
|
||||
|
||||
void* buffer = childWindow.getBuffer ();
|
||||
if (buffer == nullptr)
|
||||
return;
|
||||
|
||||
auto s = cairo_image_surface_create_for_data (
|
||||
static_cast<unsigned char*> (buffer), CAIRO_FORMAT_ARGB32, childWindow.getSize ().x,
|
||||
childWindow.getSize ().y, childWindow.getBufferStride ());
|
||||
if (cairo_surface_status (s) != CAIRO_STATUS_SUCCESS)
|
||||
return;
|
||||
|
||||
windowSurface.assign (s);
|
||||
device =
|
||||
getPlatformFactory ().asLinuxFactory ()->getCairoGraphicsDeviceFactory ().addDevice (
|
||||
cairo_surface_get_device (s));
|
||||
auto cairoDevice = std::static_pointer_cast<CairoGraphicsDevice> (device);
|
||||
drawContext = std::make_shared<CairoGraphicsDeviceContext> (*cairoDevice, windowSurface);
|
||||
}
|
||||
|
||||
bool draw (const CInvalidRectList& dirtyRects, IPlatformFrameCallback* frame)
|
||||
{
|
||||
if (drawContext == nullptr)
|
||||
onSizeChanged (childWindow.getSize ());
|
||||
|
||||
if (drawContext == nullptr)
|
||||
return false;
|
||||
|
||||
CRect copyRect;
|
||||
drawContext->beginDraw ();
|
||||
frame->platformDrawRects (drawContext, 1, dirtyRects.data ());
|
||||
for (auto rect : dirtyRects)
|
||||
{
|
||||
if (copyRect.isEmpty ())
|
||||
copyRect = rect;
|
||||
else
|
||||
copyRect.unite (rect);
|
||||
}
|
||||
drawContext->endDraw ();
|
||||
cairo_surface_flush (windowSurface);
|
||||
|
||||
childWindow.commit (copyRect);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ChildWindow& childWindow;
|
||||
Cairo::SurfaceHandle windowSurface;
|
||||
PlatformGraphicsDevicePtr device;
|
||||
std::shared_ptr<CairoGraphicsDeviceContext> drawContext;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Frame::Impl
|
||||
{
|
||||
using RectList = CInvalidRectList;
|
||||
|
||||
ChildWindow window;
|
||||
DrawHandler drawHandler;
|
||||
// TODO: DoubleClickDetector doubleClickDetector;
|
||||
IPlatformFrameCallback* frame;
|
||||
std::unique_ptr<GenericOptionMenuTheme> genericOptionMenuTheme;
|
||||
SharedPointer<RedrawTimerHandler> redrawTimer;
|
||||
RectList dirtyRects;
|
||||
CCursorType currentCursor {kCursorDefault};
|
||||
uint32_t pointerGrabed {0};
|
||||
// TODO: WaylandDragAndDropHandler dndHandler;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Impl (IWaylandFrame* waylandFrame, CPoint size, IPlatformFrameCallback* frame)
|
||||
: window (waylandFrame, size)
|
||||
, drawHandler (window)
|
||||
, frame (frame) //, dndHandler (&window, frame)
|
||||
{
|
||||
window.setFrame (frame);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
~Impl () noexcept {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void setSize (const CRect& size)
|
||||
{
|
||||
window.setSize (size);
|
||||
drawHandler.onSizeChanged (size.getSize ());
|
||||
dirtyRects.clear ();
|
||||
dirtyRects.add (size);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void setCursor (CCursorType cursor)
|
||||
{
|
||||
if (currentCursor == cursor)
|
||||
return;
|
||||
currentCursor = cursor;
|
||||
setCursorInternal (cursor);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void setCursorInternal (CCursorType cursor)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void redraw ()
|
||||
{
|
||||
if (drawHandler.draw (dirtyRects, frame))
|
||||
dirtyRects.clear ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void invalidRect (CRect r)
|
||||
{
|
||||
dirtyRects.add (r);
|
||||
if (redrawTimer)
|
||||
return;
|
||||
redrawTimer = makeOwned<RedrawTimerHandler> (16, [this] () {
|
||||
if (dirtyRects.data ().empty ())
|
||||
return;
|
||||
redraw ();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Frame::Frame (IPlatformFrameCallback* frame, const CRect& size, IPlatformFrameConfig* config)
|
||||
: IPlatformFrame (frame)
|
||||
{
|
||||
auto cfg = dynamic_cast<FrameConfig*> (config);
|
||||
if (cfg && cfg->runLoop)
|
||||
{
|
||||
if (auto f = getPlatformFactory ().asLinuxFactory ())
|
||||
{
|
||||
if (f->getRunLoop () == nullptr)
|
||||
f->setRunLoop (cfg->runLoop);
|
||||
}
|
||||
RunLoop::init (cfg->waylandHost);
|
||||
}
|
||||
|
||||
impl = std::unique_ptr<Impl> (
|
||||
new Impl (cfg ? cfg->waylandFrame : nullptr, {size.getWidth (), size.getHeight ()}, frame));
|
||||
|
||||
frame->platformOnActivate (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Frame::~Frame ()
|
||||
{
|
||||
impl.reset ();
|
||||
RunLoop::exit ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Frame::optionMenuPopupStarted () {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Frame::optionMenuPopupStopped () {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getGlobalPosition (CPoint& pos) const { return false; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::setSize (const CRect& newSize)
|
||||
{
|
||||
vstgui_assert (impl);
|
||||
impl->setSize (newSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getSize (CRect& size) const
|
||||
{
|
||||
size.setSize (impl->window.getSize ());
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getCurrentMousePosition (CPoint& mousePosition) const
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getCurrentMouseButtons (CButtonState& buttons) const
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getCurrentModifiers (Modifiers& modifiers) const
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::setMouseCursor (CCursorType type)
|
||||
{
|
||||
impl->setCursor (type);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::invalidRect (const CRect& rect)
|
||||
{
|
||||
impl->invalidRect (rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::scrollRect (const CRect& src, const CPoint& distance)
|
||||
{
|
||||
(void)src;
|
||||
(void)distance;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::showTooltip (const CRect& rect, const char* utf8Text)
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::hideTooltip ()
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void* Frame::getPlatformRepresentation () const
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return nullptr;
|
||||
// return reinterpret_cast<void*> (getX11WindowID ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformTextEdit> Frame::createPlatformTextEdit (IPlatformTextEditCallback* textEdit)
|
||||
{
|
||||
return makeOwned<GenericTextEdit> (textEdit);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformOptionMenu> Frame::createPlatformOptionMenu ()
|
||||
{
|
||||
auto cFrame = dynamic_cast<CFrame*> (frame);
|
||||
GenericOptionMenuTheme theme;
|
||||
if (impl->genericOptionMenuTheme)
|
||||
theme = *impl->genericOptionMenuTheme.get ();
|
||||
auto optionMenu =
|
||||
makeOwned<GenericOptionMenu> (cFrame, MouseEventButtonState (MouseButton::Left), theme);
|
||||
optionMenu->setListener (this);
|
||||
return optionMenu;
|
||||
}
|
||||
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformOpenGLView> Frame::createPlatformOpenGLView ()
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformViewLayer> Frame::createPlatformViewLayer (
|
||||
IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer)
|
||||
{
|
||||
// optional
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
DragResult Frame::doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap)
|
||||
{
|
||||
return kDragError;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::doDrag (const DragDescription& dragDescription,
|
||||
const SharedPointer<IDragCallback>& callback)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
PlatformType Frame::getPlatformType () const { return PlatformType::kWaylandSurfaceID; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Optional<UTF8String> Frame::convertCurrentKeyEventToText ()
|
||||
{
|
||||
// TODO: return RunLoop::instance ().convertCurrentKeyEventToText ();
|
||||
return Optional<UTF8String> ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme)
|
||||
{
|
||||
if (theme)
|
||||
impl->genericOptionMenuTheme =
|
||||
std::unique_ptr<GenericOptionMenuTheme> (new GenericOptionMenuTheme (*theme));
|
||||
else
|
||||
impl->genericOptionMenuTheme = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Wayland
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
// Originally written and contributed to VSTGUI by PreSonus Software Ltd.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../crect.h"
|
||||
#include "../iplatformframe.h"
|
||||
#include "../iplatformresourceinputstream.h"
|
||||
#include "../platform_wayland.h"
|
||||
#include "../common/genericoptionmenu.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Wayland {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class Frame : public IPlatformFrame,
|
||||
public IGenericOptionMenuListener
|
||||
{
|
||||
public:
|
||||
Frame (IPlatformFrameCallback* frame, const CRect& size, IPlatformFrameConfig* config);
|
||||
~Frame ();
|
||||
|
||||
private:
|
||||
bool getGlobalPosition (CPoint& pos) const override;
|
||||
bool setSize (const CRect& newSize) override;
|
||||
bool getSize (CRect& size) const override;
|
||||
bool getCurrentMousePosition (CPoint& mousePosition) const override;
|
||||
bool getCurrentMouseButtons (CButtonState& buttons) const override;
|
||||
bool getCurrentModifiers (Modifiers& modifiers) const override;
|
||||
bool setMouseCursor (CCursorType type) override;
|
||||
bool invalidRect (const CRect& rect) override;
|
||||
bool scrollRect (const CRect& src, const CPoint& distance) override;
|
||||
bool showTooltip (const CRect& rect, const char* utf8Text) override;
|
||||
bool hideTooltip () override;
|
||||
void* getPlatformRepresentation () const override;
|
||||
SharedPointer<IPlatformTextEdit>
|
||||
createPlatformTextEdit (IPlatformTextEditCallback* textEdit) override;
|
||||
SharedPointer<IPlatformOptionMenu> createPlatformOptionMenu () override;
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
SharedPointer<IPlatformOpenGLView> createPlatformOpenGLView () override;
|
||||
#endif
|
||||
SharedPointer<IPlatformViewLayer> createPlatformViewLayer (
|
||||
IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer) override;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) override;
|
||||
#endif
|
||||
bool doDrag (const DragDescription& dragDescription,
|
||||
const SharedPointer<IDragCallback>& callback) override;
|
||||
|
||||
PlatformType getPlatformType () const override;
|
||||
void onFrameClosed () override {}
|
||||
Optional<UTF8String> convertCurrentKeyEventToText () override;
|
||||
bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) override;
|
||||
|
||||
void optionMenuPopupStarted () override;
|
||||
void optionMenuPopupStopped () override;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Wayland
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,239 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
// Originally written and contributed to VSTGUI by PreSonus Software Ltd.
|
||||
|
||||
#include "waylandplatform.h"
|
||||
#include "linuxfactory.h"
|
||||
#include "../../cfileselector.h"
|
||||
#include "../../cframe.h"
|
||||
#include "../../cstring.h"
|
||||
#include "../../events.h"
|
||||
#include "waylandframe.h"
|
||||
// #include "x11dragging.h"
|
||||
#include "cairobitmap.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <link.h>
|
||||
#include <unordered_map>
|
||||
#include <codecvt>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
//#include <wayland-client.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "waylandclientcontext.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Wayland {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace {
|
||||
|
||||
using VirtMap = std::unordered_map<xkb_keysym_t, VirtualKey>;
|
||||
const VirtMap keyMap = {{XKB_KEY_BackSpace, VirtualKey::Back},
|
||||
{XKB_KEY_Tab, VirtualKey::Tab},
|
||||
{XKB_KEY_Clear, VirtualKey::Clear},
|
||||
{XKB_KEY_Return, VirtualKey::Return},
|
||||
{XKB_KEY_Pause, VirtualKey::Pause},
|
||||
{XKB_KEY_Escape, VirtualKey::Escape},
|
||||
{XKB_KEY_space, VirtualKey::Space},
|
||||
{XKB_KEY_End, VirtualKey::End},
|
||||
{XKB_KEY_Home, VirtualKey::Home},
|
||||
|
||||
{XKB_KEY_Left, VirtualKey::Left},
|
||||
{XKB_KEY_Up, VirtualKey::Up},
|
||||
{XKB_KEY_Right, VirtualKey::Right},
|
||||
{XKB_KEY_Down, VirtualKey::Down},
|
||||
{XKB_KEY_Page_Up, VirtualKey::PageUp},
|
||||
{XKB_KEY_Page_Down, VirtualKey::PageDown},
|
||||
|
||||
{XKB_KEY_Select, VirtualKey::Select},
|
||||
{XKB_KEY_Print, VirtualKey::Print},
|
||||
{XKB_KEY_KP_Enter, VirtualKey::Enter},
|
||||
{XKB_KEY_Insert, VirtualKey::Insert},
|
||||
{XKB_KEY_Delete, VirtualKey::Delete},
|
||||
{XKB_KEY_Help, VirtualKey::Help},
|
||||
// Numpads ???
|
||||
{XKB_KEY_KP_Multiply, VirtualKey::Multiply},
|
||||
{XKB_KEY_KP_Add, VirtualKey::Add},
|
||||
{XKB_KEY_KP_Separator, VirtualKey::Separator},
|
||||
{XKB_KEY_KP_Subtract, VirtualKey::Subtract},
|
||||
{XKB_KEY_KP_Decimal, VirtualKey::Decimal},
|
||||
{XKB_KEY_KP_Divide, VirtualKey::Divide},
|
||||
{XKB_KEY_F1, VirtualKey::F1},
|
||||
{XKB_KEY_F2, VirtualKey::F2},
|
||||
{XKB_KEY_F3, VirtualKey::F3},
|
||||
{XKB_KEY_F4, VirtualKey::F4},
|
||||
{XKB_KEY_F5, VirtualKey::F5},
|
||||
{XKB_KEY_F6, VirtualKey::F6},
|
||||
{XKB_KEY_F7, VirtualKey::F7},
|
||||
{XKB_KEY_F8, VirtualKey::F8},
|
||||
{XKB_KEY_F9, VirtualKey::F9},
|
||||
{XKB_KEY_F10, VirtualKey::F10},
|
||||
{XKB_KEY_F11, VirtualKey::F11},
|
||||
{XKB_KEY_F12, VirtualKey::F12},
|
||||
{XKB_KEY_Num_Lock, VirtualKey::NumLock},
|
||||
{XKB_KEY_Scroll_Lock, VirtualKey::Scroll}, // correct ?
|
||||
#if 0
|
||||
{XKB_KEY_Shift_L, VirtualKey::SHIFT},
|
||||
{XKB_KEY_Shift_R, VirtualKey::SHIFT},
|
||||
{XKB_KEY_Control_L, VirtualKey::CONTROL},
|
||||
{XKB_KEY_Control_R, VirtualKey::CONTROL},
|
||||
{XKB_KEY_Alt_L, VirtualKey::ALT},
|
||||
{XKB_KEY_Alt_R, VirtualKey::ALT},
|
||||
#endif
|
||||
{XKB_KEY_VoidSymbol, VirtualKey::None}};
|
||||
const VirtMap shiftKeyMap = {{XKB_KEY_KP_Page_Up, VirtualKey::PageUp},
|
||||
{XKB_KEY_KP_Page_Down, VirtualKey::PageDown},
|
||||
{XKB_KEY_KP_Home, VirtualKey::Home},
|
||||
{XKB_KEY_KP_End, VirtualKey::End}};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct RunLoop::Impl : IEventHandler
|
||||
{
|
||||
SharedPointer<IWaylandHost> waylandHost;
|
||||
std::atomic<uint32_t> useCount {0};
|
||||
cairo_device_t* device {nullptr};
|
||||
wl_display* display {nullptr};
|
||||
WaylandClientContext clientContext;
|
||||
|
||||
bool inDispatch {false};
|
||||
|
||||
Impl ()
|
||||
{
|
||||
}
|
||||
|
||||
void init (const SharedPointer<IWaylandHost>& inWaylandHost)
|
||||
{
|
||||
if (++useCount != 1)
|
||||
return;
|
||||
|
||||
waylandHost = inWaylandHost;
|
||||
|
||||
if (waylandHost == nullptr)
|
||||
return;
|
||||
|
||||
display = waylandHost->openWaylandConnection ();
|
||||
if (display == nullptr)
|
||||
return;
|
||||
|
||||
clientContext.initWayland (display);
|
||||
|
||||
RunLoop::get ()->registerEventHandler (wl_display_get_fd (display), this);
|
||||
flush ();
|
||||
}
|
||||
|
||||
void exit ()
|
||||
{
|
||||
if (--useCount != 0)
|
||||
return;
|
||||
|
||||
cairo_device_finish (device);
|
||||
cairo_device_destroy (device);
|
||||
device = nullptr;
|
||||
|
||||
RunLoop::get ()->unregisterEventHandler (this);
|
||||
|
||||
flush ();
|
||||
|
||||
if (waylandHost && display)
|
||||
waylandHost->closeWaylandConnection (display);
|
||||
waylandHost = nullptr;
|
||||
|
||||
display = nullptr;
|
||||
}
|
||||
|
||||
void flush ()
|
||||
{
|
||||
if (display && !inDispatch)
|
||||
wl_display_flush (display);
|
||||
}
|
||||
|
||||
// IEventHandler
|
||||
void onEvent () final
|
||||
{
|
||||
inDispatch = true;
|
||||
if (wl_display_prepare_read (display) == 0)
|
||||
wl_display_read_events (display);
|
||||
wl_display_dispatch_pending (display);
|
||||
inDispatch = false;
|
||||
flush ();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
RunLoop& RunLoop::instance ()
|
||||
{
|
||||
static RunLoop gInstance;
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::init (const SharedPointer<IWaylandHost>& waylandHost)
|
||||
{
|
||||
instance ().impl->init (waylandHost);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::exit () { instance ().impl->exit (); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::flush () { instance ().impl->flush (); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const SharedPointer<IRunLoop> RunLoop::get ()
|
||||
{
|
||||
return getPlatformFactory ().asLinuxFactory ()->getRunLoop ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
wl_display* RunLoop::getDisplay () { return instance ().impl->display; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
using namespace WaylandServerDelegate;
|
||||
|
||||
const IWaylandClientContext& RunLoop::getClientContext ()
|
||||
{
|
||||
return instance ().impl->clientContext;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool RunLoop::hasPointerInput ()
|
||||
{
|
||||
return (instance ().impl->clientContext.getSeatCapabilities () & WL_SEAT_CAPABILITY_POINTER) != 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool RunLoop::hasKeyboardInput ()
|
||||
{
|
||||
return (instance ().impl->clientContext.getSeatCapabilities () & WL_SEAT_CAPABILITY_KEYBOARD) != 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
RunLoop::RunLoop () { impl = std::unique_ptr<Impl> (new Impl); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
RunLoop::~RunLoop () noexcept = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::setDevice (cairo_device_t* device)
|
||||
{
|
||||
if (impl->device != device)
|
||||
{
|
||||
cairo_device_destroy (impl->device);
|
||||
impl->device = cairo_device_reference (device);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Wayland
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,61 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
// Originally written and contributed to VSTGUI by PreSonus Software Ltd.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../vstguifwd.h"
|
||||
#include "waylandframe.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
struct xdg_wm_base;
|
||||
struct wl_compositor;
|
||||
struct wl_subcompositor;
|
||||
struct wl_shm;
|
||||
struct wl_seat;
|
||||
|
||||
namespace WaylandServerDelegate {
|
||||
class IWaylandClientContext;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Wayland {
|
||||
|
||||
class Frame;
|
||||
class Timer;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct RunLoop
|
||||
{
|
||||
using IWaylandClientContext = WaylandServerDelegate::IWaylandClientContext;
|
||||
static void init (const SharedPointer<IWaylandHost>& waylandHost);
|
||||
static void exit ();
|
||||
static const SharedPointer<IRunLoop> get ();
|
||||
|
||||
static void flush ();
|
||||
|
||||
static wl_display* getDisplay ();
|
||||
static const IWaylandClientContext& getClientContext ();
|
||||
static bool hasPointerInput ();
|
||||
static bool hasKeyboardInput ();
|
||||
|
||||
void setDevice (cairo_device_t* device);
|
||||
static RunLoop& instance ();
|
||||
|
||||
private:
|
||||
RunLoop ();
|
||||
~RunLoop () noexcept;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Wayland
|
||||
} // VSTGUI
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
// Originally written and contributed to VSTGUI by PreSonus Software Ltd.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "waylandplatform.h"
|
||||
#include "wayland-client-protocol.h"
|
||||
#include "iwaylandclientcontext.h"
|
||||
|
||||
struct wl_subsurface;
|
||||
struct wl_buffer;
|
||||
struct wl_shm_pool;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace Wayland {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// ChildWindow
|
||||
//------------------------------------------------------------------------
|
||||
class ChildWindow : public wl_surface_listener
|
||||
{
|
||||
public:
|
||||
ChildWindow (IWaylandFrame* waylandFrame, CPoint size);
|
||||
~ChildWindow () noexcept;
|
||||
|
||||
void setFrame (IPlatformFrameCallback* frame);
|
||||
IPlatformFrameCallback* getFrame () const;
|
||||
|
||||
void setSize (const CRect& rect);
|
||||
const CPoint& getSize () const;
|
||||
|
||||
void* getBuffer () const;
|
||||
int getBufferStride () const;
|
||||
|
||||
wl_surface* getSurface () const;
|
||||
|
||||
void commit (const CRect& rect);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
private:
|
||||
// wl_surface_listener
|
||||
static void onEnter (void *data, wl_surface *wl_surface, wl_output *output);
|
||||
static void onLeave (void *data, wl_surface *wl_surface, wl_output *output);
|
||||
static void onPreferredBufferScale (void *data, wl_surface *wl_surface, int32_t factor);
|
||||
static void onPreferredBufferTransform (void *data, wl_surface *wl_surface, uint32_t transform);
|
||||
|
||||
static void updateScaleFactor (ChildWindow* self, int32_t factor);
|
||||
|
||||
SharedPointer<IWaylandFrame> waylandFrame;
|
||||
IPlatformFrameCallback* frameCallback;
|
||||
bool initialized;
|
||||
CPoint size;
|
||||
void* data;
|
||||
|
||||
wl_surface* surface;
|
||||
wl_subsurface* subSurface;
|
||||
wl_buffer* buffer;
|
||||
wl_shm_pool* pool;
|
||||
int allocatedSize;
|
||||
int byteSize;
|
||||
int fd;
|
||||
|
||||
void initialize ();
|
||||
void terminate ();
|
||||
void createBuffer ();
|
||||
void destroyBuffer ();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
} // Wayland
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,569 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "x11dragging.h"
|
||||
#include "x11utils.h"
|
||||
#include <glib.h>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#undef None
|
||||
|
||||
#if 0
|
||||
#define DndTrace(fmt, ...) fprintf (stderr, "Dnd " fmt "\n", ## __VA_ARGS__);
|
||||
#else
|
||||
#define DndTrace(fmt, ...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
||||
This is implemented according to the XDND specification, version 5. (1)
|
||||
|
||||
The Xdnd receiver:
|
||||
* receive the `XdndEnter` message
|
||||
* extract the types present in the dragged data
|
||||
- identify a matching mimetype for either path, text, or binary
|
||||
* receive the initial `XdndPosition` message
|
||||
- request the X server to retrieve the data for the matched type
|
||||
- identify this data with our proprietary atom `XVSTGUISelection`
|
||||
- cache this message to process it when ready, in the next step
|
||||
* receive a `SelectionNotify` message for our desired type
|
||||
- data is ready, extract it
|
||||
- finish building the IDataPackage, call `OnDragEnter`
|
||||
- send the XdndStatus message, with the desired drag operation
|
||||
* receive additional `XdndPosition` messages
|
||||
- call `OnDragMove`
|
||||
- send the `XdndStatus` message, with the desired drag operation
|
||||
* either:
|
||||
* receive the `XdndLeave` message
|
||||
- call `OnDragLeave`
|
||||
- clean up
|
||||
* receive the `XdndDrop` message
|
||||
- call `OnDrop` if data is accepted, otherwise `OnDragLeave`
|
||||
- send `XdndFinished`
|
||||
- clean up
|
||||
|
||||
Remark:
|
||||
If the receiver is proxied by another window, the replies of type
|
||||
`XdndStatus` and `XdndFinished` should also be directed to the proxy.
|
||||
This is not supposed to be necessary, but it fixes GTK2 hosts.
|
||||
This GTK2 problem will likely not be fixed. (2)
|
||||
|
||||
References
|
||||
(1) the XDND specification
|
||||
https://freedesktop.org/wiki/Specifications/XDND/
|
||||
(2) GTK does not forward XDND protocol messages for X11 embedded windows
|
||||
https://gitlab.gnome.org/GNOME/gtk/-/issues/2329
|
||||
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
uint32_t XdndDataPackage::getCount () const
|
||||
{
|
||||
return packageData.size ();
|
||||
}
|
||||
|
||||
uint32_t XdndDataPackage::getDataSize (uint32_t index) const
|
||||
{
|
||||
if (index >= packageData.size ())
|
||||
return 0;
|
||||
|
||||
return packageData[index].size ();
|
||||
}
|
||||
|
||||
IDataPackage::Type XdndDataPackage::getDataType (uint32_t index) const
|
||||
{
|
||||
if (index >= packageData.size ())
|
||||
return Type::kError;
|
||||
|
||||
return packageType;
|
||||
}
|
||||
|
||||
uint32_t XdndDataPackage::getData (uint32_t index, const void*& buffer, Type& type) const
|
||||
{
|
||||
if (index >= packageData.size ())
|
||||
{
|
||||
buffer = nullptr;
|
||||
type = Type::kError;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = packageData[index].data ();
|
||||
type = packageType;
|
||||
return packageData[index].size ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
XdndHandler::XdndHandler (ChildWindow* window, IPlatformFrameCallback* frame)
|
||||
: window (window), frame (frame)
|
||||
{
|
||||
}
|
||||
|
||||
void XdndHandler::enter (xcb_client_message_event_t& event, xcb_window_t targetId)
|
||||
{
|
||||
clearState ();
|
||||
|
||||
unsigned protocolVersion = event.data.data32[1] >> 24;
|
||||
if (protocolVersion < 5)
|
||||
return;
|
||||
|
||||
DndTrace ("[recv] Enter window=%08X, source=%08X",
|
||||
event.window,
|
||||
event.data.data32[0]);
|
||||
|
||||
if (!Atoms::xDndSelection.valid () || !Atoms::xVstguiSelection.valid ())
|
||||
return;
|
||||
|
||||
std::vector<xcb_atom_t> typeList = getTypeList (event);
|
||||
IDataPackage::Type packageType = IDataPackage::Type::kError;
|
||||
|
||||
if (dndType == XCB_ATOM_NONE)
|
||||
{
|
||||
dndType = findFilePathType(typeList);
|
||||
if (dndType != XCB_ATOM_NONE)
|
||||
packageType = IDataPackage::kFilePath;
|
||||
}
|
||||
if (dndType == XCB_ATOM_NONE)
|
||||
{
|
||||
dndType = findTextType(typeList);
|
||||
if (dndType != XCB_ATOM_NONE)
|
||||
packageType = IDataPackage::kText;
|
||||
}
|
||||
if (dndType == XCB_ATOM_NONE)
|
||||
{
|
||||
dndType = findBinaryType(typeList);
|
||||
if (dndType != XCB_ATOM_NONE)
|
||||
packageType = IDataPackage::kBinary;
|
||||
}
|
||||
|
||||
if (packageType != IDataPackage::Type::kError)
|
||||
{
|
||||
package = makeOwned<XdndDataPackage> ();
|
||||
package->setPackageType (packageType);
|
||||
|
||||
state = State::DragInitiated;
|
||||
dndTarget = targetId;
|
||||
dndSource = event.data.data32[0];
|
||||
}
|
||||
}
|
||||
|
||||
void XdndHandler::position (xcb_client_message_event_t& event)
|
||||
{
|
||||
DndTrace ("[recv] Position window=%08X, source=%08X, x=%d, y=%d, action=%s",
|
||||
event.window,
|
||||
event.data.data32[0],
|
||||
event.data.data32[2] >> 16, event.data.data32[2] & 0xffff,
|
||||
getAtomName (event.data.data32[4]).c_str ());
|
||||
|
||||
if (event.data.data32[0] != dndSource)
|
||||
return;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case State::DragInitiated:
|
||||
{
|
||||
dndPosition = Optional<xcb_client_message_event_t> (event);
|
||||
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
|
||||
xcb_delete_property (
|
||||
xcb, window->getID (), Atoms::xVstguiSelection ());
|
||||
|
||||
xcb_convert_selection (
|
||||
xcb, window->getID (), Atoms::xDndSelection (), dndType,
|
||||
Atoms::xVstguiSelection (), dndPosition->data.data32[3]);
|
||||
}
|
||||
break;
|
||||
case State::DragEntering:
|
||||
dragOperation = frame->platformOnDragEnter (getEventData ());
|
||||
state = State::DragMoving;
|
||||
replyStatus ();
|
||||
break;
|
||||
case State::DragMoving:
|
||||
dragOperation = frame->platformOnDragMove (getEventData ());
|
||||
replyStatus ();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void XdndHandler::leave (xcb_client_message_event_t& event)
|
||||
{
|
||||
DndTrace ("[recv] Leave window=%08X, source=%08X",
|
||||
event.window,
|
||||
event.data.data32[0]);
|
||||
|
||||
if (event.data.data32[0] != dndSource)
|
||||
return;
|
||||
|
||||
if (dndPosition)
|
||||
{
|
||||
frame->platformOnDragLeave (getEventData ());
|
||||
}
|
||||
|
||||
clearState ();
|
||||
}
|
||||
|
||||
void XdndHandler::drop (xcb_client_message_event_t& event)
|
||||
{
|
||||
DndTrace ("[recv] Drop window=%08X, source=%08X",
|
||||
event.window,
|
||||
event.data.data32[0]);
|
||||
|
||||
if (event.data.data32[0] != dndSource)
|
||||
return;
|
||||
|
||||
if (dndPosition)
|
||||
{
|
||||
if (dragOperation != DragOperation::None)
|
||||
frame->platformOnDrop (getEventData ());
|
||||
else
|
||||
frame->platformOnDragLeave (getEventData ());
|
||||
replyFinished ();
|
||||
}
|
||||
|
||||
clearState ();
|
||||
}
|
||||
|
||||
void XdndHandler::selectionNotify (xcb_selection_notify_event_t& event)
|
||||
{
|
||||
if (state == State::DragInitiated &&
|
||||
event.requestor == window->getID () && event.target == dndType &&
|
||||
Atoms::xDndSelection.valid () && Atoms::xVstguiSelection.valid () &&
|
||||
event.selection == Atoms::xDndSelection () &&
|
||||
event.property == Atoms::xVstguiSelection ())
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
|
||||
auto cookie = xcb_get_property (
|
||||
xcb, true, window->getID (), Atoms::xVstguiSelection (),
|
||||
XCB_GET_PROPERTY_TYPE_ANY, 0, 4096);
|
||||
|
||||
std::vector<std::string> packageData;
|
||||
|
||||
auto reply = xcb_get_property_reply (xcb, cookie, nullptr);
|
||||
if (reply)
|
||||
{
|
||||
std::string data (
|
||||
reinterpret_cast<char*> (xcb_get_property_value (reply)),
|
||||
xcb_get_property_value_length (reply));
|
||||
|
||||
if (Atoms::xMimeTypeUriList.valid () &&
|
||||
dndType == Atoms::xMimeTypeUriList ())
|
||||
{
|
||||
extractFilePathsFromUriList (data, packageData);
|
||||
}
|
||||
else
|
||||
{
|
||||
packageData.resize (1);
|
||||
packageData[0] = std::move (data);
|
||||
}
|
||||
|
||||
free (reply);
|
||||
}
|
||||
|
||||
if (packageData.empty ())
|
||||
clearState ();
|
||||
else
|
||||
{
|
||||
package->setPackageData (std::move (packageData));
|
||||
state = State::DragEntering;
|
||||
|
||||
if (dndPosition)
|
||||
position (*dndPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XdndHandler::clearState ()
|
||||
{
|
||||
state = State::DragClear;
|
||||
dndTarget = 0;
|
||||
dndSource = 0;
|
||||
dndType = XCB_ATOM_NONE;
|
||||
dndPosition.reset ();
|
||||
package = SharedPointer<XdndDataPackage> ();
|
||||
dragOperation = DragOperation::None;
|
||||
}
|
||||
|
||||
DragEventData XdndHandler::getEventData () const
|
||||
{
|
||||
assert (package);
|
||||
|
||||
DragEventData eventData;
|
||||
|
||||
eventData.drag = package.get ();
|
||||
eventData.pos = getEventPosition ();
|
||||
// TODO: the modifiers
|
||||
|
||||
return eventData;
|
||||
}
|
||||
|
||||
CPoint XdndHandler::getEventPosition () const
|
||||
{
|
||||
assert (dndPosition);
|
||||
|
||||
xcb_client_message_event_t event = *dndPosition;
|
||||
|
||||
int x = event.data.data32[2] >> 16;
|
||||
int y = event.data.data32[2] & 0xffff;
|
||||
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
auto setup = xcb_get_setup (xcb);
|
||||
auto iter = xcb_setup_roots_iterator (setup);
|
||||
auto screen = iter.data;
|
||||
|
||||
auto cookie = xcb_translate_coordinates (
|
||||
xcb, screen->root, window->getID (), x, y);
|
||||
auto reply = xcb_translate_coordinates_reply (xcb, cookie, nullptr);
|
||||
if (reply)
|
||||
{
|
||||
x = reply->dst_x;
|
||||
y = reply->dst_y;
|
||||
free (reply);
|
||||
}
|
||||
|
||||
return CPoint (x, y);
|
||||
}
|
||||
|
||||
void XdndHandler::replyStatus ()
|
||||
{
|
||||
if (!Atoms::xDndStatus.valid ())
|
||||
return;
|
||||
|
||||
bool accepted = dragOperation != DragOperation::None;
|
||||
xcb_window_t dndSource = dndPosition->data.data32[0];
|
||||
|
||||
xcb_client_message_event_t event {};
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.format = 32;
|
||||
event.window = dndSource;
|
||||
event.type = Atoms::xDndStatus ();
|
||||
event.data.data32[0] = dndTarget;
|
||||
event.data.data32[1] = accepted;
|
||||
|
||||
switch (dragOperation)
|
||||
{
|
||||
case DragOperation::Copy:
|
||||
if (Atoms::xDndActionCopy.valid ())
|
||||
event.data.data32[4] = Atoms::xDndActionCopy ();
|
||||
break;
|
||||
case DragOperation::Move:
|
||||
if (Atoms::xDndActionMove.valid ())
|
||||
event.data.data32[4] = Atoms::xDndActionMove ();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
|
||||
xcb_window_t receiver = getXdndProxy (dndSource);
|
||||
if (receiver == 0)
|
||||
receiver = dndSource;
|
||||
|
||||
DndTrace ("[send] Status receiver=%08X, window=%08X, target=%08X, accept=%d, x=%d, y=%d, w=%d, h=%d, action=%s",
|
||||
receiver,
|
||||
event.window,
|
||||
event.data.data32[0],
|
||||
event.data.data32[1] & 1,
|
||||
event.data.data32[2] >> 16, event.data.data32[2] & 0xffff,
|
||||
event.data.data32[3] >> 16, event.data.data32[3] & 0xffff,
|
||||
event.data.data32[4] ? getAtomName (event.data.data32[4]).c_str () : "None");
|
||||
|
||||
xcb_send_event (
|
||||
xcb, false, receiver, XCB_EVENT_MASK_NO_EVENT,
|
||||
reinterpret_cast<const char*> (&event));
|
||||
}
|
||||
|
||||
void XdndHandler::replyFinished ()
|
||||
{
|
||||
if (!Atoms::xDndFinished.valid ())
|
||||
return;
|
||||
|
||||
bool accepted = dragOperation != DragOperation::None;
|
||||
xcb_window_t dndSource = dndPosition->data.data32[0];
|
||||
|
||||
xcb_client_message_event_t event {};
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.format = 32;
|
||||
event.window = dndSource;
|
||||
event.type = Atoms::xDndFinished ();
|
||||
event.data.data32[0] = dndTarget;
|
||||
event.data.data32[1] = accepted;
|
||||
|
||||
switch (dragOperation)
|
||||
{
|
||||
case DragOperation::Copy:
|
||||
if (Atoms::xDndActionCopy.valid ())
|
||||
event.data.data32[2] = Atoms::xDndActionCopy ();
|
||||
break;
|
||||
case DragOperation::Move:
|
||||
if (Atoms::xDndActionMove.valid ())
|
||||
event.data.data32[2] = Atoms::xDndActionMove ();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
|
||||
xcb_window_t receiver = getXdndProxy (dndSource);
|
||||
if (receiver == 0)
|
||||
receiver = dndSource;
|
||||
|
||||
DndTrace ("[send] Finished receiver=%08X, window=%08X, target=%08X, accept=%d, action=%s",
|
||||
receiver,
|
||||
event.window,
|
||||
event.data.data32[0],
|
||||
event.data.data32[1] & 1,
|
||||
event.data.data32[2] ? getAtomName (event.data.data32[2]).c_str () : "None");
|
||||
|
||||
xcb_send_event (
|
||||
xcb, false, receiver, XCB_EVENT_MASK_NO_EVENT,
|
||||
reinterpret_cast<const char*> (&event));
|
||||
}
|
||||
|
||||
XdndHandler::TypeList XdndHandler::getTypeList (xcb_client_message_event_t& event)
|
||||
{
|
||||
TypeList typeList;
|
||||
typeList.reserve (32);
|
||||
|
||||
xcb_window_t sourceId = event.data.data32[0];
|
||||
bool longTypeList = event.data.data32[1] & 1;
|
||||
|
||||
if (!longTypeList)
|
||||
{
|
||||
for (int i = 2; i < 5; ++i) {
|
||||
uint32_t type = event.data.data32[i];
|
||||
if (type != XCB_NONE)
|
||||
typeList.push_back (type);
|
||||
}
|
||||
}
|
||||
else if (Atoms::xDndTypeList.valid ())
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
|
||||
auto cookie = xcb_get_property (
|
||||
xcb, false, sourceId, Atoms::xDndTypeList (), XCB_ATOM_ATOM,
|
||||
0, typeList.capacity ());
|
||||
auto reply = xcb_get_property_reply (xcb, cookie, nullptr);
|
||||
if (reply)
|
||||
{
|
||||
int length = xcb_get_property_value_length (reply) / 4;
|
||||
const uint32_t* data = static_cast<uint32_t*> (xcb_get_property_value (reply));
|
||||
for (int i = 0; i < length; ++i)
|
||||
typeList.push_back (data[i]);
|
||||
free (reply);
|
||||
}
|
||||
}
|
||||
|
||||
return typeList;
|
||||
}
|
||||
|
||||
xcb_atom_t XdndHandler::findFilePathType (const TypeList& typeList)
|
||||
{
|
||||
xcb_atom_t type = XCB_ATOM_NONE;
|
||||
|
||||
if (type == XCB_ATOM_NONE)
|
||||
type = searchType (typeList, Atoms::xMimeTypeUriList);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
xcb_atom_t XdndHandler::findTextType (const TypeList& typeList)
|
||||
{
|
||||
xcb_atom_t type = XCB_ATOM_NONE;
|
||||
|
||||
if (type == XCB_ATOM_NONE)
|
||||
type = searchType (typeList, Atoms::xMimeTypeTextPlainUtf8);
|
||||
if (type == XCB_ATOM_NONE)
|
||||
type = searchType (typeList, Atoms::xMimeTypeTextPlain);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
xcb_atom_t XdndHandler::findBinaryType (const TypeList& typeList)
|
||||
{
|
||||
xcb_atom_t type = XCB_ATOM_NONE;
|
||||
|
||||
if (type == XCB_ATOM_NONE)
|
||||
type = searchType (typeList, Atoms::xMimeTypeApplicationOctetStream);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
xcb_atom_t XdndHandler::searchType (const TypeList& typeList, const Atom& atom)
|
||||
{
|
||||
if (typeList.empty () || !atom.valid ())
|
||||
return XCB_ATOM_NONE;
|
||||
|
||||
xcb_atom_t needle = atom ();
|
||||
for (xcb_atom_t type : typeList) {
|
||||
if (type == needle)
|
||||
return type;
|
||||
}
|
||||
|
||||
return XCB_ATOM_NONE;
|
||||
}
|
||||
|
||||
void XdndHandler::extractFilePathsFromUriList (const std::string& data, std::vector<std::string>& filePaths)
|
||||
{
|
||||
filePaths.clear ();
|
||||
filePaths.reserve (8);
|
||||
|
||||
char** uriList = g_uri_list_extract_uris (data.c_str ());
|
||||
if (!uriList)
|
||||
return;
|
||||
|
||||
for (char** uriPtr = uriList; *uriPtr; ++uriPtr)
|
||||
{
|
||||
char* uriHostname = nullptr;
|
||||
char* uriFilename = g_filename_from_uri (*uriPtr, &uriHostname, nullptr);
|
||||
if (uriFilename)
|
||||
{
|
||||
if (!uriHostname)
|
||||
filePaths.push_back (uriFilename);
|
||||
g_free (uriFilename);
|
||||
g_free (uriHostname);
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (uriList);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool isXdndClientMessage (const xcb_client_message_event_t& event)
|
||||
{
|
||||
if ((event.response_type & ~0x80) != XCB_CLIENT_MESSAGE)
|
||||
return false;
|
||||
|
||||
const std::string name = getAtomName (event.type);
|
||||
return name.size () >= 4 && !memcmp (name.data (), "Xdnd", 4);
|
||||
}
|
||||
|
||||
xcb_window_t getXdndProxy (xcb_window_t windowId)
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_window_t proxyId = 0;
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property (
|
||||
xcb, false, windowId, Atoms::xDndProxy (),
|
||||
XCB_ATOM_WINDOW, 0, 1);
|
||||
xcb_get_property_reply_t *reply = xcb_get_property_reply (
|
||||
xcb, cookie, nullptr);
|
||||
if (reply) {
|
||||
if (xcb_get_property_value_length (reply) == 4)
|
||||
proxyId = *static_cast<uint32_t*> (xcb_get_property_value (reply));
|
||||
free (reply);
|
||||
}
|
||||
return proxyId;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,87 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../vstguifwd.h"
|
||||
#include "../../dragging.h"
|
||||
#include "../../optional.h"
|
||||
#include <xcb/xcb.h>
|
||||
#include <vector>
|
||||
#undef None
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
struct Atom;
|
||||
struct ChildWindow;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class XdndDataPackage : public IDataPackage
|
||||
{
|
||||
public:
|
||||
uint32_t getCount () const override;
|
||||
uint32_t getDataSize (uint32_t index) const override;
|
||||
Type getDataType (uint32_t index) const override;
|
||||
uint32_t getData (uint32_t index, const void*& buffer, Type& type) const override;
|
||||
|
||||
void setPackageType (Type t) { packageType = t; }
|
||||
void setPackageData (std::vector<std::string>&& d) { packageData = std::move(d); }
|
||||
|
||||
private:
|
||||
Type packageType = Type::kError;
|
||||
std::vector<std::string> packageData;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class XdndHandler
|
||||
{
|
||||
public:
|
||||
XdndHandler (ChildWindow* window, IPlatformFrameCallback* frame);
|
||||
void enter (xcb_client_message_event_t& event, xcb_window_t targetId);
|
||||
void position (xcb_client_message_event_t& event);
|
||||
void leave (xcb_client_message_event_t& event);
|
||||
void drop (xcb_client_message_event_t& event);
|
||||
void selectionNotify (xcb_selection_notify_event_t& event);
|
||||
|
||||
private:
|
||||
enum class State {
|
||||
DragClear,
|
||||
DragInitiated,
|
||||
DragEntering,
|
||||
DragMoving,
|
||||
};
|
||||
|
||||
ChildWindow* window = nullptr;
|
||||
IPlatformFrameCallback* frame = nullptr;
|
||||
State state = State::DragClear;
|
||||
xcb_window_t dndTarget = 0;
|
||||
xcb_window_t dndSource = 0;
|
||||
xcb_atom_t dndType = XCB_ATOM_NONE;
|
||||
Optional<xcb_client_message_event_t> dndPosition;
|
||||
SharedPointer<XdndDataPackage> package;
|
||||
DragOperation dragOperation = DragOperation::None;
|
||||
|
||||
void clearState ();
|
||||
DragEventData getEventData () const;
|
||||
CPoint getEventPosition () const;
|
||||
void replyStatus ();
|
||||
void replyFinished ();
|
||||
|
||||
typedef std::vector<xcb_atom_t> TypeList;
|
||||
static TypeList getTypeList (xcb_client_message_event_t& event);
|
||||
static xcb_atom_t findFilePathType (const TypeList& typeList);
|
||||
static xcb_atom_t findTextType (const TypeList& typeList);
|
||||
static xcb_atom_t findBinaryType (const TypeList& typeList);
|
||||
static xcb_atom_t searchType (const TypeList& typeList, const Atom& atom);
|
||||
static void extractFilePathsFromUriList (const std::string& data, std::vector<std::string>& filePaths);
|
||||
};
|
||||
|
||||
bool isXdndClientMessage (const xcb_client_message_event_t& event);
|
||||
xcb_window_t getXdndProxy (xcb_window_t windowId);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,260 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "x11fileselector.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cerrno>
|
||||
#include <cassert>
|
||||
extern "C" { extern char **environ; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
static constexpr auto kdialogpath = "/usr/bin/kdialog";
|
||||
static constexpr auto zenitypath = "/usr/bin/zenity";
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct FileSelector : IPlatformFileSelector
|
||||
{
|
||||
FileSelector (PlatformFileSelectorStyle style) : style (style) { identifiyExDialogType (); }
|
||||
|
||||
~FileSelector () noexcept { closeProcess (); }
|
||||
|
||||
bool cancel () override
|
||||
{
|
||||
closeProcess ();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runDialog (const PlatformFileSelectorConfig& config)
|
||||
{
|
||||
switch (exDialogType)
|
||||
{
|
||||
case ExDialogType::kdialog:
|
||||
return runKDialog (config);
|
||||
case ExDialogType::zenity:
|
||||
return runZenity (config);
|
||||
case ExDialogType::none:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool run (const PlatformFileSelectorConfig& config) override
|
||||
{
|
||||
if (runDialog (config))
|
||||
{
|
||||
std::string path;
|
||||
path.reserve (1024);
|
||||
|
||||
ssize_t count;
|
||||
char buffer[1024];
|
||||
while ((count = read (readerFd, buffer, sizeof (buffer))) > 0 ||
|
||||
(count == -1 && errno == EINTR))
|
||||
{
|
||||
if (count > 0)
|
||||
path.append (buffer, count);
|
||||
}
|
||||
|
||||
std::vector<UTF8String> result;
|
||||
if (count != -1)
|
||||
{
|
||||
if (! path.empty () && path[0] == '/')
|
||||
{
|
||||
if (path.back () == '\n')
|
||||
path.pop_back ();
|
||||
result.emplace_back (path);
|
||||
}
|
||||
}
|
||||
if (config.doneCallback)
|
||||
config.doneCallback (std::move (result));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ExDialogType
|
||||
{
|
||||
none,
|
||||
kdialog,
|
||||
zenity
|
||||
};
|
||||
|
||||
void identifiyExDialogType ()
|
||||
{
|
||||
if (access (zenitypath, X_OK) != -1)
|
||||
exDialogType = ExDialogType::zenity;
|
||||
if (access (kdialogpath, X_OK) != -1)
|
||||
exDialogType = ExDialogType::kdialog;
|
||||
}
|
||||
|
||||
bool runKDialog (const PlatformFileSelectorConfig& config)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
args.reserve (16);
|
||||
args.push_back (kdialogpath);
|
||||
if (style == PlatformFileSelectorStyle::SelectFile)
|
||||
{
|
||||
args.push_back ("--getopenfilename");
|
||||
args.push_back ("--separate-output");
|
||||
}
|
||||
else if (style == PlatformFileSelectorStyle::SelectSaveFile)
|
||||
args.push_back ("--getsavefilename");
|
||||
else if (style == PlatformFileSelectorStyle::SelectDirectory)
|
||||
args.push_back ("--getexistingdirectory");
|
||||
if (hasBit (config.flags, PlatformFileSelectorFlags::MultiFileSelection))
|
||||
args.push_back ("--multiple");
|
||||
if (!config.title.empty ())
|
||||
{
|
||||
args.push_back ("--title");
|
||||
args.push_back (config.title.getString ());
|
||||
}
|
||||
if (!config.initialPath.empty ())
|
||||
args.push_back (config.initialPath.getString ());
|
||||
if (startProcess (convertToArgv (args).data ()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runZenity (const PlatformFileSelectorConfig& config)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
args.reserve (16);
|
||||
args.push_back (zenitypath);
|
||||
args.push_back ("--file-selection");
|
||||
if (style == PlatformFileSelectorStyle::SelectDirectory)
|
||||
args.push_back ("--directory");
|
||||
else if (style == PlatformFileSelectorStyle::SelectSaveFile)
|
||||
{
|
||||
args.push_back ("--save");
|
||||
args.push_back ("--confirm-overwrite");
|
||||
}
|
||||
if (!config.title.empty ())
|
||||
args.push_back ("--title=" + config.title.getString ());
|
||||
if (!config.initialPath.empty ())
|
||||
args.push_back ("--filename=" + config.initialPath.getString ());
|
||||
if (startProcess (convertToArgv (args).data ()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::vector<char*> convertToArgv (const std::vector<std::string>& args)
|
||||
{
|
||||
std::vector<char*> argv (args.size () + 1);
|
||||
for (size_t i = 0, n = args.size (); i < n; ++i)
|
||||
argv[i] = const_cast<char*>(args[i].c_str ());
|
||||
return argv;
|
||||
}
|
||||
|
||||
bool startProcess (char* argv[])
|
||||
{
|
||||
closeProcess ();
|
||||
|
||||
struct PipePair
|
||||
{
|
||||
int fd[2] = { -1, -1 };
|
||||
~PipePair ()
|
||||
{
|
||||
if (fd[0] != -1) close (fd[0]);
|
||||
if (fd[1] != -1) close (fd[1]);
|
||||
}
|
||||
};
|
||||
|
||||
PipePair rw;
|
||||
if (pipe (rw.fd) != 0)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
char** envp = environ;
|
||||
#else
|
||||
std::vector<char*> cleanEnviron;
|
||||
cleanEnviron.reserve (256);
|
||||
for (char** envp = environ; *envp; ++envp)
|
||||
{
|
||||
// ensure the process will link with system libraries,
|
||||
// and not these from the Ardour bundle.
|
||||
if (strncmp (*envp, "LD_LIBRARY_PATH=", 16) == 0)
|
||||
continue;
|
||||
cleanEnviron.push_back (*envp);
|
||||
}
|
||||
cleanEnviron.push_back (nullptr);
|
||||
char** envp = cleanEnviron.data ();
|
||||
#endif
|
||||
|
||||
pid_t forkPid = vfork ();
|
||||
if (forkPid == -1)
|
||||
return false;
|
||||
|
||||
if (forkPid == 0) {
|
||||
execute (argv, envp, rw.fd);
|
||||
assert (false);
|
||||
}
|
||||
|
||||
spawnPid = forkPid;
|
||||
|
||||
close (rw.fd[1]);
|
||||
rw.fd[1] = -1;
|
||||
readerFd = rw.fd[0];
|
||||
rw.fd[0] = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
static void execute (char* argv[], char* envp[], const int pipeFd[2])
|
||||
{
|
||||
close (pipeFd[0]);
|
||||
if (dup2 (pipeFd[1], STDOUT_FILENO) == -1)
|
||||
_exit (1);
|
||||
close (pipeFd[1]);
|
||||
execve (argv[0], argv, envp);
|
||||
_exit (1);
|
||||
}
|
||||
|
||||
void closeProcess ()
|
||||
{
|
||||
if (spawnPid != -1)
|
||||
{
|
||||
if (waitpid (spawnPid, nullptr, WNOHANG) == 0)
|
||||
{
|
||||
kill (spawnPid, SIGTERM);
|
||||
waitpid (spawnPid, nullptr, 0);
|
||||
}
|
||||
spawnPid = -1;
|
||||
}
|
||||
|
||||
if (readerFd != -1)
|
||||
{
|
||||
close (readerFd);
|
||||
readerFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
PlatformFileSelectorStyle style;
|
||||
ExDialogType exDialogType{ExDialogType::none};
|
||||
pid_t spawnPid = -1;
|
||||
int readerFd = -1;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, Frame* frame)
|
||||
{
|
||||
return std::make_shared<FileSelector> (style);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,19 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformfileselector.h"
|
||||
#include "x11frame.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, Frame* frame);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,850 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "x11frame.h"
|
||||
#include "x11dragging.h"
|
||||
#include "x11utils.h"
|
||||
#include "../../cbuttonstate.h"
|
||||
#include "../../cframe.h"
|
||||
#include "../../crect.h"
|
||||
#include "../../dragging.h"
|
||||
#include "../../vstkeycode.h"
|
||||
#include "../../cinvalidrectlist.h"
|
||||
#include "../iplatformopenglview.h"
|
||||
#include "../iplatformviewlayer.h"
|
||||
#include "../iplatformtextedit.h"
|
||||
#include "../iplatformoptionmenu.h"
|
||||
#include "../common/fileresourceinputstream.h"
|
||||
#include "../common/generictextedit.h"
|
||||
#include "../common/genericoptionmenu.h"
|
||||
#include "cairobitmap.h"
|
||||
#include "linuxfactory.h"
|
||||
#include "cairographicscontext.h"
|
||||
#include "x11platform.h"
|
||||
#include "x11utils.h"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <X11/Xlib.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_util.h>
|
||||
#include <cairo/cairo-xcb.h>
|
||||
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
|
||||
#include "../../events.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
namespace {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline void setupMouseEventButtons (MouseEvent& event, xcb_button_t value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 1:
|
||||
event.buttonState.add (MouseButton::Left);
|
||||
break;
|
||||
case 2:
|
||||
event.buttonState.add (MouseButton::Middle);
|
||||
break;
|
||||
case 3:
|
||||
event.buttonState.add (MouseButton::Right);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline void setupMouseEventButtons (MouseEvent& event, int state)
|
||||
{
|
||||
if (state & XCB_BUTTON_MASK_1)
|
||||
event.buttonState.add (MouseButton::Left);
|
||||
if (state & XCB_BUTTON_MASK_2)
|
||||
event.buttonState.add (MouseButton::Right);
|
||||
if (state & XCB_BUTTON_MASK_3)
|
||||
event.buttonState.add (MouseButton::Middle);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline void setupEventModifiers (Modifiers& modifiers, int state)
|
||||
{
|
||||
if (state & XCB_MOD_MASK_CONTROL)
|
||||
modifiers.add (ModifierKey::Control);
|
||||
if (state & XCB_MOD_MASK_SHIFT)
|
||||
modifiers.add (ModifierKey::Shift);
|
||||
if (state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5))
|
||||
modifiers.add (ModifierKey::Alt);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline CButtonState translateMouseButtons (xcb_button_t value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 1:
|
||||
return kLButton;
|
||||
case 2:
|
||||
return kMButton;
|
||||
case 3:
|
||||
return kRButton;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline CButtonState translateMouseButtons (int state)
|
||||
{
|
||||
CButtonState buttons = 0;
|
||||
if (state & XCB_BUTTON_MASK_1)
|
||||
buttons |= kLButton;
|
||||
if (state & XCB_BUTTON_MASK_2)
|
||||
buttons |= kRButton;
|
||||
if (state & XCB_BUTTON_MASK_3)
|
||||
buttons |= kMButton;
|
||||
return buttons;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline uint32_t translateModifiers (int state)
|
||||
{
|
||||
uint32_t buttons = 0;
|
||||
if (state & XCB_MOD_MASK_CONTROL)
|
||||
buttons |= kControl;
|
||||
if (state & XCB_MOD_MASK_SHIFT)
|
||||
buttons |= kShift;
|
||||
if (state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5))
|
||||
buttons |= kAlt;
|
||||
return buttons;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline Modifiers toModifiers (int state)
|
||||
{
|
||||
Modifiers mods;
|
||||
if (state & XCB_MOD_MASK_CONTROL)
|
||||
mods.add (ModifierKey::Control);
|
||||
if (state & XCB_MOD_MASK_SHIFT)
|
||||
mods.add (ModifierKey::Shift);
|
||||
if (state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5))
|
||||
mods.add (ModifierKey::Alt);
|
||||
if (state & XCB_MOD_MASK_4)
|
||||
mods.add (ModifierKey::Super);
|
||||
return mods;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct RedrawTimerHandler
|
||||
: ITimerHandler
|
||||
, NonAtomicReferenceCounted
|
||||
{
|
||||
using RedrawCallback = std::function<void ()>;
|
||||
|
||||
RedrawTimerHandler (uint64_t delay, RedrawCallback&& redrawCallback)
|
||||
: redrawCallback (std::move (redrawCallback))
|
||||
{
|
||||
RunLoop::instance ().get ()->registerTimer (delay, this);
|
||||
}
|
||||
~RedrawTimerHandler () noexcept { RunLoop::instance ().get ()->unregisterTimer (this); }
|
||||
|
||||
void onTimer () override
|
||||
{
|
||||
SharedPointer<RedrawTimerHandler> Self (this);
|
||||
Self->redrawCallback ();
|
||||
}
|
||||
|
||||
RedrawCallback redrawCallback;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct DrawHandler
|
||||
{
|
||||
DrawHandler (const ChildWindow& window)
|
||||
{
|
||||
auto s = cairo_xcb_surface_create (RunLoop::instance ().getXcbConnection (),
|
||||
window.getID (), window.getVisual (),
|
||||
window.getSize ().x, window.getSize ().y);
|
||||
windowSurface.assign (s);
|
||||
device =
|
||||
getPlatformFactory ().asLinuxFactory ()->getCairoGraphicsDeviceFactory ().addDevice (
|
||||
cairo_surface_get_device (s));
|
||||
onSizeChanged (window.getSize ());
|
||||
}
|
||||
|
||||
~DrawHandler ()
|
||||
{
|
||||
getPlatformFactory ().asLinuxFactory ()->getCairoGraphicsDeviceFactory ().removeDevice (
|
||||
cairo_surface_get_device (windowSurface));
|
||||
}
|
||||
|
||||
void onSizeChanged (const CPoint& size)
|
||||
{
|
||||
cairo_xcb_surface_set_size (windowSurface, size.x, size.y);
|
||||
backBuffer = Cairo::SurfaceHandle (cairo_surface_create_similar (
|
||||
windowSurface, CAIRO_CONTENT_COLOR_ALPHA, size.x, size.y));
|
||||
backBufferSize.setSize (size);
|
||||
auto cairoDevice = std::static_pointer_cast<CairoGraphicsDevice> (device);
|
||||
drawContext = std::make_shared<CairoGraphicsDeviceContext> (*cairoDevice, backBuffer);
|
||||
}
|
||||
|
||||
void draw (const CInvalidRectList& dirtyRects, IPlatformFrameCallback* frame)
|
||||
{
|
||||
drawContext->beginDraw ();
|
||||
frame->platformDrawRects (drawContext, 1, dirtyRects.data ());
|
||||
drawContext->endDraw ();
|
||||
|
||||
blitBackbufferToWindow (dirtyRects);
|
||||
xcb_flush (RunLoop::instance ().getXcbConnection ());
|
||||
}
|
||||
|
||||
private:
|
||||
Cairo::SurfaceHandle windowSurface;
|
||||
Cairo::SurfaceHandle backBuffer;
|
||||
CRect backBufferSize;
|
||||
std::shared_ptr<CairoGraphicsDeviceContext> drawContext;
|
||||
PlatformGraphicsDevicePtr device;
|
||||
|
||||
void blitBackbufferToWindow (const CInvalidRectList& rects)
|
||||
{
|
||||
Cairo::ContextHandle windowContext (cairo_create (windowSurface));
|
||||
cairo_set_source_surface (windowContext, backBuffer, 0, 0);
|
||||
for (auto rect : rects)
|
||||
{
|
||||
cairo_rectangle (windowContext, rect.left, rect.top, rect.getWidth (),
|
||||
rect.getHeight ());
|
||||
cairo_clip_preserve (windowContext);
|
||||
cairo_fill (windowContext);
|
||||
cairo_reset_clip (windowContext);
|
||||
}
|
||||
cairo_surface_flush (windowSurface);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct DoubleClickDetector
|
||||
{
|
||||
void onEvent (MouseDownUpMoveEvent& event, xcb_timestamp_t time)
|
||||
{
|
||||
if (event.type == EventType::MouseDown)
|
||||
onMouseDown (event.mousePosition, event.buttonState, time);
|
||||
if (event.type == EventType::MouseMove)
|
||||
onMouseMove (event.mousePosition, event.buttonState, time);
|
||||
if (event.type == EventType::MouseUp)
|
||||
onMouseUp (event.mousePosition, event.buttonState, time);
|
||||
if (isDoubleClick)
|
||||
event.clickCount = 2;
|
||||
}
|
||||
|
||||
private:
|
||||
void onMouseDown (CPoint where, MouseEventButtonState buttonState, xcb_timestamp_t time)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case State::MouseDown:
|
||||
case State::Uninitialized:
|
||||
{
|
||||
state = State::MouseDown;
|
||||
firstClickState = buttonState;
|
||||
firstClickTime = time;
|
||||
isDoubleClick = false;
|
||||
point = where;
|
||||
break;
|
||||
}
|
||||
case State::MouseUp:
|
||||
{
|
||||
if (timeInside (time) && pointInside (where))
|
||||
{
|
||||
isDoubleClick = true;
|
||||
}
|
||||
state = State::Uninitialized;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onMouseUp (CPoint where, MouseEventButtonState buttonState, xcb_timestamp_t time)
|
||||
{
|
||||
if (state == State::MouseDown && pointInside (where))
|
||||
state = State::MouseUp;
|
||||
else
|
||||
state = State::Uninitialized;
|
||||
}
|
||||
|
||||
void onMouseMove (CPoint where, MouseEventButtonState buttonState, xcb_timestamp_t time)
|
||||
{
|
||||
if (!pointInside (where))
|
||||
state = State::Uninitialized;
|
||||
}
|
||||
|
||||
bool timeInside (xcb_timestamp_t time)
|
||||
{
|
||||
constexpr xcb_timestamp_t threshold = 250; // in milliseconds
|
||||
return (time - firstClickTime) < threshold;
|
||||
}
|
||||
|
||||
bool pointInside (CPoint p) const
|
||||
{
|
||||
CRect r;
|
||||
r.setTopLeft (point);
|
||||
r.setBottomRight (point);
|
||||
r.inset (-5, -5);
|
||||
return r.pointInside (p);
|
||||
}
|
||||
|
||||
enum class State
|
||||
{
|
||||
Uninitialized,
|
||||
MouseDown,
|
||||
MouseUp,
|
||||
};
|
||||
|
||||
State state {State::Uninitialized};
|
||||
bool isDoubleClick {false};
|
||||
CPoint point;
|
||||
MouseEventButtonState firstClickState;
|
||||
xcb_timestamp_t firstClickTime {0};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Frame::Impl : IFrameEventHandler
|
||||
{
|
||||
using RectList = CInvalidRectList;
|
||||
|
||||
ChildWindow window;
|
||||
DrawHandler drawHandler;
|
||||
DoubleClickDetector doubleClickDetector;
|
||||
IPlatformFrameCallback* frame;
|
||||
std::unique_ptr<GenericOptionMenuTheme> genericOptionMenuTheme;
|
||||
SharedPointer<RedrawTimerHandler> redrawTimer;
|
||||
RectList dirtyRects;
|
||||
CCursorType currentCursor {kCursorDefault};
|
||||
uint32_t pointerGrabed {0};
|
||||
XdndHandler dndHandler;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Impl (::Window parent, CPoint size, IPlatformFrameCallback* frame)
|
||||
: window (parent, size), drawHandler (window), frame (frame), dndHandler (&window, frame)
|
||||
{
|
||||
RunLoop::instance ().registerWindowEventHandler (window.getID (), this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
~Impl () noexcept { RunLoop::instance ().unregisterWindowEventHandler (window.getID ()); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void setSize (const CRect& size)
|
||||
{
|
||||
window.setSize (size);
|
||||
drawHandler.onSizeChanged (size.getSize ());
|
||||
dirtyRects.clear ();
|
||||
dirtyRects.add (size);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void setCursor (CCursorType cursor)
|
||||
{
|
||||
if (currentCursor == cursor)
|
||||
return;
|
||||
currentCursor = cursor;
|
||||
setCursorInternal (cursor);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void setCursorInternal (CCursorType cursor)
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_params_cw_t params;
|
||||
params.cursor = RunLoop::instance ().getCursorID (cursor);
|
||||
xcb_aux_change_window_attributes (xcb, window.getID (), XCB_CW_CURSOR, ¶ms);
|
||||
xcb_aux_sync (xcb);
|
||||
xcb_flush (xcb);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void redraw ()
|
||||
{
|
||||
drawHandler.draw (dirtyRects, frame);
|
||||
dirtyRects.clear ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void invalidRect (CRect r)
|
||||
{
|
||||
dirtyRects.add (r);
|
||||
if (redrawTimer)
|
||||
return;
|
||||
redrawTimer = makeOwned<RedrawTimerHandler> (16, [this] () {
|
||||
if (dirtyRects.data ().empty ())
|
||||
return;
|
||||
redraw ();
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void grabPointer ()
|
||||
{
|
||||
if (++pointerGrabed > 1)
|
||||
return;
|
||||
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
auto cookie =
|
||||
xcb_grab_pointer (xcb, false, window.getID (),
|
||||
(XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
|
||||
XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW |
|
||||
XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
|
||||
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE,
|
||||
XCB_CURSOR_NONE, XCB_TIME_CURRENT_TIME);
|
||||
if (auto reply = xcb_grab_pointer_reply (xcb, cookie, nullptr))
|
||||
{
|
||||
if (reply->status != XCB_GRAB_STATUS_SUCCESS)
|
||||
pointerGrabed = 0;
|
||||
free (reply);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void ungrabPointer ()
|
||||
{
|
||||
if (pointerGrabed == 0)
|
||||
return;
|
||||
if (--pointerGrabed > 0)
|
||||
return;
|
||||
vstgui_assert (pointerGrabed == 0);
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_ungrab_pointer (xcb, XCB_TIME_CURRENT_TIME);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_map_notify_event_t& event) override {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_key_press_event_t& event) override
|
||||
{
|
||||
auto type = (event.response_type & ~0x80);
|
||||
auto keyEvent = RunLoop::instance ().getCurrentKeyEvent ();
|
||||
frame->platformOnEvent (keyEvent);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_button_press_event_t& event) override
|
||||
{
|
||||
CPoint where (event.event_x, event.event_y);
|
||||
if ((event.response_type & ~0x80) == XCB_BUTTON_PRESS) // mouse down or wheel
|
||||
{
|
||||
if (event.detail >= 4 && event.detail <= 7) // mouse wheel
|
||||
{
|
||||
MouseWheelEvent wheelEvent;
|
||||
wheelEvent.mousePosition = where;
|
||||
wheelEvent.modifiers = toModifiers (event.state);
|
||||
switch (event.detail)
|
||||
{
|
||||
case 4: // up
|
||||
{
|
||||
wheelEvent.deltaY = 1;
|
||||
break;
|
||||
}
|
||||
case 5: // down
|
||||
{
|
||||
wheelEvent.deltaY = -1;
|
||||
break;
|
||||
}
|
||||
case 6: // left
|
||||
{
|
||||
wheelEvent.deltaX = -1;
|
||||
break;
|
||||
}
|
||||
case 7: // right
|
||||
{
|
||||
wheelEvent.deltaX = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
frame->platformOnEvent (wheelEvent);
|
||||
}
|
||||
else // mouse down
|
||||
{
|
||||
MouseDownEvent downEvent;
|
||||
downEvent.mousePosition = where;
|
||||
setupMouseEventButtons (downEvent, event.detail);
|
||||
setupEventModifiers (downEvent.modifiers, event.state);
|
||||
doubleClickDetector.onEvent (downEvent, event.time);
|
||||
frame->platformOnEvent (downEvent);
|
||||
grabPointer ();
|
||||
if (downEvent.consumed)
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_set_input_focus (xcb, XCB_INPUT_FOCUS_PARENT, window.getID (),
|
||||
XCB_CURRENT_TIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // mouse up
|
||||
{
|
||||
if (event.detail >= 4 && event.detail <= 7) // mouse wheel
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
MouseUpEvent upEvent;
|
||||
upEvent.mousePosition = where;
|
||||
setupMouseEventButtons (upEvent, event.detail);
|
||||
setupEventModifiers (upEvent.modifiers, event.state);
|
||||
doubleClickDetector.onEvent (upEvent, event.time);
|
||||
frame->platformOnEvent (upEvent);
|
||||
ungrabPointer ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_motion_notify_event_t& event) override
|
||||
{
|
||||
MouseMoveEvent moveEvent;
|
||||
moveEvent.mousePosition (event.event_x, event.event_y);
|
||||
setupMouseEventButtons (moveEvent, event.state);
|
||||
setupEventModifiers (moveEvent.modifiers, event.state);
|
||||
doubleClickDetector.onEvent (moveEvent, event.time);
|
||||
frame->platformOnEvent (moveEvent);
|
||||
// make sure we get more motion events
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_get_motion_events (xcb, window.getID (), event.time, event.time + 10000000);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_enter_notify_event_t& event) override
|
||||
{
|
||||
if ((event.response_type & ~0x80) == XCB_LEAVE_NOTIFY)
|
||||
{
|
||||
MouseExitEvent exitEvent;
|
||||
exitEvent.mousePosition (event.event_x, event.event_y);
|
||||
setupMouseEventButtons (exitEvent, event.state);
|
||||
setupEventModifiers (exitEvent.modifiers, event.state);
|
||||
frame->platformOnEvent (exitEvent);
|
||||
setCursorInternal (kCursorDefault);
|
||||
}
|
||||
else
|
||||
{
|
||||
setCursorInternal (currentCursor);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_focus_in_event_t& event) override {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_expose_event_t& event) override
|
||||
{
|
||||
CRect r;
|
||||
r.setTopLeft (CPoint (event.x, event.y));
|
||||
r.setSize (CPoint (event.width, event.height));
|
||||
invalidRect (r);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_property_notify_event_t& event) override
|
||||
{
|
||||
#if 1 // needed for Reaper
|
||||
if (Atoms::xEmbedInfo.valid () && event.atom == Atoms::xEmbedInfo ())
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_map_window (xcb, window.getID ());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void onEvent (xcb_selection_notify_event_t& event) override
|
||||
{
|
||||
dndHandler.selectionNotify (event);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onEvent (xcb_client_message_event_t& event, xcb_window_t proxyId = 0) override
|
||||
{
|
||||
if (Atoms::xEmbed.valid () && event.type == Atoms::xEmbed ())
|
||||
{
|
||||
switch (static_cast<XEMBED> (event.data.data32[1]))
|
||||
{
|
||||
case XEMBED::EMBEDDED_NOTIFY:
|
||||
{
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
xcb_map_window (xcb, window.getID ());
|
||||
break;
|
||||
}
|
||||
case XEMBED::WINDOW_ACTIVATE:
|
||||
{
|
||||
frame->platformOnWindowActivate (true);
|
||||
break;
|
||||
}
|
||||
case XEMBED::WINDOW_DEACTIVATE:
|
||||
{
|
||||
frame->platformOnWindowActivate (false);
|
||||
break;
|
||||
}
|
||||
case XEMBED::FOCUS_IN:
|
||||
{
|
||||
frame->platformOnActivate (true);
|
||||
break;
|
||||
}
|
||||
case XEMBED::FOCUS_OUT:
|
||||
{
|
||||
frame->platformOnActivate (false);
|
||||
break;
|
||||
}
|
||||
case XEMBED::FOCUS_NEXT:
|
||||
{
|
||||
// we could send a tab keycode here...
|
||||
break;
|
||||
}
|
||||
case XEMBED::FOCUS_PREV:
|
||||
{
|
||||
// we could send a shift-tab keycode here...
|
||||
break;
|
||||
}
|
||||
case XEMBED::MODALITY_ON:
|
||||
case XEMBED::MODALITY_OFF:
|
||||
case XEMBED::REGISTER_ACCELERATOR:
|
||||
case XEMBED::UNREGISTER_ACCELERATOR:
|
||||
case XEMBED::ACTIVATE_ACCELERATOR:
|
||||
case XEMBED::REQUEST_FOCUS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Atoms::xDndEnter.valid () && event.type == Atoms::xDndEnter ())
|
||||
{
|
||||
dndHandler.enter (event, proxyId ? proxyId : window.getID ());
|
||||
}
|
||||
else if (Atoms::xDndPosition.valid () && event.type == Atoms::xDndPosition ())
|
||||
{
|
||||
dndHandler.position (event);
|
||||
}
|
||||
else if (Atoms::xDndLeave.valid () && event.type == Atoms::xDndLeave ())
|
||||
{
|
||||
dndHandler.leave (event);
|
||||
}
|
||||
else if (Atoms::xDndDrop.valid () && event.type == Atoms::xDndDrop ())
|
||||
{
|
||||
dndHandler.drop (event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Frame::Frame (IPlatformFrameCallback* frame, const CRect& size, uint32_t parent,
|
||||
IPlatformFrameConfig* config)
|
||||
: IPlatformFrame (frame)
|
||||
{
|
||||
auto cfg = dynamic_cast<FrameConfig*> (config);
|
||||
if (cfg && cfg->runLoop)
|
||||
{
|
||||
RunLoop::init ();
|
||||
if (auto f = getPlatformFactory ().asLinuxFactory ())
|
||||
{
|
||||
if (f->getRunLoop () == nullptr)
|
||||
f->setRunLoop (cfg->runLoop);
|
||||
}
|
||||
}
|
||||
|
||||
impl = std::unique_ptr<Impl> (new Impl (parent, {size.getWidth (), size.getHeight ()}, frame));
|
||||
|
||||
frame->platformOnActivate (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Frame::~Frame ()
|
||||
{
|
||||
impl.reset ();
|
||||
RunLoop::exit ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Frame::optionMenuPopupStarted ()
|
||||
{
|
||||
impl->grabPointer ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Frame::optionMenuPopupStopped ()
|
||||
{
|
||||
impl->ungrabPointer ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getGlobalPosition (CPoint& pos) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::setSize (const CRect& newSize)
|
||||
{
|
||||
vstgui_assert (impl);
|
||||
impl->setSize (newSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getSize (CRect& size) const
|
||||
{
|
||||
size.setSize (impl->window.getSize ());
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getCurrentMousePosition (CPoint& mousePosition) const
|
||||
{
|
||||
xcb_query_pointer_cookie_t cookie =
|
||||
xcb_query_pointer (RunLoop::instance ().getXcbConnection (), getX11WindowID ());
|
||||
xcb_query_pointer_reply_t* reply =
|
||||
xcb_query_pointer_reply (RunLoop::instance ().getXcbConnection (), cookie, nullptr);
|
||||
if (!reply)
|
||||
return false;
|
||||
|
||||
mousePosition.x = reply->win_x;
|
||||
mousePosition.y = reply->win_y;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getCurrentMouseButtons (CButtonState& buttons) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::getCurrentModifiers (Modifiers& modifiers) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::setMouseCursor (CCursorType type)
|
||||
{
|
||||
impl->setCursor (type);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::invalidRect (const CRect& rect)
|
||||
{
|
||||
impl->invalidRect (rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::scrollRect (const CRect& src, const CPoint& distance)
|
||||
{
|
||||
(void)src;
|
||||
(void)distance;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::showTooltip (const CRect& rect, const char* utf8Text)
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::hideTooltip ()
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void* Frame::getPlatformRepresentation () const
|
||||
{
|
||||
return reinterpret_cast<void*> (getX11WindowID ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
uint32_t Frame::getX11WindowID () const
|
||||
{
|
||||
return impl->window.getID ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformTextEdit> Frame::createPlatformTextEdit (IPlatformTextEditCallback* textEdit)
|
||||
{
|
||||
return makeOwned<GenericTextEdit> (textEdit);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformOptionMenu> Frame::createPlatformOptionMenu ()
|
||||
{
|
||||
auto cFrame = dynamic_cast<CFrame*> (frame);
|
||||
GenericOptionMenuTheme theme;
|
||||
if (impl->genericOptionMenuTheme)
|
||||
theme = *impl->genericOptionMenuTheme.get ();
|
||||
auto optionMenu =
|
||||
makeOwned<GenericOptionMenu> (cFrame, MouseEventButtonState (MouseButton::Left), theme);
|
||||
optionMenu->setListener (this);
|
||||
return optionMenu;
|
||||
}
|
||||
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformOpenGLView> Frame::createPlatformOpenGLView ()
|
||||
{
|
||||
#warning TODO: Implementation
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformViewLayer> Frame::createPlatformViewLayer (
|
||||
IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer)
|
||||
{
|
||||
// optional
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
DragResult Frame::doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap)
|
||||
{
|
||||
return kDragError;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::doDrag (const DragDescription& dragDescription,
|
||||
const SharedPointer<IDragCallback>& callback)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
PlatformType Frame::getPlatformType () const
|
||||
{
|
||||
return PlatformType::kX11EmbedWindowID;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Optional<UTF8String> Frame::convertCurrentKeyEventToText ()
|
||||
{
|
||||
return RunLoop::instance ().convertCurrentKeyEventToText ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Frame::setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme)
|
||||
{
|
||||
if (theme)
|
||||
impl->genericOptionMenuTheme =
|
||||
std::unique_ptr<GenericOptionMenuTheme> (new GenericOptionMenuTheme (*theme));
|
||||
else
|
||||
impl->genericOptionMenuTheme = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,74 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
#pragma once
|
||||
|
||||
#include "../../crect.h"
|
||||
#include "../iplatformframe.h"
|
||||
#include "../iplatformresourceinputstream.h"
|
||||
#include "../platform_x11.h"
|
||||
#include "../common/genericoptionmenu.h"
|
||||
#include "irunloop.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class Frame
|
||||
: public IPlatformFrame
|
||||
, public IX11Frame
|
||||
, public IGenericOptionMenuListener
|
||||
{
|
||||
public:
|
||||
Frame (IPlatformFrameCallback* frame, const CRect& size, uint32_t parent,
|
||||
IPlatformFrameConfig* config);
|
||||
~Frame ();
|
||||
|
||||
private:
|
||||
bool getGlobalPosition (CPoint& pos) const override;
|
||||
bool setSize (const CRect& newSize) override;
|
||||
bool getSize (CRect& size) const override;
|
||||
bool getCurrentMousePosition (CPoint& mousePosition) const override;
|
||||
bool getCurrentMouseButtons (CButtonState& buttons) const override;
|
||||
bool getCurrentModifiers (Modifiers& modifiers) const override;
|
||||
bool setMouseCursor (CCursorType type) override;
|
||||
bool invalidRect (const CRect& rect) override;
|
||||
bool scrollRect (const CRect& src, const CPoint& distance) override;
|
||||
bool showTooltip (const CRect& rect, const char* utf8Text) override;
|
||||
bool hideTooltip () override;
|
||||
void* getPlatformRepresentation () const override;
|
||||
SharedPointer<IPlatformTextEdit>
|
||||
createPlatformTextEdit (IPlatformTextEditCallback* textEdit) override;
|
||||
SharedPointer<IPlatformOptionMenu> createPlatformOptionMenu () override;
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
SharedPointer<IPlatformOpenGLView> createPlatformOpenGLView () override;
|
||||
#endif
|
||||
SharedPointer<IPlatformViewLayer> createPlatformViewLayer (
|
||||
IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer) override;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) override;
|
||||
#endif
|
||||
bool doDrag (const DragDescription& dragDescription,
|
||||
const SharedPointer<IDragCallback>& callback) override;
|
||||
|
||||
PlatformType getPlatformType () const override;
|
||||
void onFrameClosed () override {}
|
||||
Optional<UTF8String> convertCurrentKeyEventToText () override;
|
||||
bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) override;
|
||||
|
||||
uint32_t getX11WindowID () const override;
|
||||
|
||||
void optionMenuPopupStarted () override;
|
||||
void optionMenuPopupStopped () override;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,561 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "x11platform.h"
|
||||
#include "linuxfactory.h"
|
||||
#include "../../cfileselector.h"
|
||||
#include "../../cframe.h"
|
||||
#include "../../cstring.h"
|
||||
#include "../../events.h"
|
||||
#include "x11frame.h"
|
||||
#include "x11dragging.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <link.h>
|
||||
#include <unordered_map>
|
||||
#include <codecvt>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_cursor.h>
|
||||
#include <xcb/xcb_util.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
// c++11 compile error workaround
|
||||
#define explicit _explicit
|
||||
#include <xcb/xkb.h>
|
||||
#undef explicit
|
||||
#undef None
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace X11 {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace {
|
||||
|
||||
using VirtMap = std::unordered_map<xkb_keysym_t, VirtualKey>;
|
||||
const VirtMap keyMap = {{XKB_KEY_BackSpace, VirtualKey::Back},
|
||||
{XKB_KEY_Tab, VirtualKey::Tab},
|
||||
{XKB_KEY_Clear, VirtualKey::Clear},
|
||||
{XKB_KEY_Return, VirtualKey::Return},
|
||||
{XKB_KEY_Pause, VirtualKey::Pause},
|
||||
{XKB_KEY_Escape, VirtualKey::Escape},
|
||||
{XKB_KEY_space, VirtualKey::Space},
|
||||
{XKB_KEY_End, VirtualKey::End},
|
||||
{XKB_KEY_Home, VirtualKey::Home},
|
||||
|
||||
{XKB_KEY_Left, VirtualKey::Left},
|
||||
{XKB_KEY_Up, VirtualKey::Up},
|
||||
{XKB_KEY_Right, VirtualKey::Right},
|
||||
{XKB_KEY_Down, VirtualKey::Down},
|
||||
{XKB_KEY_Page_Up, VirtualKey::PageUp},
|
||||
{XKB_KEY_Page_Down, VirtualKey::PageDown},
|
||||
|
||||
{XKB_KEY_Select, VirtualKey::Select},
|
||||
{XKB_KEY_Print, VirtualKey::Print},
|
||||
{XKB_KEY_KP_Enter, VirtualKey::Enter},
|
||||
{XKB_KEY_Insert, VirtualKey::Insert},
|
||||
{XKB_KEY_Delete, VirtualKey::Delete},
|
||||
{XKB_KEY_Help, VirtualKey::Help},
|
||||
// Numpads ???
|
||||
{XKB_KEY_KP_Multiply, VirtualKey::Multiply},
|
||||
{XKB_KEY_KP_Add, VirtualKey::Add},
|
||||
{XKB_KEY_KP_Separator, VirtualKey::Separator},
|
||||
{XKB_KEY_KP_Subtract, VirtualKey::Subtract},
|
||||
{XKB_KEY_KP_Decimal, VirtualKey::Decimal},
|
||||
{XKB_KEY_KP_Divide, VirtualKey::Divide},
|
||||
{XKB_KEY_F1, VirtualKey::F1},
|
||||
{XKB_KEY_F2, VirtualKey::F2},
|
||||
{XKB_KEY_F3, VirtualKey::F3},
|
||||
{XKB_KEY_F4, VirtualKey::F4},
|
||||
{XKB_KEY_F5, VirtualKey::F5},
|
||||
{XKB_KEY_F6, VirtualKey::F6},
|
||||
{XKB_KEY_F7, VirtualKey::F7},
|
||||
{XKB_KEY_F8, VirtualKey::F8},
|
||||
{XKB_KEY_F9, VirtualKey::F9},
|
||||
{XKB_KEY_F10, VirtualKey::F10},
|
||||
{XKB_KEY_F11, VirtualKey::F11},
|
||||
{XKB_KEY_F12, VirtualKey::F12},
|
||||
{XKB_KEY_Num_Lock, VirtualKey::NumLock},
|
||||
{XKB_KEY_Scroll_Lock, VirtualKey::Scroll}, // correct ?
|
||||
#if 0
|
||||
{XKB_KEY_Shift_L, VirtualKey::SHIFT},
|
||||
{XKB_KEY_Shift_R, VirtualKey::SHIFT},
|
||||
{XKB_KEY_Control_L, VirtualKey::CONTROL},
|
||||
{XKB_KEY_Control_R, VirtualKey::CONTROL},
|
||||
{XKB_KEY_Alt_L, VirtualKey::ALT},
|
||||
{XKB_KEY_Alt_R, VirtualKey::ALT},
|
||||
#endif
|
||||
{XKB_KEY_VoidSymbol, VirtualKey::None}};
|
||||
const VirtMap shiftKeyMap = {{XKB_KEY_KP_Page_Up, VirtualKey::PageUp},
|
||||
{XKB_KEY_KP_Page_Down, VirtualKey::PageDown},
|
||||
{XKB_KEY_KP_Home, VirtualKey::Home},
|
||||
{XKB_KEY_KP_End, VirtualKey::End}};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct RunLoop::Impl : IEventHandler
|
||||
{
|
||||
using WindowEventHandlerMap = std::unordered_map<uint32_t, IFrameEventHandler*>;
|
||||
|
||||
std::atomic<uint32_t> useCount {0};
|
||||
xcb_connection_t* xcbConnection {nullptr};
|
||||
xcb_cursor_context_t* cursorContext {nullptr};
|
||||
xkb_context* xkbContext {nullptr};
|
||||
xkb_state* xkbState {nullptr};
|
||||
xkb_state* xkbUnprocessedState {nullptr};
|
||||
xkb_keymap* xkbKeymap {nullptr};
|
||||
WindowEventHandlerMap windowEventHandlerMap;
|
||||
std::array<xcb_cursor_t, CCursorType::kCursorIBeam + 1> cursors {{XCB_CURSOR_NONE}};
|
||||
KeyboardEvent lastUnprocessedKeyEvent;
|
||||
uint32_t lastUtf32KeyEventChar {0};
|
||||
|
||||
void init ()
|
||||
{
|
||||
if (++useCount != 1)
|
||||
return;
|
||||
int screenNo;
|
||||
xcbConnection = xcb_connect (nullptr, &screenNo);
|
||||
RunLoop::get ()->registerEventHandler (xcb_get_file_descriptor (xcbConnection), this);
|
||||
auto screen = xcb_aux_get_screen (xcbConnection, screenNo);
|
||||
xcb_cursor_context_new (xcbConnection, screen, &cursorContext);
|
||||
|
||||
xcb_xkb_use_extension (xcbConnection, XKB_X11_MIN_MAJOR_XKB_VERSION,
|
||||
XKB_X11_MIN_MINOR_XKB_VERSION);
|
||||
xkbContext = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
|
||||
|
||||
int32_t deviceId = xkb_x11_get_core_keyboard_device_id (xcbConnection);
|
||||
if (deviceId > -1)
|
||||
{
|
||||
xkbKeymap = xkb_x11_keymap_new_from_device (xkbContext, xcbConnection, deviceId,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
xkbState = xkb_state_new (xkbKeymap);
|
||||
xkbUnprocessedState = xkb_state_new (xkbKeymap);
|
||||
|
||||
auto xkbStateCookie = xcb_xkb_get_state (xcbConnection, deviceId);
|
||||
auto* xkbStateReply = xcb_xkb_get_state_reply (xcbConnection, xkbStateCookie, nullptr);
|
||||
if (xkbStateReply)
|
||||
{
|
||||
xkb_state_update_mask (xkbState,
|
||||
xkbStateReply->baseMods,
|
||||
xkbStateReply->latchedMods,
|
||||
xkbStateReply->lockedMods,
|
||||
xkbStateReply->baseGroup,
|
||||
xkbStateReply->latchedGroup,
|
||||
xkbStateReply->lockedGroup);
|
||||
free (xkbStateReply);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exit ()
|
||||
{
|
||||
if (--useCount != 0)
|
||||
return;
|
||||
|
||||
if (xcbConnection)
|
||||
{
|
||||
if (xkbUnprocessedState)
|
||||
xkb_state_unref (xkbUnprocessedState);
|
||||
if (xkbState)
|
||||
xkb_state_unref (xkbState);
|
||||
if (xkbKeymap)
|
||||
xkb_keymap_unref (xkbKeymap);
|
||||
if (xkbContext)
|
||||
xkb_context_unref (xkbContext);
|
||||
if (cursorContext)
|
||||
{
|
||||
for (auto c : cursors)
|
||||
{
|
||||
if (c != XCB_CURSOR_NONE)
|
||||
xcb_free_cursor (xcbConnection, c);
|
||||
}
|
||||
xcb_cursor_context_free (cursorContext);
|
||||
}
|
||||
|
||||
xcb_disconnect (xcbConnection);
|
||||
}
|
||||
RunLoop::get ()->unregisterEventHandler (this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void dispatchEvent (T& event, xcb_window_t windowId)
|
||||
{
|
||||
auto it = windowEventHandlerMap.find (windowId);
|
||||
|
||||
if (it != windowEventHandlerMap.end ())
|
||||
{
|
||||
it->second->onEvent (event);
|
||||
return;
|
||||
}
|
||||
|
||||
// we may receive a proxied drag-and-drop event; if that is the case,
|
||||
// the window has the attribute XdndProxy, and acts as a proxy for the
|
||||
// other window designated by the value of this attribute.
|
||||
if (std::is_same<T, xcb_client_message_event_t>::value)
|
||||
{
|
||||
xcb_client_message_event_t& cmsg =
|
||||
reinterpret_cast<xcb_client_message_event_t&> (event);
|
||||
|
||||
if (isXdndClientMessage (cmsg))
|
||||
{
|
||||
xcb_window_t targetId = getXdndProxy (windowId);
|
||||
if (targetId != 0)
|
||||
it = windowEventHandlerMap.find (targetId);
|
||||
if (it != windowEventHandlerMap.end ())
|
||||
{
|
||||
it->second->onEvent (cmsg, windowId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void onKeyEvent (const xcb_key_press_event_t& event, bool isKeyDown)
|
||||
{
|
||||
if (!xkbUnprocessedState)
|
||||
return;
|
||||
|
||||
KeyboardEvent keyEvent;
|
||||
keyEvent.type = isKeyDown ? EventType::KeyDown : EventType::KeyUp;
|
||||
|
||||
if (event.state & XCB_MOD_MASK_SHIFT)
|
||||
keyEvent.modifiers.add (ModifierKey::Shift);
|
||||
if (event.state & XCB_MOD_MASK_CONTROL)
|
||||
keyEvent.modifiers.add (ModifierKey::Control);
|
||||
if (event.state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5))
|
||||
keyEvent.modifiers.add (ModifierKey::Alt);
|
||||
|
||||
auto ksym = xkb_state_key_get_one_sym (xkbUnprocessedState, event.detail);
|
||||
xkb_state_update_key (xkbState, event.detail, isKeyDown ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
|
||||
VirtMap::const_iterator it;
|
||||
bool ksymMapped = false;
|
||||
if (!ksymMapped && keyEvent.modifiers.has(ModifierKey::Shift))
|
||||
{
|
||||
it = shiftKeyMap.find (ksym);
|
||||
ksymMapped = it != shiftKeyMap.end ();
|
||||
}
|
||||
if (!ksymMapped)
|
||||
{
|
||||
it = keyMap.find (ksym);
|
||||
ksymMapped = it != keyMap.end ();
|
||||
}
|
||||
|
||||
if (ksymMapped)
|
||||
{
|
||||
keyEvent.virt = it->second;
|
||||
lastUtf32KeyEventChar = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyEvent.character = xkb_state_key_get_utf32 (xkbState, event.detail);
|
||||
lastUtf32KeyEventChar = keyEvent.character;
|
||||
}
|
||||
|
||||
lastUnprocessedKeyEvent = std::move (keyEvent);
|
||||
}
|
||||
|
||||
void onEvent () override
|
||||
{
|
||||
while (auto event = xcb_poll_for_event (xcbConnection))
|
||||
{
|
||||
auto type = event->response_type & ~0x80;
|
||||
switch (type)
|
||||
{
|
||||
case XCB_KEY_PRESS:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_key_press_event_t*> (event);
|
||||
onKeyEvent (*ev, true);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_KEY_RELEASE:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_key_release_event_t*> (event);
|
||||
onKeyEvent (*ev, false);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_BUTTON_PRESS:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_button_press_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_BUTTON_RELEASE:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_button_release_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_MOTION_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_motion_notify_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_ENTER_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_enter_notify_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_LEAVE_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_leave_notify_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
case XCB_EXPOSE:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_expose_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->window);
|
||||
break;
|
||||
}
|
||||
case XCB_UNMAP_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_unmap_notify_event_t*> (event);
|
||||
break;
|
||||
}
|
||||
case XCB_MAP_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_map_notify_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->window);
|
||||
break;
|
||||
}
|
||||
case XCB_CONFIGURE_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_configure_notify_event_t*> (event);
|
||||
break;
|
||||
}
|
||||
case XCB_PROPERTY_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_property_notify_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->window);
|
||||
break;
|
||||
}
|
||||
case XCB_SELECTION_NOTIFY:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_selection_notify_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->requestor);
|
||||
break;
|
||||
}
|
||||
case XCB_CLIENT_MESSAGE:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_client_message_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->window);
|
||||
break;
|
||||
}
|
||||
case XCB_FOCUS_IN:
|
||||
case XCB_FOCUS_OUT:
|
||||
{
|
||||
auto ev = reinterpret_cast<xcb_focus_in_event_t*> (event);
|
||||
dispatchEvent (*ev, ev->event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::free (event);
|
||||
}
|
||||
xcb_aux_sync (xcbConnection);
|
||||
xcb_flush (xcbConnection);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
RunLoop& RunLoop::instance ()
|
||||
{
|
||||
static RunLoop gInstance;
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::init () { instance ().impl->init (); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::exit ()
|
||||
{
|
||||
instance ().impl->exit ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const SharedPointer<IRunLoop> RunLoop::get ()
|
||||
{
|
||||
return getPlatformFactory ().asLinuxFactory ()->getRunLoop ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
RunLoop::RunLoop ()
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
RunLoop::~RunLoop () noexcept = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::registerWindowEventHandler (uint32_t windowId, IFrameEventHandler* handler)
|
||||
{
|
||||
impl->windowEventHandlerMap.emplace (windowId, handler);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void RunLoop::unregisterWindowEventHandler (uint32_t windowId)
|
||||
{
|
||||
auto it = impl->windowEventHandlerMap.find (windowId);
|
||||
if (it == impl->windowEventHandlerMap.end ())
|
||||
return;
|
||||
impl->windowEventHandlerMap.erase (it);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
xcb_connection_t* RunLoop::getXcbConnection () const
|
||||
{
|
||||
return impl->xcbConnection;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
uint32_t makeCursor (xcb_cursor_context_t* context, const T& names)
|
||||
{
|
||||
for (auto& name : names)
|
||||
{
|
||||
auto result = xcb_cursor_load_cursor (context, name);
|
||||
if (result != XCB_CURSOR_NONE)
|
||||
return result;
|
||||
}
|
||||
return XCB_CURSOR_NONE;
|
||||
}
|
||||
|
||||
template<size_t count>
|
||||
using CharPtrArray = std::array<const char*, count>;
|
||||
|
||||
constexpr auto CursorDefaultNames = //
|
||||
CharPtrArray<4> {"left_ptr", "arrow", "dnd-none", "op_left_arrow"};
|
||||
constexpr auto CursorWaitNames = //
|
||||
CharPtrArray<3> {"wait", "watch", "progress"};
|
||||
constexpr auto CursorHSizeNames = //
|
||||
CharPtrArray<8> {"size_hor", "sb_h_double_arrow", "h_double_arrow", "e-resize",
|
||||
"w-resize", "row-resize", "right_side", "left_side"};
|
||||
constexpr auto CursorVSizeNames = //
|
||||
CharPtrArray<12> {"size_ver", "sb_v_double_arrow", "v_double_arrow", "n-resize",
|
||||
"s-resize", "col-resize", "top_side", "bottom_side",
|
||||
"base_arrow_up", "base_arrow_down", "based_arrow_down", "based_arrow_up"};
|
||||
constexpr auto CursorNESWSizeNames = //
|
||||
CharPtrArray<5> {"size_bdiag", "fd_double_arrow", "bottom_left_corner", "top_right_corner"};
|
||||
constexpr auto CursorNWSESizeNames = //
|
||||
CharPtrArray<5> {"size_fdiag", "bd_double_arrow", "bottom_right_corner", "top_left_corner"};
|
||||
constexpr auto CursorSizeAllNames = //
|
||||
CharPtrArray<4> {"cross", "diamond-cross", "cross-reverse", "crosshair"};
|
||||
constexpr auto CursorCopyNames = //
|
||||
CharPtrArray<2> {"dnd-copy", "copy"};
|
||||
constexpr auto CursorNotAllowedNames = //
|
||||
CharPtrArray<4> {"forbidden", "circle", "dnd-no-drop", "not-allowed"};
|
||||
constexpr auto CursorHandNames = //
|
||||
CharPtrArray<4> {"openhand", "hand1", "all_scroll", "all-scroll"};
|
||||
constexpr auto CursorIBeamNames = //
|
||||
CharPtrArray<3> {"ibeam", "xterm", "text"};
|
||||
constexpr auto CursorMovableObjectNames = //
|
||||
CharPtrArray<2> {"hand1", "fleur"};
|
||||
constexpr auto CursorMoveObjectNames = //
|
||||
CharPtrArray<2> {"fleur", "hand1"};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
uint32_t RunLoop::getCursorID (CCursorType cursor)
|
||||
{
|
||||
if (impl->cursors[cursor] == XCB_CURSOR_NONE && impl->cursorContext)
|
||||
{
|
||||
uint32_t cursorID = XCB_CURSOR_NONE;
|
||||
switch (cursor)
|
||||
{
|
||||
case kCursorDefault:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorDefaultNames);
|
||||
break;
|
||||
case kCursorWait:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorWaitNames);
|
||||
break;
|
||||
case kCursorHSize:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorHSizeNames);
|
||||
break;
|
||||
case kCursorVSize:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorVSizeNames);
|
||||
break;
|
||||
case kCursorNESWSize:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorNESWSizeNames);
|
||||
break;
|
||||
case kCursorNWSESize:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorNWSESizeNames);
|
||||
break;
|
||||
case kCursorCrosshair:
|
||||
case kCursorSizeAll:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorSizeAllNames);
|
||||
break;
|
||||
case kCursorCopy:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorCopyNames);
|
||||
break;
|
||||
case kCursorNotAllowed:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorNotAllowedNames);
|
||||
break;
|
||||
case kCursorPointingHand:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorHandNames);
|
||||
break;
|
||||
case kCursorIBeam:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorIBeamNames);
|
||||
break;
|
||||
case kCursorMovableObject:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorMovableObjectNames);
|
||||
break;
|
||||
case kCursorMoveObject:
|
||||
cursorID = makeCursor (impl->cursorContext, CursorMoveObjectNames);
|
||||
break;
|
||||
}
|
||||
impl->cursors[cursor] = cursorID;
|
||||
}
|
||||
return impl->cursors[cursor];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
KeyboardEvent&& RunLoop::getCurrentKeyEvent () const
|
||||
{
|
||||
return std::move (impl->lastUnprocessedKeyEvent);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Optional<UTF8String> RunLoop::convertCurrentKeyEventToText () const
|
||||
{
|
||||
if (impl->lastUtf32KeyEventChar == 0)
|
||||
return {};
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
|
||||
return Optional<UTF8String> (UTF8String (conv.to_bytes (impl->lastUtf32KeyEventChar)));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,79 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../vstguifwd.h"
|
||||
#include "x11frame.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
struct xcb_connection_t; // forward declaration
|
||||
struct xcb_key_press_event_t; // forward declaration
|
||||
struct xcb_button_press_event_t;
|
||||
struct xcb_motion_notify_event_t;
|
||||
struct xcb_enter_notify_event_t;
|
||||
struct xcb_focus_in_event_t;
|
||||
struct xcb_expose_event_t;
|
||||
struct xcb_map_notify_event_t;
|
||||
struct xcb_property_notify_event_t;
|
||||
struct xcb_selection_notify_event_t;
|
||||
struct xcb_client_message_event_t;
|
||||
using xcb_window_t = uint32_t;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace X11 {
|
||||
|
||||
class Frame;
|
||||
class Timer;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct IFrameEventHandler
|
||||
{
|
||||
virtual void onEvent (xcb_map_notify_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_key_press_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_button_press_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_motion_notify_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_enter_notify_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_focus_in_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_expose_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_property_notify_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_selection_notify_event_t& event) = 0;
|
||||
virtual void onEvent (xcb_client_message_event_t& event, xcb_window_t proxyId = 0) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct RunLoop
|
||||
{
|
||||
static void init ();
|
||||
static void exit ();
|
||||
static const SharedPointer<IRunLoop> get ();
|
||||
|
||||
xcb_connection_t* getXcbConnection () const;
|
||||
|
||||
void registerWindowEventHandler (uint32_t windowId, IFrameEventHandler* handler);
|
||||
void unregisterWindowEventHandler (uint32_t windowId);
|
||||
|
||||
uint32_t getCursorID (CCursorType cursor);
|
||||
KeyboardEvent&& getCurrentKeyEvent () const;
|
||||
Optional<UTF8String> convertCurrentKeyEventToText () const;
|
||||
|
||||
void setDevice (cairo_device_t* device);
|
||||
static RunLoop& instance ();
|
||||
|
||||
private:
|
||||
RunLoop ();
|
||||
~RunLoop () noexcept;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,53 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "x11timer.h"
|
||||
#include "x11platform.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Timer::Timer (IPlatformTimerCallback* _callback)
|
||||
{
|
||||
callback = _callback;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Timer::~Timer () noexcept
|
||||
{
|
||||
stop ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Timer::start (uint32_t periodMs)
|
||||
{
|
||||
auto runLoop = RunLoop::get ();
|
||||
vstgui_assert (runLoop, "Timer only works of run loop was set");
|
||||
if (!runLoop)
|
||||
return false;
|
||||
return runLoop->registerTimer (periodMs, this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Timer::stop ()
|
||||
{
|
||||
auto runLoop = RunLoop::get ();
|
||||
vstgui_assert (runLoop, "Timer only works of run loop was set");
|
||||
if (!runLoop)
|
||||
return false;
|
||||
return runLoop->unregisterTimer (this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Timer::onTimer ()
|
||||
{
|
||||
if (callback)
|
||||
callback->fire ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,32 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformtimer.h"
|
||||
#include "x11frame.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class Timer : public IPlatformTimer, public ITimerHandler
|
||||
{
|
||||
public:
|
||||
Timer (IPlatformTimerCallback* callback);
|
||||
~Timer () noexcept;
|
||||
|
||||
bool start (uint32_t periodMs) override;
|
||||
bool stop () override;
|
||||
|
||||
void onTimer () override;
|
||||
|
||||
private:
|
||||
IPlatformTimerCallback* callback = nullptr;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,197 @@
|
||||
|
||||
#include "x11utils.h"
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_util.h>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
namespace {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static xcb_visualtype_t* getVisualType (const xcb_screen_t* screen)
|
||||
{
|
||||
auto depth_iter = xcb_screen_allowed_depths_iterator (screen);
|
||||
for (; depth_iter.rem; xcb_depth_next (&depth_iter))
|
||||
{
|
||||
xcb_visualtype_iterator_t visual_iter;
|
||||
|
||||
visual_iter = xcb_depth_visuals_iterator (depth_iter.data);
|
||||
for (; visual_iter.rem; xcb_visualtype_next (&visual_iter))
|
||||
{
|
||||
if (screen->root_visual == visual_iter.data->visual_id)
|
||||
{
|
||||
return visual_iter.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // anonymous
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ChildWindow::ChildWindow (::Window parentId, CPoint size)
|
||||
: size (size), id (xcb_generate_id (RunLoop::instance ().getXcbConnection ()))
|
||||
|
||||
{
|
||||
auto connection = RunLoop::instance ().getXcbConnection ();
|
||||
auto setup = xcb_get_setup (connection);
|
||||
auto iter = xcb_setup_roots_iterator (setup);
|
||||
auto screen = iter.data;
|
||||
visual = getVisualType (screen);
|
||||
#if 0
|
||||
parentId = screen->root;
|
||||
#endif
|
||||
uint32_t paramMask = XCB_CW_BACK_PIXMAP | XCB_CW_BACKING_STORE | XCB_CW_EVENT_MASK;
|
||||
xcb_params_cw_t params{};
|
||||
params.back_pixel = XCB_BACK_PIXMAP_NONE;
|
||||
params.backing_store = XCB_BACKING_STORE_WHEN_MAPPED;
|
||||
params.event_mask =
|
||||
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
|
||||
XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT |
|
||||
XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_PROPERTY_CHANGE |
|
||||
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_FOCUS_CHANGE;
|
||||
|
||||
xcb_aux_create_window (connection, XCB_COPY_FROM_PARENT, getID (), parentId, 0, 0, size.x,
|
||||
size.y, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
|
||||
paramMask, ¶ms);
|
||||
|
||||
// setup XEMBED
|
||||
if (Atoms::xEmbedInfo.valid ())
|
||||
{
|
||||
XEmbedInfo info;
|
||||
xcb_change_property (connection, XCB_PROP_MODE_REPLACE, getID (), Atoms::xEmbedInfo (),
|
||||
Atoms::xEmbedInfo (), 32, 2, &info);
|
||||
}
|
||||
|
||||
// setup Xdnd
|
||||
if (Atoms::xDndAware.valid ())
|
||||
{
|
||||
uint32_t version = 5;
|
||||
xcb_change_property (connection, XCB_PROP_MODE_REPLACE, getID (), Atoms::xDndAware (),
|
||||
XCB_ATOM_ATOM, 32, 1, &version);
|
||||
}
|
||||
if (Atoms::xDndProxy.valid ())
|
||||
{
|
||||
uint32_t proxy = getID ();
|
||||
xcb_change_property (connection, XCB_PROP_MODE_REPLACE, getID (), Atoms::xDndProxy (),
|
||||
XCB_ATOM_WINDOW, 32, 1, &proxy);
|
||||
}
|
||||
|
||||
xcb_flush (connection);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ChildWindow::~ChildWindow () noexcept {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
xcb_window_t ChildWindow::getID () const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
xcb_visualtype_t* ChildWindow::getVisual () const
|
||||
{
|
||||
return visual;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void ChildWindow::setSize (const CRect& rect)
|
||||
{
|
||||
size = rect.getSize ();
|
||||
auto connection = RunLoop::instance ().getXcbConnection ();
|
||||
uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
|
||||
XCB_CONFIG_WINDOW_HEIGHT;
|
||||
uint32_t values[] = {static_cast<uint32_t> (rect.left), static_cast<uint32_t> (rect.top),
|
||||
static_cast<uint32_t> (rect.getWidth ()),
|
||||
static_cast<uint32_t> (rect.getHeight ())};
|
||||
xcb_configure_window (connection, getID (), mask, values);
|
||||
xcb_flush (connection);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const CPoint& ChildWindow::getSize () const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Atom::Atom (const char* name) : name (name) {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool Atom::valid () const
|
||||
{
|
||||
create ();
|
||||
return value ? true : false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
auto Atom::operator() () const -> xcb_atom_t
|
||||
{
|
||||
create ();
|
||||
return *value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void Atom::create () const
|
||||
{
|
||||
if (value)
|
||||
return;
|
||||
auto connection = RunLoop::instance ().getXcbConnection ();
|
||||
auto cookie = xcb_intern_atom (connection, 0, name.size (), name.data ());
|
||||
if (auto reply = xcb_intern_atom_reply (connection, cookie, nullptr))
|
||||
{
|
||||
value = Optional<xcb_atom_t> (reply->atom);
|
||||
free (reply);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Atoms {
|
||||
|
||||
Atom xEmbedInfo ("_XEMBED_INFO");
|
||||
Atom xEmbed ("_XEMBED");
|
||||
Atom xDndAware ("XdndAware");
|
||||
Atom xDndProxy ("XdndProxy");
|
||||
Atom xDndEnter ("XdndEnter");
|
||||
Atom xDndPosition ("XdndPosition");
|
||||
Atom xDndLeave ("XdndLeave");
|
||||
Atom xDndStatus ("XdndStatus");
|
||||
Atom xDndDrop ("XdndDrop");
|
||||
Atom xDndTypeList ("XdndTypeList");
|
||||
Atom xDndSelection ("XdndSelection");
|
||||
Atom xDndFinished ("XdndFinished");
|
||||
Atom xDndActionCopy ("XdndActionCopy");
|
||||
Atom xDndActionMove ("XdndActionMove");
|
||||
Atom xMimeTypeTextPlain ("text/plain");
|
||||
Atom xMimeTypeTextPlainUtf8 ("text/plain;charset=utf-8");
|
||||
Atom xMimeTypeUriList ("text/uri-list");
|
||||
Atom xMimeTypeApplicationOctetStream ("application/octet-stream");
|
||||
Atom xVstguiSelection ("XVSTGUISelection");
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
std::string getAtomName (xcb_atom_t atom)
|
||||
{
|
||||
std::string name;
|
||||
auto xcb = RunLoop::instance ().getXcbConnection ();
|
||||
auto cookie = xcb_get_atom_name (xcb, atom);
|
||||
if (auto reply = xcb_get_atom_name_reply (xcb, cookie, nullptr))
|
||||
{
|
||||
auto length = xcb_get_atom_name_name_length (reply);
|
||||
name.assign (
|
||||
xcb_get_atom_name_name (reply),
|
||||
xcb_get_atom_name_name_length (reply));
|
||||
free (reply);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,114 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../optional.h"
|
||||
#include "x11platform.h"
|
||||
#include <X11/Xlib.h>
|
||||
#include <string>
|
||||
|
||||
struct xcb_visualtype_t;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace X11 {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ChildWindow
|
||||
{
|
||||
using xcb_window_t = uint32_t;
|
||||
|
||||
ChildWindow (::Window parentId, CPoint size);
|
||||
|
||||
~ChildWindow () noexcept;
|
||||
|
||||
xcb_window_t getID () const;
|
||||
xcb_visualtype_t* getVisual () const;
|
||||
|
||||
void setSize (const CRect& rect);
|
||||
|
||||
const CPoint& getSize () const;
|
||||
|
||||
private:
|
||||
xcb_window_t id;
|
||||
CPoint size;
|
||||
xcb_visualtype_t* visual{nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct Atom
|
||||
{
|
||||
using xcb_atom_t = uint32_t;
|
||||
|
||||
Atom (const char* name);
|
||||
|
||||
bool valid () const;
|
||||
xcb_atom_t operator() () const;
|
||||
|
||||
private:
|
||||
void create () const;
|
||||
|
||||
std::string name;
|
||||
mutable Optional<xcb_atom_t> value;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct XEmbedInfo
|
||||
{
|
||||
uint32_t version{1};
|
||||
uint32_t flags{0};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/* XEMBED messages */
|
||||
enum class XEMBED
|
||||
{
|
||||
EMBEDDED_NOTIFY = 0,
|
||||
WINDOW_ACTIVATE = 1,
|
||||
WINDOW_DEACTIVATE = 2,
|
||||
REQUEST_FOCUS = 3,
|
||||
FOCUS_IN = 4,
|
||||
FOCUS_OUT = 5,
|
||||
FOCUS_NEXT = 6,
|
||||
FOCUS_PREV = 7,
|
||||
/* 8-9 were used for GRAB_KEY/UNGRAB_KEY */
|
||||
MODALITY_ON = 10,
|
||||
MODALITY_OFF = 11,
|
||||
REGISTER_ACCELERATOR = 12,
|
||||
UNREGISTER_ACCELERATOR = 13,
|
||||
ACTIVATE_ACCELERATOR = 14,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Atoms {
|
||||
|
||||
extern Atom xEmbedInfo;
|
||||
extern Atom xEmbed;
|
||||
extern Atom xDndAware;
|
||||
extern Atom xDndProxy;
|
||||
extern Atom xDndEnter;
|
||||
extern Atom xDndPosition;
|
||||
extern Atom xDndLeave;
|
||||
extern Atom xDndStatus;
|
||||
extern Atom xDndDrop;
|
||||
extern Atom xDndTypeList;
|
||||
extern Atom xDndSelection;
|
||||
extern Atom xDndFinished;
|
||||
extern Atom xDndActionCopy;
|
||||
extern Atom xDndActionMove;
|
||||
extern Atom xMimeTypeTextPlain;
|
||||
extern Atom xMimeTypeTextPlainUtf8;
|
||||
extern Atom xMimeTypeUriList;
|
||||
extern Atom xMimeTypeApplicationOctetStream;
|
||||
extern Atom xVstguiSelection;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
using xcb_atom_t = uint32_t;
|
||||
std::string getAtomName (xcb_atom_t atom);
|
||||
|
||||
} // X11
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,53 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformviewlayer.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#include "../platform_macos.h"
|
||||
#include <functional>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ICAViewLayerPrivate
|
||||
{
|
||||
virtual ~ICAViewLayerPrivate () = default;
|
||||
virtual void drawLayer (void* cgContext) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAViewLayer : public IPlatformViewLayer,
|
||||
public ICocoaViewLayer,
|
||||
private ICAViewLayerPrivate
|
||||
//-----------------------------------------------------------------------------
|
||||
{
|
||||
public:
|
||||
CAViewLayer (CALayer* parent);
|
||||
~CAViewLayer () noexcept override;
|
||||
|
||||
bool init (IPlatformViewLayerDelegate* drawDelegate);
|
||||
|
||||
void invalidRect (const CRect& size) override;
|
||||
void setSize (const CRect& size) override;
|
||||
void setZIndex (uint32_t zIndex) override;
|
||||
void setAlpha (float alpha) override;
|
||||
void onScaleFactorChanged (double newScaleFactor) override;
|
||||
|
||||
CALayer* getCALayer () const override { return layer; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
private:
|
||||
void drawLayer (void* cgContext) final;
|
||||
|
||||
CALayer* layer {nullptr};
|
||||
IPlatformViewLayerDelegate* drawDelegate {nullptr};
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
@@ -0,0 +1,264 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "caviewlayer.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#import "coregraphicsdevicecontext.h"
|
||||
#import "macglobals.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#if __clang__
|
||||
#if __clang_major__ >= 3 && __has_feature(objc_arc)
|
||||
#define ARC_ENABLED 1
|
||||
#endif // __has_feature(objc_arc)
|
||||
#endif // __clang__
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
//-----------------------------------------------------------------------------
|
||||
@interface VSTGUI_CALayer : CALayer
|
||||
//-----------------------------------------------------------------------------
|
||||
{
|
||||
VSTGUI::ICAViewLayerPrivate* _viewLayer;
|
||||
}
|
||||
@end
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@implementation VSTGUI_CALayer
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
self.needsDisplayOnBoundsChange = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (id<CAAction>)actionForKey:(NSString *)event
|
||||
{
|
||||
// no implicit animations
|
||||
return nil;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)setCAViewLayer:(VSTGUI::ICAViewLayerPrivate*)viewLayer
|
||||
{
|
||||
_viewLayer = viewLayer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)drawInContext:(CGContextRef)ctx
|
||||
{
|
||||
if (_viewLayer)
|
||||
_viewLayer->drawLayer (ctx);
|
||||
}
|
||||
|
||||
@end
|
||||
#else
|
||||
#import "cocoa/cocoahelpers.h"
|
||||
#import "cocoa/autoreleasepool.h"
|
||||
#import "cocoa/objcclassbuilder.h"
|
||||
//-----------------------------------------------------------------------------
|
||||
@interface VSTGUI_CALayer : CALayer
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)setCAViewLayer:(VSTGUI::ICAViewLayerPrivate*)viewLayer;
|
||||
@end
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct VSTGUI_macOS_CALayer : VSTGUI::RuntimeObjCClass<VSTGUI_macOS_CALayer>
|
||||
{
|
||||
static constexpr const auto viewLayerVarName = "_viewLayer";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Class CreateClass ()
|
||||
{
|
||||
return VSTGUI::ObjCClassBuilder ()
|
||||
.init ("VSTGUI_CALayer", [CALayer class])
|
||||
.addMethod (@selector (init), Init)
|
||||
.addMethod (@selector (actionForKey:), ActionForKey)
|
||||
.addMethod (@selector (setCAViewLayer:), SetCAViewLayer)
|
||||
.addMethod (@selector (drawInContext:), DrawInContext)
|
||||
.addIvar<VSTGUI::ICAViewLayerPrivate*> (viewLayerVarName)
|
||||
.finalize ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static id Init (id self, SEL _cmd)
|
||||
{
|
||||
self = makeInstance (self).callSuper<id (id, SEL), id> (_cmd);
|
||||
if (self)
|
||||
{
|
||||
[self setNeedsDisplayOnBoundsChange:YES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static id<CAAction> ActionForKey (id self, SEL _cmd, NSString* event) { return nil; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void SetCAViewLayer (id self, SEL _cmd, VSTGUI::CAViewLayer* viewLayer)
|
||||
{
|
||||
using namespace VSTGUI;
|
||||
if (auto var = makeInstance (self).getVariable<VSTGUI::CAViewLayer*> (viewLayerVarName))
|
||||
var->set (viewLayer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void DrawInContext (id self, SEL _cmd, CGContextRef ctx)
|
||||
{
|
||||
using namespace VSTGUI;
|
||||
|
||||
if (auto var =
|
||||
makeInstance (self).getVariable<VSTGUI::ICAViewLayerPrivate*> (viewLayerVarName);
|
||||
var.has_value ())
|
||||
{
|
||||
var->get ()->drawLayer (ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CAViewLayer::CAViewLayer (CALayer* parent)
|
||||
{
|
||||
#if !TARGET_OS_IPHONE
|
||||
layer = [VSTGUI_macOS_CALayer::alloc () init];
|
||||
#else
|
||||
layer = [VSTGUI_CALayer new];
|
||||
#endif
|
||||
[layer setContentsScale:parent.contentsScale];
|
||||
[parent addSublayer:layer];
|
||||
[(id)layer setCAViewLayer:this];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CAViewLayer::~CAViewLayer () noexcept
|
||||
{
|
||||
if (layer)
|
||||
{
|
||||
[layer removeFromSuperlayer];
|
||||
#if !ARC_ENABLED
|
||||
[layer release];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAViewLayer::init (IPlatformViewLayerDelegate* delegate)
|
||||
{
|
||||
drawDelegate = delegate;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAViewLayer::invalidRect (const CRect& size)
|
||||
{
|
||||
if (layer)
|
||||
{
|
||||
CGRect r = CGRectFromCRect (size);
|
||||
if (layer.contentsAreFlipped == layer.isGeometryFlipped)
|
||||
{
|
||||
r.origin.y = (-r.origin.y - r.size.height) + layer.frame.size.height;
|
||||
}
|
||||
[layer setNeedsDisplayInRect:r];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAViewLayer::setSize (const CRect& size)
|
||||
{
|
||||
CRect r (size);
|
||||
r.makeIntegral ();
|
||||
CGRect cgRect = CGRectFromCRect (r);
|
||||
if (layer.contentsAreFlipped == layer.isGeometryFlipped)
|
||||
{
|
||||
CGRect parentSize = layer.superlayer.frame;
|
||||
cgRect.origin.y = (-cgRect.origin.y - cgRect.size.height) + parentSize.size.height;
|
||||
}
|
||||
if (CGRectEqualToRect (layer.frame, cgRect) == false)
|
||||
layer.frame = cgRect;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAViewLayer::setZIndex (uint32_t zIndex)
|
||||
{
|
||||
if (layer)
|
||||
layer.zPosition = static_cast<CGFloat>(zIndex);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAViewLayer::setAlpha (float alpha)
|
||||
{
|
||||
if (layer)
|
||||
layer.opacity = alpha;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAViewLayer::onScaleFactorChanged (double newScaleFactor)
|
||||
{
|
||||
if (layer)
|
||||
layer.contentsScale = newScaleFactor;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAViewLayer::drawLayer (void* cgContext)
|
||||
{
|
||||
CGContextRef ctx = reinterpret_cast<CGContextRef> (cgContext);
|
||||
|
||||
#if DEBUG
|
||||
static bool visualizeLayer = false;
|
||||
if (visualizeLayer)
|
||||
CGContextClearRect (ctx, [layer bounds]);
|
||||
#endif
|
||||
|
||||
CGRect dirtyRect = CGContextGetClipBoundingBox (ctx);
|
||||
if ([layer contentsAreFlipped] == [layer isGeometryFlipped])
|
||||
{
|
||||
CGContextScaleCTM (ctx, 1, -1);
|
||||
CGContextTranslateCTM (ctx, 0, -[layer bounds].size.height);
|
||||
dirtyRect.origin.y =
|
||||
(-dirtyRect.origin.y - dirtyRect.size.height) + [layer bounds].size.height;
|
||||
}
|
||||
CGContextSaveGState (ctx);
|
||||
|
||||
auto device = getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen (
|
||||
DefaultScreenIdentifier);
|
||||
if (!device)
|
||||
return;
|
||||
auto cgDevice = std::static_pointer_cast<CoreGraphicsDevice> (device);
|
||||
if (auto deviceContext =
|
||||
std::make_shared<CoreGraphicsDeviceContext> (*cgDevice.get (), cgContext))
|
||||
{
|
||||
deviceContext->beginDraw ();
|
||||
drawDelegate->drawViewLayerRects (deviceContext, layer.contentsScale,
|
||||
{1, CRectFromCGRect (dirtyRect)});
|
||||
deviceContext->endDraw ();
|
||||
}
|
||||
|
||||
CGContextRestoreGState (ctx);
|
||||
|
||||
#if DEBUG
|
||||
if (visualizeLayer)
|
||||
{
|
||||
CGContextSetRGBFillColor (ctx, 1., 0., 0., 0.3);
|
||||
CGContextFillRect (ctx, [layer bounds]);
|
||||
CGContextSetRGBFillColor (ctx, 0., 1., 0., 0.3);
|
||||
CGContextFillRect (ctx, dirtyRect);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
@@ -0,0 +1,65 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformfont.h"
|
||||
#include "../platformfactory.h"
|
||||
|
||||
#if MAC
|
||||
#include "../../ccolor.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <CoreText/CoreText.h>
|
||||
#else
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
class MacString;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CoreTextFont : public IPlatformFont, public IFontPainter
|
||||
{
|
||||
public:
|
||||
CoreTextFont (const UTF8String& name, const CCoord& size, const int32_t& style);
|
||||
|
||||
double getAscent () const override;
|
||||
double getDescent () const override;
|
||||
double getLeading () const override;
|
||||
double getCapHeight () const override;
|
||||
|
||||
const IFontPainter* getPainter () const override { return this; }
|
||||
|
||||
CTFontRef getFontRef () const { return fontRef; }
|
||||
CGFloat getSize () const { return CTFontGetSize (fontRef); }
|
||||
|
||||
static bool getAllFontFamilies (const FontFamilyCallback& callback) noexcept;
|
||||
//------------------------------------------------------------------------------------
|
||||
protected:
|
||||
~CoreTextFont () noexcept override;
|
||||
|
||||
void drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string,
|
||||
const CPoint& p, const CColor& color, bool antialias = true) const override;
|
||||
CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string,
|
||||
bool antialias = true) const override;
|
||||
CFDictionaryRef getStringAttributes (const CGColorRef color = nullptr) const;
|
||||
|
||||
CTLineRef createCTLine (const PlatformGraphicsDeviceContextPtr& context, MacString* macString,
|
||||
const CColor& color) const;
|
||||
|
||||
CTFontRef fontRef;
|
||||
int32_t style;
|
||||
bool underlineStyle;
|
||||
mutable CColor lastColor;
|
||||
mutable CFMutableDictionaryRef stringAttributes;
|
||||
double ascent;
|
||||
double descent;
|
||||
double leading;
|
||||
double capHeight;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
@@ -0,0 +1,328 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "cfontmac.h"
|
||||
|
||||
#if MAC
|
||||
#import "macstring.h"
|
||||
#import "coregraphicsdevicecontext.h"
|
||||
#import "macglobals.h"
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_14
|
||||
#define MAC_OS_X_VERSION_10_14 101400
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct RegisterBundleFonts
|
||||
{
|
||||
static void init ()
|
||||
{
|
||||
static RegisterBundleFonts instance;
|
||||
}
|
||||
private:
|
||||
static void ErrorApplierFunction (const void *value, void *context)
|
||||
{
|
||||
auto error = CFErrorRef (value);
|
||||
CFShow (error);
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_14
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
RegisterBundleFonts ()
|
||||
{
|
||||
fontUrls = CFArrayCreateMutable (kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
auto fontTypes = {CFSTR ("ttf"), CFSTR ("ttc"), CFSTR ("otf")};
|
||||
for (auto& t : fontTypes)
|
||||
getUrlsForType (t, fontUrls);
|
||||
if (CFArrayGetCount (fontUrls) == 0)
|
||||
return;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_14
|
||||
CTFontManagerRegisterFontURLs (
|
||||
fontUrls, kCTFontManagerScopeProcess, true, [] (CFArrayRef errors, bool done) {
|
||||
CFArrayApplyFunction (errors, CFRangeMake (0, CFArrayGetCount (errors)),
|
||||
ErrorApplierFunction, nullptr);
|
||||
return true;
|
||||
});
|
||||
#else
|
||||
CFArrayRef errors;
|
||||
if (!CTFontManagerRegisterFontsForURLs (fontUrls, kCTFontManagerScopeProcess, &errors))
|
||||
{
|
||||
CFArrayApplyFunction (errors, CFRangeMake (0, CFArrayGetCount (errors)),
|
||||
ErrorApplierFunction, nullptr);
|
||||
CFRelease (errors);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
~RegisterBundleFonts ()
|
||||
{
|
||||
if (CFArrayGetCount (fontUrls) == 0)
|
||||
return;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_14
|
||||
CTFontManagerUnregisterFontURLs (
|
||||
fontUrls, kCTFontManagerScopeProcess, [] (CFArrayRef errors, bool done) {
|
||||
CFArrayApplyFunction (errors, CFRangeMake (0, CFArrayGetCount (errors)),
|
||||
ErrorApplierFunction, nullptr);
|
||||
return true;
|
||||
});
|
||||
#else
|
||||
CFArrayRef errors;
|
||||
if (!CTFontManagerUnregisterFontsForURLs (fontUrls, kCTFontManagerScopeProcess, &errors))
|
||||
{
|
||||
CFArrayApplyFunction (errors, CFRangeMake (0, CFArrayGetCount (errors)),
|
||||
ErrorApplierFunction, nullptr);
|
||||
CFRelease (errors);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_14
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
void getUrlsForType (CFStringRef fontType, CFMutableArrayRef& array)
|
||||
{
|
||||
if (auto a = CFBundleCopyResourceURLsOfType (getBundleRef (), fontType, CFSTR ("Fonts")))
|
||||
{
|
||||
CFArrayAppendArray (array, a, CFRangeMake (0, CFArrayGetCount (a)));
|
||||
CFRelease (a);
|
||||
}
|
||||
}
|
||||
|
||||
CFMutableArrayRef fontUrls;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CoreTextFont::getAllFontFamilies (const FontFamilyCallback& callback) noexcept
|
||||
{
|
||||
RegisterBundleFonts::init ();
|
||||
#if TARGET_OS_IPHONE
|
||||
NSArray* fonts = [UIFont familyNames];
|
||||
#else
|
||||
NSArray* fonts = [(NSArray*)CTFontManagerCopyAvailableFontFamilyNames () autorelease];
|
||||
#endif
|
||||
fonts = [fonts sortedArrayUsingSelector:@selector (localizedCaseInsensitiveCompare:)];
|
||||
for (uint32_t i = 0; i < [fonts count]; i++)
|
||||
{
|
||||
NSString* font = [fonts objectAtIndex:i];
|
||||
if (!callback ([font UTF8String]))
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicTraits trait)
|
||||
{
|
||||
auto traitsFontRef = CTFontCreateCopyWithSymbolicTraits (fontRef, CTFontGetSize (fontRef),
|
||||
nullptr, trait, trait);
|
||||
if (traitsFontRef)
|
||||
{
|
||||
CFRelease (fontRef);
|
||||
return traitsFontRef;
|
||||
}
|
||||
else if (trait == kCTFontItalicTrait)
|
||||
{
|
||||
CGAffineTransform transform = { 1, 0, -0.5, 1, 0, 0 };
|
||||
traitsFontRef =
|
||||
CTFontCreateCopyWithAttributes (fontRef, CTFontGetSize (fontRef), &transform, nullptr);
|
||||
if (traitsFontRef)
|
||||
{
|
||||
CFRelease (fontRef);
|
||||
return traitsFontRef;
|
||||
}
|
||||
}
|
||||
return fontRef;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CoreTextFont::CoreTextFont (const UTF8String& name, const CCoord& size, const int32_t& style)
|
||||
: fontRef (nullptr)
|
||||
, style (style)
|
||||
, underlineStyle (false)
|
||||
, lastColor (MakeCColor (0,0,0,0))
|
||||
, stringAttributes (nullptr)
|
||||
, ascent (0.)
|
||||
, descent (0.)
|
||||
, leading (0.)
|
||||
, capHeight (0.)
|
||||
{
|
||||
RegisterBundleFonts::init ();
|
||||
CFStringRef fontNameRef = fromUTF8String<CFStringRef> (name);
|
||||
if (fontNameRef)
|
||||
{
|
||||
if (@available (macOS 10.10, *))
|
||||
{
|
||||
auto attributes =
|
||||
CFDictionaryCreateMutable (kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionaryAddValue (attributes, kCTFontFamilyNameAttribute, fontNameRef);
|
||||
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes (attributes);
|
||||
fontRef = CTFontCreateWithFontDescriptor (descriptor, static_cast<CGFloat> (size), nullptr);
|
||||
CFRelease (attributes);
|
||||
CFRelease (descriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
fontRef = CTFontCreateWithName (fontNameRef, static_cast<CGFloat> (size), nullptr);
|
||||
}
|
||||
|
||||
if (style & kBoldFace)
|
||||
fontRef = CoreTextCreateTraitsVariant (fontRef, kCTFontBoldTrait);
|
||||
if (style & kItalicFace)
|
||||
fontRef = CoreTextCreateTraitsVariant (fontRef, kCTFontItalicTrait);
|
||||
if (fontRef)
|
||||
{
|
||||
ascent = CTFontGetAscent (fontRef);
|
||||
descent = CTFontGetDescent (fontRef);
|
||||
leading = CTFontGetLeading (fontRef);
|
||||
capHeight = CTFontGetCapHeight (fontRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CoreTextFont::~CoreTextFont () noexcept
|
||||
{
|
||||
if (stringAttributes)
|
||||
CFRelease (stringAttributes);
|
||||
if (fontRef)
|
||||
CFRelease (fontRef);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
double CoreTextFont::getAscent () const
|
||||
{
|
||||
return ascent;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
double CoreTextFont::getDescent () const
|
||||
{
|
||||
return descent;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
double CoreTextFont::getLeading () const
|
||||
{
|
||||
return leading;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
double CoreTextFont::getCapHeight () const
|
||||
{
|
||||
return capHeight;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CFDictionaryRef CoreTextFont::getStringAttributes (const CGColorRef color) const
|
||||
{
|
||||
if (stringAttributes == nullptr)
|
||||
{
|
||||
stringAttributes =
|
||||
CFDictionaryCreateMutable (kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue (stringAttributes, kCTFontAttributeName, fontRef);
|
||||
}
|
||||
if (color)
|
||||
{
|
||||
CFDictionarySetValue (stringAttributes, kCTForegroundColorAttributeName, color);
|
||||
}
|
||||
return stringAttributes;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CTLineRef CoreTextFont::createCTLine (const PlatformGraphicsDeviceContextPtr& context,
|
||||
MacString* macString, const CColor& color) const
|
||||
{
|
||||
if (macString->getCTLineFontRef () == this && macString->getCTLineColor () == color)
|
||||
{
|
||||
CTLineRef line = macString->getCTLine ();
|
||||
CFRetain (line);
|
||||
return line;
|
||||
}
|
||||
CFStringRef cfStr = macString->getCFString ();
|
||||
if (cfStr == nullptr)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugPrint ("Empty CFStringRef in MacString. This is unexpected !\n");
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CGColorRef cgColorRef = nullptr;
|
||||
if (color != lastColor)
|
||||
{
|
||||
cgColorRef = getCGColor (color);
|
||||
lastColor = color;
|
||||
}
|
||||
|
||||
if (auto attrStr =
|
||||
CFAttributedStringCreate (kCFAllocatorDefault, cfStr, getStringAttributes (cgColorRef)))
|
||||
{
|
||||
CTLineRef line = CTLineCreateWithAttributedString (attrStr);
|
||||
if (context && line)
|
||||
{
|
||||
macString->setCTLine (line, this, color);
|
||||
}
|
||||
CFRelease (attrStr);
|
||||
return line;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CoreTextFont::drawString (const PlatformGraphicsDeviceContextPtr& context,
|
||||
IPlatformString* string, const CPoint& point, const CColor& color,
|
||||
bool antialias) const
|
||||
{
|
||||
MacString* macString = dynamic_cast<MacString*> (string);
|
||||
if (macString == nullptr)
|
||||
return;
|
||||
|
||||
auto deviceContext = std::dynamic_pointer_cast<CoreGraphicsDeviceContext> (context);
|
||||
if (!deviceContext)
|
||||
return;
|
||||
|
||||
CTLineRef line = createCTLine (context, macString, color);
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
CGPoint cgPoint = CGPointFromCPoint (point);
|
||||
deviceContext->drawCTLine (line, cgPoint, fontRef, color, style & kUnderlineFace,
|
||||
style & kStrikethroughFace, antialias);
|
||||
CFRelease (line);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CCoord CoreTextFont::getStringWidth (const PlatformGraphicsDeviceContextPtr& context,
|
||||
IPlatformString* string, bool antialias) const
|
||||
{
|
||||
CCoord result = 0;
|
||||
MacString* macString = dynamic_cast<MacString*> (string);
|
||||
if (macString == nullptr)
|
||||
return result;
|
||||
|
||||
CTLineRef line = createCTLine (context, macString, kBlackCColor);
|
||||
if (line)
|
||||
{
|
||||
result = CTLineGetTypographicBounds (line, nullptr, nullptr, nullptr);
|
||||
CFRelease (line);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
@@ -0,0 +1,452 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cgbitmap.h"
|
||||
#include "../../cresourcedescription.h"
|
||||
|
||||
#if MAC
|
||||
#include "macglobals.h"
|
||||
#include <Accelerate/Accelerate.h>
|
||||
#include <AssertMacros.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <MobileCoreServices/MobileCoreServices.h>
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr CGBitmap::create (CPoint* size)
|
||||
{
|
||||
if (size)
|
||||
return makeOwned<CGBitmap> (*size);
|
||||
return makeOwned<CGBitmap> ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr CGBitmap::createFromPath (UTF8StringPtr absolutePath)
|
||||
{
|
||||
PlatformBitmapPtr bitmap;
|
||||
CFURLRef url = CFURLCreateFromFileSystemRepresentation (nullptr, (const UInt8*)absolutePath, static_cast<CFIndex> (strlen (absolutePath)), false);
|
||||
if (url)
|
||||
{
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL (url, nullptr);
|
||||
if (source)
|
||||
{
|
||||
auto cgBitmap = makeOwned<CGBitmap> ();
|
||||
bool result = cgBitmap->loadFromImageSource (source);
|
||||
if (result)
|
||||
bitmap = std::move (cgBitmap);
|
||||
CFRelease (source);
|
||||
}
|
||||
CFRelease (url);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr CGBitmap::createFromMemory (const void* ptr, uint32_t memSize)
|
||||
{
|
||||
PlatformBitmapPtr bitmap;
|
||||
CFDataRef data = CFDataCreate (nullptr, (const UInt8*)ptr, static_cast<CFIndex> (memSize));
|
||||
if (data)
|
||||
{
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData (data, nullptr);
|
||||
if (source)
|
||||
{
|
||||
auto cgBitmap = makeOwned<CGBitmap> ();
|
||||
bool result = cgBitmap->loadFromImageSource (source);
|
||||
if (result)
|
||||
bitmap = std::move (cgBitmap);
|
||||
CFRelease (source);
|
||||
}
|
||||
CFRelease (data);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PNGBitmapBuffer CGBitmap::createMemoryPNGRepresentation (const PlatformBitmapPtr& bitmap)
|
||||
{
|
||||
PNGBitmapBuffer buffer;
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (auto cgBitmap = bitmap.cast<CGBitmap> ())
|
||||
{
|
||||
CGImageRef image = cgBitmap->getCGImage ();
|
||||
if (image)
|
||||
{
|
||||
CFMutableDataRef data = CFDataCreateMutable (nullptr, 0);
|
||||
if (data)
|
||||
{
|
||||
CGImageDestinationRef dest = CGImageDestinationCreateWithData (data, CFSTR ("public.png"), 1, nullptr);
|
||||
if (dest)
|
||||
{
|
||||
auto scaleFactor = bitmap->getScaleFactor ();
|
||||
CFMutableDictionaryRef properties = nullptr;
|
||||
if (scaleFactor != 1.)
|
||||
{
|
||||
properties = CFDictionaryCreateMutable (nullptr, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
double dpi = 72 * scaleFactor;
|
||||
auto number = CFNumberCreate(nullptr, kCFNumberDoubleType, &dpi);
|
||||
CFDictionaryAddValue (properties, kCGImagePropertyDPIWidth, number);
|
||||
CFDictionaryAddValue (properties, kCGImagePropertyDPIHeight, number);
|
||||
CFRelease (number);
|
||||
}
|
||||
CGImageDestinationAddImage (dest, image, properties);
|
||||
if (CGImageDestinationFinalize (dest))
|
||||
{
|
||||
buffer.resize(CFDataGetLength (data));
|
||||
CFDataGetBytes (data, CFRangeMake (0, CFDataGetLength (data)), buffer.data ());
|
||||
}
|
||||
if (properties)
|
||||
CFRelease (properties);
|
||||
CFRelease (dest);
|
||||
}
|
||||
CFRelease (data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGBitmap::CGBitmap (const CPoint& inSize)
|
||||
: size (inSize)
|
||||
{
|
||||
allocBits ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGBitmap::CGBitmap (CGImageRef image)
|
||||
: image (image)
|
||||
{
|
||||
CGImageRetain (image);
|
||||
size.x = CGImageGetWidth (image);
|
||||
size.y = CGImageGetHeight (image);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGBitmap::CGBitmap ()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGBitmap::~CGBitmap () noexcept
|
||||
{
|
||||
if (image)
|
||||
CGImageRelease (image);
|
||||
if (layer)
|
||||
CFRelease (layer);
|
||||
if (imageSource)
|
||||
CFRelease (imageSource);
|
||||
if (bitsDataProvider)
|
||||
CFRelease (bitsDataProvider);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CGBitmap::load (const CResourceDescription& desc)
|
||||
{
|
||||
if (bits)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
if (getBundleRef ())
|
||||
{
|
||||
// find the bitmap in our Bundle.
|
||||
// If the resource description is of type integer, it must be in the form of bmp00123.png, where the resource id would be 123.
|
||||
// else it just uses the name
|
||||
char filename [PATH_MAX];
|
||||
if (desc.type == CResourceDescription::kIntegerType)
|
||||
snprintf (filename, PATH_MAX, "bmp%05d", (int32_t)desc.u.id);
|
||||
else
|
||||
std::strcpy (filename, desc.u.name);
|
||||
CFStringRef cfStr = CFStringCreateWithCString (nullptr, filename, kCFStringEncodingUTF8);
|
||||
if (cfStr)
|
||||
{
|
||||
CFURLRef url = nullptr;
|
||||
if (filename[0] == '/')
|
||||
url = CFURLCreateFromFileSystemRepresentation (nullptr, (const UInt8*)filename, static_cast<CFIndex> (strlen (filename)), false);
|
||||
int32_t i = 0;
|
||||
while (url == nullptr)
|
||||
{
|
||||
static CFStringRef resTypes [] = { CFSTR("png"), CFSTR("bmp"), CFSTR("jpg"), CFSTR("pict"), nullptr };
|
||||
url = CFBundleCopyResourceURL (getBundleRef (), cfStr, desc.type == CResourceDescription::kIntegerType ? resTypes[i] : nullptr, nullptr);
|
||||
if (resTypes[++i] == nullptr)
|
||||
break;
|
||||
}
|
||||
CFRelease (cfStr);
|
||||
if (url)
|
||||
{
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL (url, nullptr);
|
||||
if (source)
|
||||
{
|
||||
result = loadFromImageSource (source);
|
||||
CFRelease (source);
|
||||
}
|
||||
CFRelease (url);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
if (result == false)
|
||||
{
|
||||
if (desc.type == CResourceDescription::kIntegerType)
|
||||
DebugPrint ("*** Bitmap Nr.:%d not found.\n", desc.u.id);
|
||||
else
|
||||
DebugPrint ("*** Bitmap '%s' not found.\n", desc.u.name);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
|
||||
|
||||
#define VSTGUI_QUARTZ_WORKAROUND_PNG_DECODE_ON_DRAW_BUG __i386__ || TARGET_OS_IPHONE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CGBitmap::loadFromImageSource (CGImageSourceRef source)
|
||||
{
|
||||
imageSource = source;
|
||||
if (imageSource)
|
||||
{
|
||||
CFRetain (imageSource);
|
||||
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex (imageSource, 0, nullptr);
|
||||
if (properties == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
CFNumberRef value = (CFNumberRef)CFDictionaryGetValue (properties, kCGImagePropertyPixelHeight);
|
||||
if (value)
|
||||
{
|
||||
double fValue = 0;
|
||||
if (CFNumberGetValue (value, kCFNumberDoubleType, &fValue))
|
||||
size.y = fValue;
|
||||
}
|
||||
value = (CFNumberRef)CFDictionaryGetValue (properties, kCGImagePropertyPixelWidth);
|
||||
if (value)
|
||||
{
|
||||
double fValue = 0;
|
||||
if (CFNumberGetValue (value, kCFNumberDoubleType, &fValue))
|
||||
size.x = fValue;
|
||||
}
|
||||
#if VSTGUI_QUARTZ_WORKAROUND_PNG_DECODE_ON_DRAW_BUG
|
||||
// workaround a bug in Mac OS X 10.6 (32 bit), where PNG bitmaps were decoded all the time when drawn.
|
||||
// we fix this by copying the pixels of the bitmap into our own buffer.
|
||||
CFStringRef imageType = CGImageSourceGetType (imageSource);
|
||||
if (imageType && CFStringCompare (imageType, kUTTypePNG, 0) == kCFCompareEqualTo)
|
||||
{
|
||||
CGContextRef context = createCGContext ();
|
||||
if (context)
|
||||
{
|
||||
dirty = true;
|
||||
CFRelease (context);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
CFRelease (properties);
|
||||
}
|
||||
return (size.x != 0 && size.y != 0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGImageRef CGBitmap::getCGImage ()
|
||||
{
|
||||
if (image == nullptr && imageSource)
|
||||
{
|
||||
const void* keys[] = {kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32};
|
||||
const void* values[] = {kCFBooleanTrue, kCFBooleanTrue};
|
||||
CFDictionaryRef options = CFDictionaryCreate (nullptr, keys, values, 2, nullptr, nullptr);
|
||||
image = CGImageSourceCreateImageAtIndex (imageSource, 0, options);
|
||||
CFRelease (imageSource);
|
||||
CFRelease (options);
|
||||
imageSource = nullptr;
|
||||
}
|
||||
if ((dirty || image == nullptr) && bits)
|
||||
{
|
||||
freeCGImage ();
|
||||
|
||||
size_t rowBytes = getBytesPerRow ();
|
||||
size_t bitDepth = 32;
|
||||
|
||||
CGBitmapInfo bitmapInfo =
|
||||
static_cast<CGBitmapInfo> (kCGImageAlphaPremultipliedFirst) | kCGBitmapByteOrder32Big;
|
||||
image = CGImageCreate (static_cast<size_t> (size.x), static_cast<size_t> (size.y), 8, bitDepth, rowBytes, GetCGColorSpace (), bitmapInfo, bitsDataProvider, nullptr, false, kCGRenderingIntentDefault);
|
||||
dirty = false;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGContextRef CGBitmap::createCGContext ()
|
||||
{
|
||||
CGContextRef context = nullptr;
|
||||
if (bits == nullptr)
|
||||
{
|
||||
allocBits ();
|
||||
if (imageSource)
|
||||
getCGImage ();
|
||||
if (image)
|
||||
{
|
||||
context = createCGContext ();
|
||||
if (context)
|
||||
{
|
||||
CGContextScaleCTM (context, 1, -1);
|
||||
CGContextDrawImage (context, CGRectMake (0, static_cast<CGFloat> (-size.y), static_cast<CGFloat> (size.x), static_cast<CGFloat> (size.y)), image);
|
||||
CGContextScaleCTM (context, 1, -1);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bits)
|
||||
{
|
||||
CGBitmapInfo bitmapInfo =
|
||||
static_cast<CGBitmapInfo> (kCGImageAlphaPremultipliedFirst) | kCGBitmapByteOrder32Big;
|
||||
context = CGBitmapContextCreate (bits,
|
||||
static_cast<size_t> (size.x),
|
||||
static_cast<size_t> (size.y),
|
||||
8,
|
||||
getBytesPerRow (),
|
||||
GetCGColorSpace (),
|
||||
bitmapInfo);
|
||||
CGContextTranslateCTM (context, 0, (CGFloat)size.y);
|
||||
CGContextScaleCTM (context, 1, -1);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGLayerRef CGBitmap::createCGLayer (CGContextRef context)
|
||||
{
|
||||
if (layer && !dirty)
|
||||
return layer;
|
||||
CGImageRef cgImage = getCGImage ();
|
||||
layer = cgImage ? CGLayerCreateWithContext (context, CGSizeFromCPoint (size), nullptr) : nullptr;
|
||||
if (layer)
|
||||
{
|
||||
CGContextRef layerContext = CGLayerGetContext (layer);
|
||||
CGContextDrawImage (layerContext, CGRectMake (0, 0, static_cast<CGFloat> (size.x), static_cast<CGFloat> (size.y)), cgImage);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGBitmap::allocBits ()
|
||||
{
|
||||
if (bits == nullptr)
|
||||
{
|
||||
bytesPerRow = static_cast<uint32_t> (size.x * 4);
|
||||
if (bytesPerRow % 16)
|
||||
bytesPerRow += 16 - (bytesPerRow % 16);
|
||||
uint32_t bitmapByteCount = bytesPerRow * static_cast<uint32_t> (size.y);
|
||||
bits = calloc (1, bitmapByteCount);
|
||||
bitsDataProvider = CGDataProviderCreateWithData (
|
||||
nullptr, bits, bitmapByteCount,
|
||||
[] (void* __nullable info, const void* data, size_t) {
|
||||
std::free (const_cast<void*> (data));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGBitmap::freeCGImage ()
|
||||
{
|
||||
if (image)
|
||||
CFRelease (image);
|
||||
image = nullptr;
|
||||
if (layer)
|
||||
CFRelease (layer);
|
||||
layer = nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGBitmapPixelAccess : public IPlatformBitmapPixelAccess
|
||||
{
|
||||
public:
|
||||
CGBitmapPixelAccess (CGBitmap* bitmap, bool alphaPremultiplied)
|
||||
: bitmap (bitmap)
|
||||
, alphaPremultiplied (alphaPremultiplied)
|
||||
{
|
||||
if (!alphaPremultiplied)
|
||||
{
|
||||
vImage_Buffer buffer;
|
||||
buffer.data = bitmap->getBits ();
|
||||
buffer.width = static_cast<vImagePixelCount> (bitmap->getSize ().x);
|
||||
buffer.height = static_cast<vImagePixelCount> (bitmap->getSize ().y);
|
||||
buffer.rowBytes = bitmap->getBytesPerRow ();
|
||||
#if DEBUG
|
||||
vImage_Error error =
|
||||
#endif
|
||||
vImageUnpremultiplyData_ARGB8888 (&buffer, &buffer, kvImageNoFlags);
|
||||
#if DEBUG
|
||||
assert (error == kvImageNoError);
|
||||
#endif
|
||||
}
|
||||
bitmap->remember ();
|
||||
}
|
||||
|
||||
~CGBitmapPixelAccess () noexcept override
|
||||
{
|
||||
if (!alphaPremultiplied)
|
||||
{
|
||||
vImage_Buffer buffer;
|
||||
buffer.data = bitmap->getBits ();
|
||||
buffer.width = static_cast<vImagePixelCount> (bitmap->getSize ().x);
|
||||
buffer.height = static_cast<vImagePixelCount> (bitmap->getSize ().y);
|
||||
buffer.rowBytes = bitmap->getBytesPerRow ();
|
||||
#if DEBUG
|
||||
vImage_Error error =
|
||||
#endif
|
||||
vImagePremultiplyData_ARGB8888 (&buffer, &buffer, kvImageNoFlags);
|
||||
#if DEBUG
|
||||
assert (error == kvImageNoError);
|
||||
#endif
|
||||
}
|
||||
bitmap->setDirty ();
|
||||
bitmap->forget ();
|
||||
}
|
||||
|
||||
uint8_t* getAddress () const override
|
||||
{
|
||||
return (uint8_t*)bitmap->getBits ();
|
||||
}
|
||||
|
||||
uint32_t getBytesPerRow () const override
|
||||
{
|
||||
return bitmap->getBytesPerRow ();
|
||||
}
|
||||
|
||||
PixelFormat getPixelFormat () const override
|
||||
{
|
||||
#ifdef __BIG_ENDIAN__
|
||||
return kRGBA;
|
||||
#else
|
||||
return kARGB;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
CGBitmap* bitmap;
|
||||
bool alphaPremultiplied;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformBitmapPixelAccess> CGBitmap::lockPixels (bool alphaPremultiplied)
|
||||
{
|
||||
if (bits == nullptr)
|
||||
{
|
||||
CGContextRef context = createCGContext ();
|
||||
if (context)
|
||||
CFRelease (context);
|
||||
}
|
||||
if (bits)
|
||||
{
|
||||
return makeOwned<CGBitmapPixelAccess> (this, alphaPremultiplied);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformbitmap.h"
|
||||
#include "../platformfwd.h"
|
||||
|
||||
#if MAC
|
||||
#include "../../cpoint.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <ImageIO/ImageIO.h>
|
||||
#else
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGBitmap : public IPlatformBitmap
|
||||
{
|
||||
public:
|
||||
static PlatformBitmapPtr create (CPoint* size);
|
||||
static PlatformBitmapPtr createFromPath (UTF8StringPtr absolutePath);
|
||||
static PlatformBitmapPtr createFromMemory (const void* ptr, uint32_t memSize);
|
||||
static PNGBitmapBuffer createMemoryPNGRepresentation (const PlatformBitmapPtr& bitmap);
|
||||
|
||||
explicit CGBitmap (const CPoint& size);
|
||||
explicit CGBitmap (CGImageRef image);
|
||||
CGBitmap ();
|
||||
~CGBitmap () noexcept override;
|
||||
|
||||
bool load (const CResourceDescription& desc);
|
||||
const CPoint& getSize () const override { return size; }
|
||||
SharedPointer<IPlatformBitmapPixelAccess> lockPixels (bool alphaPremultiplied) override;
|
||||
void setScaleFactor (double factor) override { scaleFactor = factor; }
|
||||
double getScaleFactor () const override { return scaleFactor; }
|
||||
|
||||
CGImageRef getCGImage ();
|
||||
CGContextRef createCGContext ();
|
||||
bool loadFromImageSource (CGImageSourceRef source);
|
||||
|
||||
void setDirty () { dirty = true; }
|
||||
void* getBits () const { return bits; }
|
||||
uint32_t getBytesPerRow () const { return bytesPerRow; }
|
||||
|
||||
CGLayerRef createCGLayer (CGContextRef context);
|
||||
CGLayerRef getCGLayer () const { return layer; }
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
void allocBits ();
|
||||
void freeCGImage ();
|
||||
|
||||
CPoint size;
|
||||
CGImageRef image {nullptr};
|
||||
CGImageSourceRef imageSource {nullptr};
|
||||
|
||||
CGLayerRef layer {nullptr};
|
||||
|
||||
CGDataProviderRef bitsDataProvider {nullptr};
|
||||
|
||||
void* bits {nullptr};
|
||||
bool dirty {false};
|
||||
uint32_t bytesPerRow {0};
|
||||
double scaleFactor {1.};
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../vstguibase.h"
|
||||
|
||||
#if MAC
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class NSAutoreleasePool;
|
||||
#else
|
||||
struct NSAutoreleasePool;
|
||||
#endif // __OBJC__
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
class AutoreleasePool
|
||||
{
|
||||
public:
|
||||
AutoreleasePool ();
|
||||
~AutoreleasePool () noexcept;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
protected:
|
||||
NSAutoreleasePool* pool;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "autoreleasepool.h"
|
||||
|
||||
#if MAC
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
AutoreleasePool::AutoreleasePool ()
|
||||
{
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
AutoreleasePool::~AutoreleasePool () noexcept
|
||||
{
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
@@ -0,0 +1,131 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "../../../crect.h"
|
||||
#import "../../../cpoint.h"
|
||||
#import "../../../ccolor.h"
|
||||
#import "../../../vstguifwd.h"
|
||||
|
||||
#if MAC_COCOA && defined (__OBJC__)
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define HIDDEN __attribute__((__visibility__("hidden")))
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
extern HIDDEN bool CreateKeyboardEventFromNSEvent (NSEvent* theEvent, VSTGUI::KeyboardEvent& event);
|
||||
extern HIDDEN NSString* GetVirtualKeyCodeString (VSTGUI::VirtualKey virtualKey);
|
||||
extern HIDDEN int32_t eventButton (NSEvent* theEvent);
|
||||
extern HIDDEN void convertPointToGlobal (NSView* view, NSPoint& p);
|
||||
extern HIDDEN NSImage* bitmapToNSImage (VSTGUI::CBitmap* bitmap);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN inline NSRect nsRectFromCRect (const VSTGUI::CRect& rect)
|
||||
{
|
||||
NSRect r;
|
||||
r.origin.x = static_cast<CGFloat> (rect.left);
|
||||
r.origin.y = static_cast<CGFloat> (rect.top);
|
||||
r.size.width = static_cast<CGFloat> (rect.getWidth ());
|
||||
r.size.height = static_cast<CGFloat> (rect.getHeight ());
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN inline NSPoint nsPointFromCPoint (const VSTGUI::CPoint& point)
|
||||
{
|
||||
NSPoint p = { static_cast<CGFloat>(point.x), static_cast<CGFloat>(point.y) };
|
||||
return p;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN inline VSTGUI::CRect rectFromNSRect (const NSRect& rect)
|
||||
{
|
||||
VSTGUI::CRect r (rect.origin.x, rect.origin.y, 0, 0);
|
||||
r.setWidth (rect.size.width);
|
||||
r.setHeight (rect.size.height);
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN inline VSTGUI::CPoint pointFromNSPoint (const NSPoint& point)
|
||||
{
|
||||
VSTGUI::CPoint p (point.x, point.y);
|
||||
return p;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN inline NSColor* nsColorFromCColor (const VSTGUI::CColor& color)
|
||||
{
|
||||
return [NSColor colorWithDeviceRed:color.normRed<CGFloat> ()
|
||||
green:color.normGreen<CGFloat> ()
|
||||
blue:color.normBlue<CGFloat> ()
|
||||
alpha:color.normAlpha<CGFloat> ()];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
struct MacEventModifier
|
||||
{
|
||||
enum mask
|
||||
{
|
||||
#ifdef MAC_OS_X_VERSION_10_12
|
||||
ShiftKeyMask = NSEventModifierFlagShift,
|
||||
CommandKeyMask = NSEventModifierFlagCommand,
|
||||
AlternateKeyMask = NSEventModifierFlagOption,
|
||||
ControlKeyMask = NSEventModifierFlagControl
|
||||
#else
|
||||
ShiftKeyMask = NSShiftKeyMask,
|
||||
CommandKeyMask = NSCommandKeyMask,
|
||||
AlternateKeyMask = NSAlternateKeyMask,
|
||||
ControlKeyMask = NSControlKeyMask
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
namespace MacEventType
|
||||
{
|
||||
#ifdef MAC_OS_X_VERSION_10_12
|
||||
static constexpr auto LeftMouseDown = ::NSEventTypeLeftMouseDown;
|
||||
static constexpr auto LeftMouseDragged = ::NSEventTypeLeftMouseDragged;
|
||||
static constexpr auto MouseMoved = ::NSEventTypeMouseMoved;
|
||||
static constexpr auto KeyDown = ::NSEventTypeKeyDown;
|
||||
static constexpr auto KeyUp = ::NSEventTypeKeyUp;
|
||||
#else
|
||||
static constexpr auto LeftMouseDown = ::NSLeftMouseDown;
|
||||
static constexpr auto LeftMouseDragged = ::NSLeftMouseDragged;
|
||||
static constexpr auto MouseMoved = ::NSMouseMoved;
|
||||
static constexpr auto KeyDown = ::NSKeyDown;
|
||||
static constexpr auto KeyUp = ::NSKeyUp;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
namespace MacWindowStyleMask
|
||||
{
|
||||
#ifdef MAC_OS_X_VERSION_10_12
|
||||
static constexpr auto Borderless = ::NSWindowStyleMaskBorderless;
|
||||
static constexpr auto Titled = ::NSWindowStyleMaskTitled;
|
||||
static constexpr auto Resizable = ::NSWindowStyleMaskResizable;
|
||||
static constexpr auto Miniaturizable = ::NSWindowStyleMaskMiniaturizable;
|
||||
static constexpr auto Closable = ::NSWindowStyleMaskClosable;
|
||||
static constexpr auto Utility = ::NSWindowStyleMaskUtilityWindow;
|
||||
static constexpr auto FullSizeContentView = ::NSWindowStyleMaskFullSizeContentView;
|
||||
#else
|
||||
static constexpr auto Borderless = ::NSBorderlessWindowMask;
|
||||
static constexpr auto Titled = ::NSTitledWindowMask;
|
||||
static constexpr auto Resizable = ::NSResizableWindowMask;
|
||||
static constexpr auto Miniaturizable = ::NSMiniaturizableWindowMask;
|
||||
static constexpr auto Closable = ::NSClosableWindowMask;
|
||||
static constexpr auto Utility = ::NSUtilityWindowMask;
|
||||
static constexpr auto FullSizeContentView = ::NSFullSizeContentViewWindowMask;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MAC_COCOA
|
||||
@@ -0,0 +1,259 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cocoahelpers.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#include "../../../vstkeycode.h"
|
||||
#include "../../../events.h"
|
||||
#include "../../../cview.h"
|
||||
#include "../../../cbitmap.h"
|
||||
#include "../cgbitmap.h"
|
||||
|
||||
using namespace VSTGUI;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN bool CreateKeyboardEventFromNSEvent (NSEvent* theEvent, KeyboardEvent& event)
|
||||
{
|
||||
if (theEvent.type == NSEventTypeKeyUp)
|
||||
event.type = EventType::KeyUp;
|
||||
else if (theEvent.type == NSEventTypeKeyDown || theEvent.type == NSEventTypeFlagsChanged)
|
||||
{
|
||||
event.type = EventType::KeyDown;
|
||||
if (theEvent.ARepeat)
|
||||
event.isRepeat = true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
NSString *s = [theEvent charactersIgnoringModifiers];
|
||||
if ([s length] == 1)
|
||||
{
|
||||
char32_t utf32Char = {};
|
||||
if (![s getBytes:&utf32Char
|
||||
maxLength:sizeof (utf32Char)
|
||||
usedLength:nullptr
|
||||
encoding:NSUTF32StringEncoding
|
||||
options:0
|
||||
range:NSMakeRange (0, 1)
|
||||
remainingRange:nullptr])
|
||||
{
|
||||
utf32Char = [s characterAtIndex:0];
|
||||
}
|
||||
switch (utf32Char)
|
||||
{
|
||||
case 8: case 0x7f: event.virt = VirtualKey::Back; break;
|
||||
case 9: case 0x19: event.virt = VirtualKey::Tab; break;
|
||||
case NSClearLineFunctionKey: event.virt = VirtualKey::Clear; break;
|
||||
case 0xd: event.virt = VirtualKey::Return; break;
|
||||
case NSPauseFunctionKey: event.virt = VirtualKey::Pause; break;
|
||||
case 0x1b: event.virt = VirtualKey::Escape; break;
|
||||
case ' ': event.virt = VirtualKey::Space; break;
|
||||
case NSNextFunctionKey: event.virt = VirtualKey::Next; break;
|
||||
case NSEndFunctionKey: event.virt = VirtualKey::End; break;
|
||||
case NSHomeFunctionKey: event.virt = VirtualKey::Home; break;
|
||||
|
||||
case NSLeftArrowFunctionKey: event.virt = VirtualKey::Left; break;
|
||||
case NSUpArrowFunctionKey: event.virt = VirtualKey::Up; break;
|
||||
case NSRightArrowFunctionKey: event.virt = VirtualKey::Right; break;
|
||||
case NSDownArrowFunctionKey: event.virt = VirtualKey::Down; break;
|
||||
case NSPageUpFunctionKey: event.virt = VirtualKey::PageUp; break;
|
||||
case NSPageDownFunctionKey: event.virt = VirtualKey::PageDown; break;
|
||||
|
||||
case NSSelectFunctionKey: event.virt = VirtualKey::Select; break;
|
||||
case NSPrintFunctionKey: event.virt = VirtualKey::Print; break;
|
||||
// VirtualKey::ENTER
|
||||
// VirtualKey::SNAPSHOT
|
||||
case NSInsertFunctionKey: event.virt = VirtualKey::Insert; break;
|
||||
case NSDeleteFunctionKey: event.virt = VirtualKey::Delete; break;
|
||||
case NSHelpFunctionKey: event.virt = VirtualKey::Help; break;
|
||||
|
||||
|
||||
case NSF1FunctionKey: event.virt = VirtualKey::F1; break;
|
||||
case NSF2FunctionKey: event.virt = VirtualKey::F2; break;
|
||||
case NSF3FunctionKey: event.virt = VirtualKey::F3; break;
|
||||
case NSF4FunctionKey: event.virt = VirtualKey::F4; break;
|
||||
case NSF5FunctionKey: event.virt = VirtualKey::F5; break;
|
||||
case NSF6FunctionKey: event.virt = VirtualKey::F6; break;
|
||||
case NSF7FunctionKey: event.virt = VirtualKey::F7; break;
|
||||
case NSF8FunctionKey: event.virt = VirtualKey::F8; break;
|
||||
case NSF9FunctionKey: event.virt = VirtualKey::F9; break;
|
||||
case NSF10FunctionKey: event.virt = VirtualKey::F10; break;
|
||||
case NSF11FunctionKey: event.virt = VirtualKey::F11; break;
|
||||
case NSF12FunctionKey: event.virt = VirtualKey::F12; break;
|
||||
default:
|
||||
{
|
||||
switch ([theEvent keyCode])
|
||||
{
|
||||
case 82: event.virt = VirtualKey::NumPad0; break;
|
||||
case 83: event.virt = VirtualKey::NumPad1; break;
|
||||
case 84: event.virt = VirtualKey::NumPad2; break;
|
||||
case 85: event.virt = VirtualKey::NumPad3; break;
|
||||
case 86: event.virt = VirtualKey::NumPad4; break;
|
||||
case 87: event.virt = VirtualKey::NumPad5; break;
|
||||
case 88: event.virt = VirtualKey::NumPad6; break;
|
||||
case 89: event.virt = VirtualKey::NumPad7; break;
|
||||
case 91: event.virt = VirtualKey::NumPad8; break;
|
||||
case 92: event.virt = VirtualKey::NumPad9; break;
|
||||
case 67: event.virt = VirtualKey::Multiply; break;
|
||||
case 69: event.virt = VirtualKey::Add; break;
|
||||
case 78: event.virt = VirtualKey::Subtract; break;
|
||||
case 65: event.virt = VirtualKey::Decimal; break;
|
||||
case 75: event.virt = VirtualKey::Divide; break;
|
||||
case 76: event.virt = VirtualKey::Enter; break;
|
||||
default:
|
||||
{
|
||||
if ((utf32Char >= 'A') && (utf32Char <= 'Z'))
|
||||
utf32Char += ('a' - 'A');
|
||||
else
|
||||
utf32Char = static_cast<char32_t> (tolower (utf32Char));
|
||||
event.character = utf32Char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSUInteger modifiers = [theEvent modifierFlags];
|
||||
if (modifiers & MacEventModifier::ShiftKeyMask)
|
||||
event.modifiers.add (ModifierKey::Shift);
|
||||
if (modifiers & MacEventModifier::CommandKeyMask)
|
||||
event.modifiers.add (ModifierKey::Control);
|
||||
if (modifiers & MacEventModifier::AlternateKeyMask)
|
||||
event.modifiers.add (ModifierKey::Alt);
|
||||
if (modifiers & MacEventModifier::ControlKeyMask)
|
||||
event.modifiers.add (ModifierKey::Super);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN NSString* GetVirtualKeyCodeString (VirtualKey virtualKey)
|
||||
{
|
||||
unichar character = 0;
|
||||
switch (virtualKey)
|
||||
{
|
||||
case VirtualKey::Back: character = NSBackspaceCharacter; break;
|
||||
case VirtualKey::Tab: character = NSTabCharacter; break;
|
||||
case VirtualKey::Clear: character = NSClearLineFunctionKey; break;
|
||||
case VirtualKey::Return: character = NSCarriageReturnCharacter; break;
|
||||
case VirtualKey::Pause: character = NSPauseFunctionKey; break;
|
||||
case VirtualKey::Escape: character = 0x1b; break;
|
||||
case VirtualKey::Space: character = ' '; break;
|
||||
case VirtualKey::Next: character = NSNextFunctionKey; break;
|
||||
case VirtualKey::End: character = NSEndFunctionKey; break;
|
||||
case VirtualKey::Home: character = NSHomeFunctionKey; break;
|
||||
case VirtualKey::Left: character = NSLeftArrowFunctionKey; break;
|
||||
case VirtualKey::Up: character = NSUpArrowFunctionKey; break;
|
||||
case VirtualKey::Right: character = NSRightArrowFunctionKey; break;
|
||||
case VirtualKey::Down: character = NSDownArrowFunctionKey; break;
|
||||
case VirtualKey::PageUp: character = NSPageUpFunctionKey; break;
|
||||
case VirtualKey::PageDown: character = NSPageDownFunctionKey; break;
|
||||
case VirtualKey::Select: character = NSSelectFunctionKey; break;
|
||||
case VirtualKey::Print: character = NSPrintFunctionKey; break;
|
||||
case VirtualKey::Enter: character = NSEnterCharacter; break;
|
||||
case VirtualKey::Snapshot: break;
|
||||
case VirtualKey::Insert: character = NSInsertFunctionKey; break;
|
||||
case VirtualKey::Delete: character = NSDeleteFunctionKey; break;
|
||||
case VirtualKey::Help: character = NSHelpFunctionKey; break;
|
||||
case VirtualKey::NumPad0: break;
|
||||
case VirtualKey::NumPad1: break;
|
||||
case VirtualKey::NumPad2: break;
|
||||
case VirtualKey::NumPad3: break;
|
||||
case VirtualKey::NumPad4: break;
|
||||
case VirtualKey::NumPad5: break;
|
||||
case VirtualKey::NumPad6: break;
|
||||
case VirtualKey::NumPad7: break;
|
||||
case VirtualKey::NumPad8: break;
|
||||
case VirtualKey::NumPad9: break;
|
||||
case VirtualKey::Multiply: break;
|
||||
case VirtualKey::Add: break;
|
||||
case VirtualKey::Separator: break;
|
||||
case VirtualKey::Subtract: break;
|
||||
case VirtualKey::Decimal: break;
|
||||
case VirtualKey::Divide: break;
|
||||
case VirtualKey::F1: character = NSF1FunctionKey; break;
|
||||
case VirtualKey::F2: character = NSF2FunctionKey; break;
|
||||
case VirtualKey::F3: character = NSF3FunctionKey; break;
|
||||
case VirtualKey::F4: character = NSF4FunctionKey; break;
|
||||
case VirtualKey::F5: character = NSF5FunctionKey; break;
|
||||
case VirtualKey::F6: character = NSF6FunctionKey; break;
|
||||
case VirtualKey::F7: character = NSF7FunctionKey; break;
|
||||
case VirtualKey::F8: character = NSF8FunctionKey; break;
|
||||
case VirtualKey::F9: character = NSF9FunctionKey; break;
|
||||
case VirtualKey::F10: character = NSF10FunctionKey; break;
|
||||
case VirtualKey::F11: character = NSF11FunctionKey; break;
|
||||
case VirtualKey::F12: character = NSF12FunctionKey; break;
|
||||
case VirtualKey::NumLock: break;
|
||||
case VirtualKey::Scroll: break;
|
||||
case VirtualKey::Equals: break;
|
||||
case VirtualKey::ShiftModifier: break;
|
||||
case VirtualKey::ControlModifier: break;
|
||||
case VirtualKey::AltModifier: break;
|
||||
case VirtualKey::None: break;
|
||||
}
|
||||
if (character != 0)
|
||||
return [NSString stringWithFormat:@"%C", character];
|
||||
return nil;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
HIDDEN int32_t eventButton (NSEvent* theEvent)
|
||||
{
|
||||
if ([theEvent type] == MacEventType::MouseMoved)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t buttons = 0;
|
||||
switch ([theEvent buttonNumber])
|
||||
{
|
||||
case 0: buttons = ([theEvent modifierFlags] & MacEventModifier::ControlKeyMask) ? kRButton : kLButton; break;
|
||||
case 1: buttons = kRButton; break;
|
||||
case 2: buttons = kMButton; break;
|
||||
case 3: buttons = kButton4; break;
|
||||
case 4: buttons = kButton5; break;
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
HIDDEN void convertPointToGlobal (NSView* view, NSPoint& p)
|
||||
{
|
||||
p = [view convertPoint:p toView:nil];
|
||||
if ([view window] == nil)
|
||||
return;
|
||||
|
||||
NSRect r = {};
|
||||
r.origin = p;
|
||||
r = [[view window] convertRectToScreen:r];
|
||||
p = r.origin;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
HIDDEN NSImage* bitmapToNSImage (CBitmap* bitmap)
|
||||
{
|
||||
if (!bitmap)
|
||||
return nil;
|
||||
|
||||
NSImage* image =
|
||||
[[NSImage alloc] initWithSize:NSMakeSize (bitmap->getWidth (), bitmap->getHeight ())];
|
||||
for (auto& platformBitmap : *bitmap)
|
||||
{
|
||||
if (auto cgBitmap = dynamic_cast<CGBitmap*> (platformBitmap.get ()))
|
||||
{
|
||||
if (auto rep = [[NSBitmapImageRep alloc] initWithCGImage:cgBitmap->getCGImage ()])
|
||||
{
|
||||
[image addRepresentation:rep];
|
||||
[rep release];
|
||||
}
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
#endif // MAC_COCOA
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../iplatformopenglview.h"
|
||||
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
#if MAC_COCOA
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
struct NSOpenGLView;
|
||||
struct NSView;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
class NSViewFrame;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CocoaOpenGLView : public IPlatformOpenGLView
|
||||
{
|
||||
public:
|
||||
CocoaOpenGLView (NSView* parent);
|
||||
~CocoaOpenGLView () noexcept override = default;
|
||||
|
||||
bool init (IOpenGLView* view, PixelFormat* pixelFormat = nullptr) override;
|
||||
void remove () override;
|
||||
|
||||
void invalidRect (const CRect& rect) override;
|
||||
void viewSizeChanged (const CRect& visibleSize) override;
|
||||
|
||||
bool makeContextCurrent () override;
|
||||
bool lockContext () override;
|
||||
bool unlockContext () override;
|
||||
|
||||
void swapBuffers () override;
|
||||
|
||||
void doDraw (const CRect& r);
|
||||
void reshape ();
|
||||
protected:
|
||||
|
||||
NSView* parent;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSOpenGLView* platformView;
|
||||
#pragma clang diagnostic pop
|
||||
IOpenGLView* view;
|
||||
PixelFormat pixelFormat;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
#endif // VSTGUI_OPENGL_SUPPORT
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#define GL_SILENCE_DEPRECATION
|
||||
|
||||
#import "cocoaopenglview.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
|
||||
#import "nsviewframe.h"
|
||||
#import "cocoahelpers.h"
|
||||
#import "objcclassbuilder.h"
|
||||
#import "autoreleasepool.h"
|
||||
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <vector>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@interface NSObject (VSTGUI_NSOpenGLView)
|
||||
- (id)initWithFrame:(NSRect)frameRect
|
||||
pixelFormat:(NSOpenGLPixelFormat*)format
|
||||
callback:(VSTGUI::CocoaOpenGLView*)callback;
|
||||
@end
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct VSTGUI_NSOpenGLView : RuntimeObjCClass<VSTGUI_NSOpenGLView>
|
||||
{
|
||||
static constexpr const auto cocoaOpenGLViewVarName = "cocoaOpenGLView";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Class CreateClass ()
|
||||
{
|
||||
return ObjCClassBuilder ()
|
||||
.init ("VSTGUI_NSOpenGLView", [NSOpenGLView class])
|
||||
.addMethod (@selector (initWithFrame:pixelFormat:callback:), Init)
|
||||
.addMethod (@selector (dealloc), Dealloc)
|
||||
.addMethod (@selector (update), Update_Reshape)
|
||||
.addMethod (@selector (reshape), Update_Reshape)
|
||||
.addMethod (@selector (isFlipped), IsFlipped)
|
||||
.addMethod (@selector (drawRect:), DrawRect)
|
||||
.addMethod (@selector (mouseMoved:), MouseXXX)
|
||||
.addMethod (@selector (rightMouseDown:), MouseXXX)
|
||||
.addMethod (@selector (rightMouseUp:), MouseXXX)
|
||||
.addIvar<CocoaOpenGLView*> (cocoaOpenGLViewVarName)
|
||||
.finalize ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static id Init (id self, SEL _cmd, NSRect frameRect, NSOpenGLPixelFormat* format,
|
||||
CocoaOpenGLView* callback)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
self = obj.callSuper<id (id, SEL, NSRect, NSOpenGLPixelFormat*), id> (
|
||||
@selector (initWithFrame:pixelFormat:), frameRect, format);
|
||||
if (self)
|
||||
{
|
||||
if (auto var = obj.getVariable<CocoaOpenGLView*> (cocoaOpenGLViewVarName))
|
||||
{
|
||||
var->set (callback);
|
||||
callback->remember ();
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void Dealloc (id self, SEL _cmd)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
if (auto var = obj.getVariable<CocoaOpenGLView*> (cocoaOpenGLViewVarName))
|
||||
{
|
||||
if (auto callback = var->get ())
|
||||
callback->forget ();
|
||||
}
|
||||
obj.callSuper<void (id, SEL)> (_cmd);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void Update_Reshape (id self, SEL _cmd)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
if (auto var = obj.getVariable<CocoaOpenGLView*> (cocoaOpenGLViewVarName))
|
||||
{
|
||||
if (auto callback = var->get ())
|
||||
callback->reshape ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static BOOL IsFlipped (id self, SEL _cmd) { return YES; }
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void DrawRect (id self, SEL _cmd, NSRect rect)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
if (auto var = obj.getVariable<CocoaOpenGLView*> (cocoaOpenGLViewVarName))
|
||||
{
|
||||
if (auto callback = var->get ())
|
||||
callback->doDraw (rectFromNSRect (rect));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void MouseXXX (id self, SEL _cmd, NSEvent* theEvent)
|
||||
{
|
||||
if ([self nextResponder])
|
||||
[[self nextResponder] performSelector:_cmd withObject:theEvent];
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_10_12
|
||||
static constexpr auto CocoaOpenGLContextParameterSwapInterval = NSOpenGLContextParameterSwapInterval;
|
||||
static constexpr auto CocoaOpenGLContextParameterSurfaceOpacity = NSOpenGLContextParameterSurfaceOpacity;
|
||||
#else
|
||||
static constexpr auto CocoaOpenGLContextParameterSwapInterval = NSOpenGLCPSwapInterval;
|
||||
static constexpr auto CocoaOpenGLContextParameterSurfaceOpacity = NSOpenGLCPSurfaceOpacity;
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CocoaOpenGLView::CocoaOpenGLView (NSView* parent)
|
||||
: parent (parent)
|
||||
, platformView (nullptr)
|
||||
, view (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaOpenGLView::init (IOpenGLView* inView, PixelFormat* _pixelFormat)
|
||||
{
|
||||
if (platformView)
|
||||
return false;
|
||||
if (parent)
|
||||
{
|
||||
NSRect r = NSMakeRect (0, 0, 100, 100);
|
||||
|
||||
std::vector<NSOpenGLPixelFormatAttribute> formatAttributes;
|
||||
if (_pixelFormat)
|
||||
{
|
||||
pixelFormat = *_pixelFormat;
|
||||
formatAttributes.emplace_back (NSOpenGLPFADepthSize);
|
||||
formatAttributes.emplace_back (pixelFormat.depthBufferSize);
|
||||
formatAttributes.emplace_back (NSOpenGLPFAStencilSize);
|
||||
formatAttributes.emplace_back (pixelFormat.stencilBufferSize);
|
||||
formatAttributes.emplace_back (NSOpenGLPFANoRecovery);
|
||||
formatAttributes.emplace_back (NSOpenGLPFAAccelerated);
|
||||
if (pixelFormat.flags & PixelFormat::kDoubleBuffered)
|
||||
{
|
||||
formatAttributes.emplace_back (NSOpenGLPFADoubleBuffer);
|
||||
formatAttributes.emplace_back (NSOpenGLPFABackingStore);
|
||||
}
|
||||
if (pixelFormat.flags & PixelFormat::kMultiSample)
|
||||
{
|
||||
formatAttributes.emplace_back (NSOpenGLPFAMultisample);
|
||||
formatAttributes.emplace_back (true);
|
||||
formatAttributes.emplace_back (NSOpenGLPFASampleBuffers);
|
||||
formatAttributes.emplace_back (2);
|
||||
formatAttributes.emplace_back (NSOpenGLPFASamples);
|
||||
formatAttributes.emplace_back (pixelFormat.samples);
|
||||
}
|
||||
if (pixelFormat.flags & PixelFormat::kModernOpenGL)
|
||||
{
|
||||
formatAttributes.emplace_back (NSOpenGLPFAOpenGLProfile);
|
||||
formatAttributes.emplace_back (NSOpenGLProfileVersion3_2Core);
|
||||
}
|
||||
else
|
||||
{
|
||||
formatAttributes.emplace_back (NSOpenGLPFAOpenGLProfile);
|
||||
formatAttributes.emplace_back (NSOpenGLProfileVersionLegacy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
formatAttributes.emplace_back (NSOpenGLPFANoRecovery);
|
||||
formatAttributes.emplace_back (NSOpenGLPFAAccelerated);
|
||||
formatAttributes.emplace_back (NSOpenGLPFADoubleBuffer);
|
||||
formatAttributes.emplace_back (NSOpenGLPFABackingStore);
|
||||
formatAttributes.emplace_back (NSOpenGLPFADepthSize);
|
||||
formatAttributes.emplace_back (32);
|
||||
formatAttributes.emplace_back (NSOpenGLPFAOpenGLProfile);
|
||||
formatAttributes.emplace_back (NSOpenGLProfileVersionLegacy);
|
||||
}
|
||||
formatAttributes.emplace_back (0);
|
||||
NSOpenGLPixelFormat* nsPixelFormat = [[[NSOpenGLPixelFormat alloc]
|
||||
initWithAttributes:&formatAttributes.front ()] autorelease];
|
||||
platformView = [VSTGUI_NSOpenGLView::alloc () initWithFrame:r
|
||||
pixelFormat:nsPixelFormat
|
||||
callback:this];
|
||||
if (platformView)
|
||||
{
|
||||
NSOpenGLContext* context = [platformView openGLContext];
|
||||
GLint value = 1;
|
||||
[context setValues:&value forParameter:CocoaOpenGLContextParameterSwapInterval];
|
||||
value = 0;
|
||||
[context setValues:&value forParameter:CocoaOpenGLContextParameterSurfaceOpacity];
|
||||
|
||||
#if DEBUG
|
||||
if (pixelFormat.flags & PixelFormat::kModernOpenGL)
|
||||
{
|
||||
CGLEnable (static_cast<CGLContextObj> ([context CGLContextObj]),
|
||||
kCGLCECrashOnRemovedFunctions);
|
||||
}
|
||||
#endif
|
||||
view = inView;
|
||||
platformView.wantsBestResolutionOpenGLSurface = YES;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaOpenGLView::remove ()
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
AutoreleasePool ap;
|
||||
[platformView removeFromSuperview];
|
||||
[platformView release];
|
||||
platformView = nil;
|
||||
view = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaOpenGLView::invalidRect (const CRect& rect)
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
[platformView setNeedsDisplayInRect:nsRectFromCRect (rect)];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaOpenGLView::makeContextCurrent ()
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
NSOpenGLContext* context = [platformView openGLContext];
|
||||
if (context)
|
||||
{
|
||||
[context makeCurrentContext];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaOpenGLView::lockContext ()
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
NSOpenGLContext* context = [platformView openGLContext];
|
||||
if (context)
|
||||
{
|
||||
CGLLockContext ((CGLContextObj)[context CGLContextObj]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaOpenGLView::unlockContext ()
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
NSOpenGLContext* context = [platformView openGLContext];
|
||||
if (context)
|
||||
{
|
||||
CGLUnlockContext ((CGLContextObj)[context CGLContextObj]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaOpenGLView::swapBuffers ()
|
||||
{
|
||||
if (platformView && pixelFormat.flags & PixelFormat::kDoubleBuffered)
|
||||
{
|
||||
NSOpenGLContext* context = [platformView openGLContext];
|
||||
if (context)
|
||||
{
|
||||
[context flushBuffer];
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaOpenGLView::viewSizeChanged (const CRect& visibleSize)
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
lockContext ();
|
||||
NSRect r = nsRectFromCRect (visibleSize);
|
||||
[platformView setFrame:r];
|
||||
if ([platformView superview] == nil)
|
||||
{
|
||||
[parent addSubview:platformView];
|
||||
}
|
||||
unlockContext ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaOpenGLView::doDraw (const CRect& rect)
|
||||
{
|
||||
if (view)
|
||||
{
|
||||
lockContext ();
|
||||
view->drawOpenGL (rect);
|
||||
unlockContext ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaOpenGLView::reshape ()
|
||||
{
|
||||
lockContext ();
|
||||
NSOpenGLContext* context = [platformView openGLContext];
|
||||
if (context)
|
||||
[context update];
|
||||
unlockContext ();
|
||||
view->reshape ();
|
||||
[platformView setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // VSTGUI_OPENGL_SUPPORT
|
||||
#endif // MAC_COCOA
|
||||
@@ -0,0 +1,45 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../iplatformtextedit.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class NSView;
|
||||
@class NSTextField;
|
||||
#else
|
||||
struct NSView;
|
||||
struct NSTextField;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CocoaTextEdit : public IPlatformTextEdit
|
||||
{
|
||||
public:
|
||||
CocoaTextEdit (NSView* parent, IPlatformTextEditCallback* textEdit);
|
||||
~CocoaTextEdit () noexcept override;
|
||||
|
||||
UTF8String getText () override;
|
||||
bool setText (const UTF8String& text) override;
|
||||
bool updateSize () override;
|
||||
bool drawsPlaceholder () const override { return true; }
|
||||
|
||||
NSTextField* getPlatformControl () const { return platformControl; }
|
||||
NSView* getParent () const { return parent; }
|
||||
IPlatformTextEditCallback* getTextEdit () const { return textEdit; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
NSTextField* platformControl;
|
||||
NSView* parent;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "cocoatextedit.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#import "cocoahelpers.h"
|
||||
#import "objcclassbuilder.h"
|
||||
#import "autoreleasepool.h"
|
||||
#import "../cfontmac.h"
|
||||
#import "../macstring.h"
|
||||
#import "../../../events.h"
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
@interface NSObject (VSTGUI_NSTextField_Private)
|
||||
-(id)initWithTextEdit:(id)textEit;
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
namespace MacTextAlignment {
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_10_12
|
||||
static constexpr auto Right = ::NSTextAlignmentRight;
|
||||
static constexpr auto Center = ::NSTextAlignmentCenter;
|
||||
#else
|
||||
static constexpr auto Right = ::NSRightTextAlignment;
|
||||
static constexpr auto Center = ::NSCenterTextAlignment;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
} // MacTextAlignment
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
template<bool SecureT>
|
||||
struct VSTGUI_NSTextFieldT : RuntimeObjCClass<VSTGUI_NSTextFieldT<SecureT>>
|
||||
{
|
||||
using Base = RuntimeObjCClass<VSTGUI_NSTextFieldT<SecureT>>;
|
||||
|
||||
static constexpr const auto textEditVarName = "_textEdit";
|
||||
|
||||
static Class CreateClass ()
|
||||
{
|
||||
if constexpr (SecureT)
|
||||
{
|
||||
return ObjCClassBuilder ()
|
||||
.init ("VSTGUI_NSSecureTextField", [NSSecureTextField class])
|
||||
.addMethod (@selector (initWithTextEdit:), Init)
|
||||
.addMethod (@selector (syncSize), SyncSize)
|
||||
.addMethod (@selector (removeFromSuperview), RemoveFromSuperview)
|
||||
.addMethod (@selector (control:textView:doCommandBySelector:), DoCommandBySelector)
|
||||
.addMethod (@selector (textDidChange:), TextDidChange)
|
||||
.addIvar<IPlatformTextEditCallback*> (textEditVarName)
|
||||
.finalize ();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ObjCClassBuilder ()
|
||||
.init ("VSTGUI_NSTextField", [NSTextField class])
|
||||
.addMethod (@selector (initWithTextEdit:), Init)
|
||||
.addMethod (@selector (syncSize), SyncSize)
|
||||
.addMethod (@selector (removeFromSuperview), RemoveFromSuperview)
|
||||
.addMethod (@selector (control:textView:doCommandBySelector:), DoCommandBySelector)
|
||||
.addMethod (@selector (textDidChange:), TextDidChange)
|
||||
.addIvar<CocoaTextEdit*> (textEditVarName)
|
||||
.finalize ();
|
||||
}
|
||||
}
|
||||
|
||||
static id Init (id self, SEL _cmd, void* textEdit)
|
||||
{
|
||||
if (self)
|
||||
{
|
||||
CocoaTextEdit* te = (CocoaTextEdit*)textEdit;
|
||||
IPlatformTextEditCallback* tec = te->getTextEdit ();
|
||||
NSView* frameView = te->getParent ();
|
||||
NSRect editFrameRect = nsRectFromCRect (tec->platformGetSize ());
|
||||
NSView* containerView = [[NSView alloc] initWithFrame:editFrameRect];
|
||||
[containerView setAutoresizesSubviews:YES];
|
||||
|
||||
if ([frameView wantsLayer])
|
||||
{
|
||||
containerView.wantsLayer = YES;
|
||||
double maxZPosition = -1.;
|
||||
for (CALayer* layer in frameView.layer.sublayers)
|
||||
{
|
||||
double zPosition = layer.zPosition;
|
||||
if (zPosition > maxZPosition)
|
||||
maxZPosition = zPosition;
|
||||
}
|
||||
[containerView layer].zPosition = static_cast<CGFloat> (maxZPosition + 1);
|
||||
}
|
||||
|
||||
CPoint textInset = tec->platformGetTextInset ();
|
||||
|
||||
editFrameRect.origin.x = static_cast<CGFloat> (textInset.x / 2. - 1.);
|
||||
editFrameRect.origin.y = static_cast<CGFloat> (textInset.y / 2.);
|
||||
editFrameRect.size.width -= textInset.x / 2.;
|
||||
editFrameRect.size.height -= textInset.y / 2. - 1.;
|
||||
self = Base::makeInstance (self).template callSuper<id (id, SEL, NSRect), id> (
|
||||
@selector (initWithFrame:), editFrameRect);
|
||||
if (!self)
|
||||
{
|
||||
[containerView release];
|
||||
return nil;
|
||||
}
|
||||
auto obj = Base::makeInstance (self);
|
||||
if (auto var = obj.template getVariable<IPlatformTextEditCallback*> (textEditVarName))
|
||||
var->set (tec);
|
||||
|
||||
CoreTextFont* ctf = tec->platformGetFont ()->getPlatformFont ().cast<CoreTextFont> ();
|
||||
if (ctf)
|
||||
{
|
||||
CTFontRef fontRef = ctf->getFontRef ();
|
||||
if (fontRef)
|
||||
{
|
||||
CTFontDescriptorRef fontDesc = CTFontCopyFontDescriptor (fontRef);
|
||||
|
||||
[self setFont:[NSFont fontWithDescriptor:(NSFontDescriptor*)fontDesc
|
||||
size:ctf->getSize ()]];
|
||||
CFRelease (fontDesc);
|
||||
}
|
||||
}
|
||||
|
||||
NSString* text = fromUTF8String<NSString*> (tec->platformGetText ());
|
||||
NSString* placeholder = fromUTF8String<NSString*> (tec->platformGetPlaceholderText ());
|
||||
|
||||
[self setTextColor:nsColorFromCColor (tec->platformGetFontColor ())];
|
||||
[self setBordered:NO];
|
||||
[self setAllowsEditingTextAttributes:NO];
|
||||
[self setImportsGraphics:NO];
|
||||
[self setStringValue:text];
|
||||
[self setFocusRingType:NSFocusRingTypeNone];
|
||||
[self sizeToFit];
|
||||
[(NSView*)self setAutoresizingMask:NSViewMinYMargin];
|
||||
if ([self frame].size.height < editFrameRect.size.height)
|
||||
{
|
||||
CGFloat offset = editFrameRect.size.height - [self frame].size.height;
|
||||
editFrameRect.origin.y = static_cast<CGFloat> (offset / 2.);
|
||||
editFrameRect.size.height = [self frame].size.height;
|
||||
}
|
||||
else
|
||||
editFrameRect.size.height = [self frame].size.height;
|
||||
[self setFrame:editFrameRect];
|
||||
|
||||
[containerView addSubview:self];
|
||||
[self performSelector:@selector (syncSize)];
|
||||
[frameView addSubview:containerView];
|
||||
|
||||
NSTextFieldCell* cell = [self cell];
|
||||
[cell setDrawsBackground:NO];
|
||||
[cell setLineBreakMode:NSLineBreakByClipping];
|
||||
[cell setScrollable:YES];
|
||||
if (tec->platformGetHoriTxtAlign () == kCenterText)
|
||||
[cell setAlignment:MacTextAlignment::Center];
|
||||
else if (tec->platformGetHoriTxtAlign () == kRightText)
|
||||
[cell setAlignment:MacTextAlignment::Right];
|
||||
if (placeholder.length > 0)
|
||||
{
|
||||
CColor color = tec->platformGetFontColor ();
|
||||
color.alpha /= 2;
|
||||
NSMutableParagraphStyle* paragraphStyle =
|
||||
[[[NSMutableParagraphStyle alloc] init] autorelease];
|
||||
if (tec->platformGetHoriTxtAlign () == kCenterText)
|
||||
paragraphStyle.alignment = MacTextAlignment::Center;
|
||||
else if (tec->platformGetHoriTxtAlign () == kRightText)
|
||||
paragraphStyle.alignment = MacTextAlignment::Right;
|
||||
NSDictionary* attrDict = [NSDictionary
|
||||
dictionaryWithObjectsAndKeys:[self font], NSFontAttributeName,
|
||||
nsColorFromCColor (color),
|
||||
NSForegroundColorAttributeName, paragraphStyle,
|
||||
NSParagraphStyleAttributeName, nil];
|
||||
NSAttributedString* as =
|
||||
[[[NSAttributedString alloc] initWithString:placeholder
|
||||
attributes:attrDict] autorelease];
|
||||
[cell setPlaceholderAttributedString:as];
|
||||
}
|
||||
|
||||
[self setDelegate:self];
|
||||
[self setNextKeyView:frameView];
|
||||
|
||||
if (auto tv = static_cast<NSTextView*> ([[self window] fieldEditor:YES forObject:self]))
|
||||
tv.insertionPointColor = nsColorFromCColor (tec->platformGetFontColor ());
|
||||
|
||||
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t) (1 * NSEC_PER_MSEC)),
|
||||
dispatch_get_main_queue (), ^{
|
||||
[[self window] makeFirstResponder:self];
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void SyncSize (id self, SEL _cmd)
|
||||
{
|
||||
if (auto te = Base::makeInstance (self).template getVariable<IPlatformTextEditCallback*> (
|
||||
textEditVarName))
|
||||
{
|
||||
auto textEdit = te->get ();
|
||||
if (!textEdit)
|
||||
return;
|
||||
NSView* containerView = [self superview];
|
||||
CRect rect (textEdit->platformGetVisibleSize ());
|
||||
rect.makeIntegral ();
|
||||
|
||||
[containerView setFrame:nsRectFromCRect (rect)];
|
||||
|
||||
rect.extend (15, -15);
|
||||
[[containerView superview] setNeedsDisplayInRect:nsRectFromCRect (rect)];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void RemoveFromSuperview (id self, SEL _cmd)
|
||||
{
|
||||
auto obj = Base::makeInstance (self);
|
||||
if (auto var = obj.template getVariable<IPlatformTextEditCallback*> (textEditVarName))
|
||||
var->set (nullptr);
|
||||
NSView* containerView = [self superview];
|
||||
if (containerView)
|
||||
{
|
||||
[[containerView window] makeFirstResponder:[containerView superview]];
|
||||
[containerView removeFromSuperview];
|
||||
// [super removeFromSuperview];
|
||||
obj.template callSuper<void (id, SEL)> (_cmd);
|
||||
[containerView release];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void TextDidChange (id self, SEL _cmd, NSNotification* notification)
|
||||
{
|
||||
auto obj = Base::makeInstance (self);
|
||||
if (auto var = obj.template getVariable<IPlatformTextEditCallback*> (textEditVarName))
|
||||
{
|
||||
if (auto te = var->get ())
|
||||
te->platformTextDidChange ();
|
||||
}
|
||||
obj.template callSuper<void (id, SEL, NSNotification*)> (_cmd, notification);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static BOOL DoCommandBySelector (id self, SEL _cmd, NSControl* control, NSTextView* textView,
|
||||
SEL commandSelector)
|
||||
{
|
||||
auto var = Base::makeInstance (self).template getVariable<IPlatformTextEditCallback*> (
|
||||
textEditVarName);
|
||||
if (!var)
|
||||
return NO;
|
||||
IPlatformTextEditCallback* tec = var->get ();
|
||||
if (!tec)
|
||||
return NO;
|
||||
if (commandSelector == @selector (insertNewline:))
|
||||
{
|
||||
KeyboardEvent event;
|
||||
event.type = EventType::KeyDown;
|
||||
event.virt = VirtualKey::Return;
|
||||
tec->platformOnKeyboardEvent (event);
|
||||
if (event.consumed)
|
||||
return YES;
|
||||
}
|
||||
else if (commandSelector == @selector (insertTab:))
|
||||
{
|
||||
KeyboardEvent event;
|
||||
event.type = EventType::KeyDown;
|
||||
event.virt = VirtualKey::Tab;
|
||||
tec->platformOnKeyboardEvent (event);
|
||||
if (event.consumed)
|
||||
return YES;
|
||||
}
|
||||
else if (commandSelector == @selector (insertBacktab:))
|
||||
{
|
||||
KeyboardEvent event;
|
||||
event.type = EventType::KeyDown;
|
||||
event.virt = VirtualKey::Tab;
|
||||
event.modifiers.add (ModifierKey::Shift);
|
||||
tec->platformOnKeyboardEvent (event);
|
||||
if (event.consumed)
|
||||
return YES;
|
||||
}
|
||||
else if (commandSelector == @selector (cancelOperation:))
|
||||
{
|
||||
KeyboardEvent event;
|
||||
event.type = EventType::KeyDown;
|
||||
event.virt = VirtualKey::Escape;
|
||||
tec->platformOnKeyboardEvent (event);
|
||||
if (event.consumed)
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
};
|
||||
|
||||
using VSTGUI_NSTextField = VSTGUI_NSTextFieldT<false>;
|
||||
using VSTGUI_NSTextField_Secure = VSTGUI_NSTextFieldT<true>;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CocoaTextEdit::CocoaTextEdit (NSView* parent, IPlatformTextEditCallback* textEdit)
|
||||
: IPlatformTextEdit (textEdit)
|
||||
, platformControl (nullptr)
|
||||
, parent (parent)
|
||||
{
|
||||
if (textEdit->platformIsSecureTextEdit ())
|
||||
platformControl = [VSTGUI_NSTextField_Secure::alloc () initWithTextEdit:(id)this];
|
||||
else
|
||||
platformControl = [VSTGUI_NSTextField::alloc () initWithTextEdit:(id)this];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CocoaTextEdit::~CocoaTextEdit () noexcept
|
||||
{
|
||||
[platformControl performSelector:@selector(removeFromSuperview)];
|
||||
[platformControl performSelector:@selector(autorelease)];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
UTF8String CocoaTextEdit::getText ()
|
||||
{
|
||||
return [[[platformControl stringValue] decomposedStringWithCanonicalMapping] UTF8String];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaTextEdit::updateSize ()
|
||||
{
|
||||
[platformControl performSelector:@selector(syncSize)];
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaTextEdit::setText (const UTF8String& text)
|
||||
{
|
||||
[platformControl setStringValue:fromUTF8String<NSString*> (text)];
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../vstguifwd.h"
|
||||
|
||||
#if MAC_COCOA && !TARGET_OS_IPHONE
|
||||
|
||||
#include "../../../dragging.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
struct NSView;
|
||||
struct NSDraggingSession;
|
||||
struct NSImage;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct NSViewDraggingSession : public IDraggingSession, public NonAtomicReferenceCounted
|
||||
{
|
||||
static SharedPointer<NSViewDraggingSession> create (
|
||||
NSView* view, const DragDescription& desc, const SharedPointer<IDragCallback>& callback);
|
||||
|
||||
NSViewDraggingSession (NSDraggingSession* session, const DragDescription& desc,
|
||||
const SharedPointer<IDragCallback>& callback);
|
||||
|
||||
bool setBitmap (const SharedPointer<CBitmap>& bitmap, CPoint offset) override;
|
||||
|
||||
void dragWillBegin (CPoint pos);
|
||||
void dragMoved (CPoint pos);
|
||||
void dragEnded (CPoint pos, DragOperation result);
|
||||
|
||||
private:
|
||||
static NSImage* nsImageForDragOperation (CBitmap* bitmap);
|
||||
|
||||
NSDraggingSession* session;
|
||||
DragDescription desc;
|
||||
SharedPointer<IDragCallback> callback;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
|
||||
#endif
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "nsviewdraggingsession.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#import "../cgbitmap.h"
|
||||
#import "../macclipboard.h"
|
||||
#import "cocoahelpers.h"
|
||||
#import "objcclassbuilder.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@interface NSObject (VSTGUI_BinaryDataType_Private)
|
||||
- (id)initWithData:(const void*)data andSize:(size_t)size;
|
||||
@end
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_13
|
||||
#define MAC_OS_X_VERSION_10_13 101300
|
||||
#endif
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
|
||||
typedef NSString *NSPasteboardType;
|
||||
typedef NSString *NSPasteboardReadingOptionKey;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct BinaryDataType : RuntimeObjCClass<BinaryDataType>
|
||||
{
|
||||
static constexpr const auto dataVarName = "_data";
|
||||
|
||||
static NSString* getCocoaPasteboardTypeString ()
|
||||
{
|
||||
return [NSString stringWithCString:VSTGUI::MacClipboard::getPasteboardBinaryType ()
|
||||
encoding:NSASCIIStringEncoding];
|
||||
}
|
||||
|
||||
static id Init (id self, SEL, const void* buffer, size_t bufferSize)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
self = obj.callSuper<id (id, SEL), id> (@selector (init));
|
||||
if (self)
|
||||
{
|
||||
auto data = [[NSData alloc] initWithBytes:buffer length:bufferSize];
|
||||
if (auto var = obj.getVariable<NSData*> (dataVarName))
|
||||
var->set (data);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static void Dealloc (id self, SEL _cmd)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
if (auto var = obj.getVariable<NSData*> (dataVarName); var.has_value () && var->get ())
|
||||
{
|
||||
[var->get () release];
|
||||
var->set (nullptr);
|
||||
}
|
||||
obj.callSuper<void (id, SEL)> (_cmd);
|
||||
}
|
||||
|
||||
static NSArray<NSPasteboardType>* WritableTypesForPasteboard (id, SEL, NSPasteboard*)
|
||||
{
|
||||
return @[getCocoaPasteboardTypeString ()];
|
||||
}
|
||||
|
||||
static id PasteboardPropertyListForType (id self, SEL, NSPasteboardType)
|
||||
{
|
||||
auto obj = makeInstance (self);
|
||||
if (auto var = obj.getVariable<NSData*> (dataVarName); var.has_value () && var->get ())
|
||||
return var->get ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Class CreateClass ()
|
||||
{
|
||||
return ObjCClassBuilder ()
|
||||
.init ("VSTGUI_BinaryDataType", [NSObject class])
|
||||
.addProtocol ("NSPasteboardWriting")
|
||||
.addMethod (@selector (initWithData:andSize:), Init)
|
||||
.addMethod (@selector (dealloc), Dealloc)
|
||||
.addMethod (@selector (writableTypesForPasteboard:), WritableTypesForPasteboard)
|
||||
.addMethod (@selector (pasteboardPropertyListForType:), PasteboardPropertyListForType)
|
||||
.addIvar<NSData*> (dataVarName)
|
||||
.finalize ();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
SharedPointer<NSViewDraggingSession> NSViewDraggingSession::create (
|
||||
NSView* nsView, const DragDescription& desc, const SharedPointer<IDragCallback>& callback)
|
||||
{
|
||||
NSEvent* event = [NSApp currentEvent];
|
||||
if (event == nullptr || !([event type] == MacEventType::LeftMouseDown ||
|
||||
[event type] == MacEventType::LeftMouseDragged))
|
||||
return nullptr;
|
||||
|
||||
NSPoint bitmapOffset = {static_cast<CGFloat> (desc.bitmapOffset.x),
|
||||
static_cast<CGFloat> (desc.bitmapOffset.y)};
|
||||
|
||||
auto bitmap = desc.bitmap;
|
||||
NSPoint nsLocation = [event locationInWindow];
|
||||
NSImage* nsImage = nil;
|
||||
if ((nsImage = nsImageForDragOperation (bitmap)))
|
||||
{
|
||||
nsLocation = [nsView convertPoint:nsLocation fromView:nil];
|
||||
bitmapOffset.x += nsLocation.x;
|
||||
bitmapOffset.y += nsLocation.y + [nsImage size].height;
|
||||
}
|
||||
|
||||
NSMutableArray* dragItems = [[NSMutableArray new] autorelease];
|
||||
for (uint32_t index = 0, count = desc.data->getCount (); index < count; ++index)
|
||||
{
|
||||
const void* buffer = nullptr;
|
||||
IDataPackage::Type type {};
|
||||
auto size = desc.data->getData (index, buffer, type);
|
||||
if (size == 0)
|
||||
continue;
|
||||
NSDraggingItem* item = nil;
|
||||
switch (type)
|
||||
{
|
||||
case IDataPackage::kFilePath:
|
||||
{
|
||||
if (auto fileUrl = [NSURL
|
||||
fileURLWithPath:[NSString
|
||||
stringWithUTF8String:reinterpret_cast<const char*> (
|
||||
buffer)]])
|
||||
{
|
||||
item = [[[NSDraggingItem alloc] initWithPasteboardWriter:fileUrl] autorelease];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IDataPackage::kText:
|
||||
{
|
||||
if (auto string =
|
||||
[NSString stringWithUTF8String:reinterpret_cast<const char*> (buffer)])
|
||||
{
|
||||
item = [[[NSDraggingItem alloc] initWithPasteboardWriter:string] autorelease];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IDataPackage::kBinary:
|
||||
{
|
||||
if (id data = [[BinaryDataType::alloc () initWithData:buffer
|
||||
andSize:size] autorelease])
|
||||
item = [[[NSDraggingItem alloc] initWithPasteboardWriter:data] autorelease];
|
||||
break;
|
||||
}
|
||||
case IDataPackage::kError: { continue;
|
||||
}
|
||||
}
|
||||
if (item)
|
||||
{
|
||||
if (nsImage && [dragItems count] == 0)
|
||||
{
|
||||
NSRect r;
|
||||
r.size.width = bitmap->getWidth ();
|
||||
r.size.height = bitmap->getHeight ();
|
||||
r.origin = bitmapOffset;
|
||||
r.origin.y -= r.size.height;
|
||||
[item setDraggingFrame:r contents:nsImage];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRect r;
|
||||
r.origin = bitmapOffset;
|
||||
r.size = NSMakeSize (1, 1);
|
||||
item.draggingFrame = r;
|
||||
}
|
||||
[dragItems addObject:item];
|
||||
}
|
||||
}
|
||||
NSView<NSDraggingSource>* draggingSource = (NSView<NSDraggingSource>*)nsView;
|
||||
if (auto session =
|
||||
[nsView beginDraggingSessionWithItems:dragItems event:event source:draggingSource])
|
||||
{
|
||||
session.animatesToStartingPositionsOnCancelOrFail = YES;
|
||||
return makeOwned<NSViewDraggingSession> (session, desc, callback);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
NSViewDraggingSession::NSViewDraggingSession (NSDraggingSession* session,
|
||||
const DragDescription& desc,
|
||||
const SharedPointer<IDragCallback>& callback)
|
||||
: session (session), desc (desc), callback (callback)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool NSViewDraggingSession::setBitmap (const SharedPointer<CBitmap>& bitmap, CPoint offset)
|
||||
{
|
||||
[session enumerateDraggingItemsWithOptions:0
|
||||
forView:nil
|
||||
classes:[NSArray arrayWithObject:[NSPasteboardItem class]]
|
||||
searchOptions:[NSDictionary<NSPasteboardReadingOptionKey, id> new]
|
||||
usingBlock:[&] (NSDraggingItem* _Nonnull draggingItem,
|
||||
NSInteger idx, BOOL* _Nonnull stop) {
|
||||
if (idx != 0)
|
||||
return;
|
||||
if (auto nsImage = nsImageForDragOperation (bitmap))
|
||||
{
|
||||
NSRect r;
|
||||
r.origin = nsPointFromCPoint (offset);
|
||||
r.origin.y -= nsImage.size.height;
|
||||
r.size = nsImage.size;
|
||||
[draggingItem setDraggingFrame:r contents:nsImage];
|
||||
}
|
||||
else
|
||||
{
|
||||
[draggingItem
|
||||
setDraggingFrame:draggingItem.draggingFrame
|
||||
contents:nil];
|
||||
}
|
||||
*stop = YES;
|
||||
}];
|
||||
desc.bitmap = bitmap;
|
||||
desc.bitmapOffset = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void NSViewDraggingSession::dragWillBegin (CPoint pos)
|
||||
{
|
||||
if (!callback)
|
||||
return;
|
||||
callback->dragWillBegin (this, pos);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void NSViewDraggingSession::dragMoved (CPoint pos)
|
||||
{
|
||||
if (!callback)
|
||||
return;
|
||||
callback->dragMoved (this, pos);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void NSViewDraggingSession::dragEnded (CPoint pos, DragOperation result)
|
||||
{
|
||||
if (!callback)
|
||||
return;
|
||||
callback->dragEnded (this, pos, result);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
NSImage* NSViewDraggingSession::nsImageForDragOperation (CBitmap* bitmap)
|
||||
{
|
||||
return [bitmapToNSImage (bitmap) autorelease];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../vstguifwd.h"
|
||||
|
||||
#if MAC_COCOA && !TARGET_OS_IPHONE
|
||||
|
||||
#include "../../platform_macos.h"
|
||||
#include "../../iplatformtextinputclient.h"
|
||||
#include "../../../cinvalidrectlist.h"
|
||||
#include "../../../idatapackage.h"
|
||||
#import "../coregraphicsdevicecontext.h"
|
||||
#include "nsviewdraggingsession.h"
|
||||
#include <list>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
struct NSView;
|
||||
struct NSRect;
|
||||
struct NSDraggingSession;
|
||||
struct NSEvent;
|
||||
struct CALayer;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
class CocoaTooltipWindow;
|
||||
struct NSViewDraggingSession;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IPlatformFrameTouchBarExtension
|
||||
{
|
||||
public:
|
||||
NSViewFrame (IPlatformFrameCallback* frame, const CRect& size, NSView* parent, IPlatformFrameConfig* config);
|
||||
~NSViewFrame () noexcept override;
|
||||
|
||||
NSView* getNSView () const override { return nsView; }
|
||||
void setTextInputClient (ICocoaTextInputClient* client) override;
|
||||
ICocoaTextInputClient* getTextInputClient () const { return textInputClient; }
|
||||
|
||||
CALayer* getCALayer () const { return caLayer; }
|
||||
IPlatformFrameCallback* getFrame () const { return frame; }
|
||||
void* makeTouchBar () const;
|
||||
NSViewDraggingSession* getDraggingSession () const { return draggingSession; }
|
||||
void clearDraggingSession () { draggingSession = nullptr; }
|
||||
void setNeedsDisplayInRect (NSRect r);
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
void setLastDragOperationResult (DragResult result) { lastDragOperationResult = result; }
|
||||
#endif
|
||||
|
||||
void setDragDataPackage (SharedPointer<IDataPackage>&& package) { dragDataPackage = std::move (package); }
|
||||
const SharedPointer<IDataPackage>& getDragDataPackage () const { return dragDataPackage; }
|
||||
|
||||
void initTrackingArea ();
|
||||
void scaleFactorChanged (double newScaleFactor);
|
||||
void cursorUpdate ();
|
||||
void drawLayer (CALayer* layer, CGContextRef ctx);
|
||||
void drawRect (NSRect* rect);
|
||||
bool onMouseDown (NSEvent* evt);
|
||||
bool onMouseUp (NSEvent* evt);
|
||||
bool onMouseMoved (NSEvent* evt);
|
||||
|
||||
// IPlatformFrame
|
||||
bool getGlobalPosition (CPoint& pos) const override;
|
||||
bool setSize (const CRect& newSize) override;
|
||||
bool getSize (CRect& size) const override;
|
||||
bool getCurrentMousePosition (CPoint& mousePosition) const override;
|
||||
bool getCurrentMouseButtons (CButtonState& buttons) const override;
|
||||
bool getCurrentModifiers (Modifiers& modifiers) const override;
|
||||
bool setMouseCursor (CCursorType type) override;
|
||||
bool invalidRect (const CRect& rect) override;
|
||||
bool scrollRect (const CRect& src, const CPoint& distance) override;
|
||||
bool showTooltip (const CRect& rect, const char* utf8Text) override;
|
||||
bool hideTooltip () override;
|
||||
void* getPlatformRepresentation () const override { return nsView; }
|
||||
SharedPointer<IPlatformTextEdit> createPlatformTextEdit (IPlatformTextEditCallback* textEdit) override;
|
||||
SharedPointer<IPlatformOptionMenu> createPlatformOptionMenu () override;
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
SharedPointer<IPlatformOpenGLView> createPlatformOpenGLView () override;
|
||||
#endif
|
||||
SharedPointer<IPlatformViewLayer> createPlatformViewLayer (IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer = nullptr) override;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) override;
|
||||
#endif
|
||||
bool doDrag (const DragDescription& dragDescription, const SharedPointer<IDragCallback>& callback) override;
|
||||
|
||||
PlatformType getPlatformType () const override { return PlatformType::kNSView; }
|
||||
void onFrameClosed () override {}
|
||||
Optional<UTF8String> convertCurrentKeyEventToText () override;
|
||||
bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) override;
|
||||
|
||||
// IPlatformFrameTouchBarExtension
|
||||
void setTouchBarCreator (const SharedPointer<ITouchBarCreator>& creator) override;
|
||||
void recreateTouchBar () override;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
void draw (CGContextRef context, CRect updateRect, double scaleFactor);
|
||||
void addDebugRedrawRect (CRect r, bool isClipBoundingBox = false);
|
||||
|
||||
NSView* nsView {nullptr};
|
||||
CALayer* caLayer {nullptr};
|
||||
CocoaTooltipWindow* tooltipWindow {nullptr};
|
||||
ICocoaTextInputClient* textInputClient {nullptr};
|
||||
SharedPointer<IDataPackage> dragDataPackage;
|
||||
SharedPointer<ITouchBarCreator> touchBarCreator;
|
||||
SharedPointer<NSViewDraggingSession> draggingSession;
|
||||
std::unique_ptr<GenericOptionMenuTheme> genericOptionMenuTheme;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
DragResult lastDragOperationResult;
|
||||
#endif
|
||||
bool trackingAreaInitialized;
|
||||
bool inDraw;
|
||||
bool useInvalidRects {false};
|
||||
|
||||
CCursorType cursor;
|
||||
CButtonState mouseDownButtonState {};
|
||||
CInvalidRectList invalidRectList;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
File diff suppressed because it is too large
Load Diff
+22
@@ -0,0 +1,22 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../iplatformoptionmenu.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class NSViewOptionMenu : public IPlatformOptionMenu
|
||||
{
|
||||
public:
|
||||
void popup (COptionMenu* optionMenu, const Callback& callback) override;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "nsviewoptionmenu.h"
|
||||
|
||||
#if MAC_COCOA
|
||||
|
||||
#import "../../../cbitmap.h"
|
||||
#import "../../../cframe.h"
|
||||
#import "../../../controls/coptionmenu.h"
|
||||
#import "../cgbitmap.h"
|
||||
#import "../macstring.h"
|
||||
#import "cocoahelpers.h"
|
||||
#import "nsviewframe.h"
|
||||
#import "objcclassbuilder.h"
|
||||
|
||||
@interface NSObject (VSTGUI_NSMenu_Private)
|
||||
- (id)initWithOptionMenu:(id)menu;
|
||||
@end
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_14
|
||||
#define MAC_OS_X_VERSION_10_14 101400
|
||||
#endif
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_14
|
||||
static constexpr auto NSControlStateValueOn = NSOnState;
|
||||
static constexpr auto NSControlStateValueOff = NSOffState;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
struct VSTGUI_NSMenu : RuntimeObjCClass<VSTGUI_NSMenu>
|
||||
{
|
||||
static constexpr const auto privateVarName = "_private";
|
||||
|
||||
int32_t menuClassCount = 0;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static Class CreateClass ()
|
||||
{
|
||||
return ObjCClassBuilder ()
|
||||
.init ("VSTGUI_NSMenu", [NSMenu class])
|
||||
.addMethod (@selector (initWithOptionMenu:), Init)
|
||||
.addMethod (@selector (dealloc), Dealloc)
|
||||
.addMethod (@selector (validateMenuItem:), ValidateMenuItem)
|
||||
.addMethod (@selector (menuItemSelected:), MenuItemSelected)
|
||||
.addMethod (@selector (optionMenu), OptionMenu)
|
||||
.addMethod (@selector (selectedMenu), SelectedMenu)
|
||||
.addMethod (@selector (selectedItem), SelectedItem)
|
||||
.addMethod (@selector (setSelectedMenu:), SetSelectedMenu)
|
||||
.addMethod (@selector (setSelectedItem:), SetSelectedItem)
|
||||
.addIvar<VSTGUI_NSMenu::Var*> (privateVarName)
|
||||
.finalize ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
~VSTGUI_NSMenu () noexcept { vstgui_assert (menuClassCount == 0); }
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
struct Var
|
||||
{
|
||||
COptionMenu* _optionMenu {nullptr};
|
||||
COptionMenu* _selectedMenu {nullptr};
|
||||
int32_t _selectedItem {0};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static id Init (id self, SEL _cmd, void* _menu)
|
||||
{
|
||||
instance ().menuClassCount++;
|
||||
auto obj = makeInstance (self);
|
||||
self = obj.callSuper<id (id, SEL), id> (@selector (init));
|
||||
if (self)
|
||||
{
|
||||
NSMenu* nsMenu = (NSMenu*)self;
|
||||
COptionMenu* menu = (COptionMenu*)_menu;
|
||||
Var* var = new Var;
|
||||
var->_optionMenu = menu;
|
||||
setVar (self, var);
|
||||
|
||||
int32_t index = -1;
|
||||
bool multipleCheck = menu->isMultipleCheckStyle ();
|
||||
CConstMenuItemIterator it = menu->getItems ()->begin ();
|
||||
while (it != menu->getItems ()->end ())
|
||||
{
|
||||
CMenuItem* item = (*it);
|
||||
it++;
|
||||
index++;
|
||||
NSMenuItem* nsItem = nullptr;
|
||||
NSMutableString* itemTitle = [[[NSMutableString alloc]
|
||||
initWithString:fromUTF8String<NSString*> (item->getTitle ())] autorelease];
|
||||
if (menu->getPrefixNumbers ())
|
||||
{
|
||||
NSString* prefixString = nullptr;
|
||||
switch (menu->getPrefixNumbers ())
|
||||
{
|
||||
case 2:
|
||||
prefixString = [NSString stringWithFormat:@"%1d ", index + 1];
|
||||
break;
|
||||
case 3:
|
||||
prefixString = [NSString stringWithFormat:@"%02d ", index + 1];
|
||||
break;
|
||||
case 4:
|
||||
prefixString = [NSString stringWithFormat:@"%03d ", index + 1];
|
||||
break;
|
||||
}
|
||||
[itemTitle insertString:prefixString atIndex:0];
|
||||
}
|
||||
if (item->getSubmenu ())
|
||||
{
|
||||
nsItem = [nsMenu addItemWithTitle:itemTitle action:nil keyEquivalent:@""];
|
||||
NSMenu* subMenu = [[[[self class] alloc]
|
||||
initWithOptionMenu:(id)item->getSubmenu ()] autorelease];
|
||||
[nsMenu setSubmenu:subMenu forItem:nsItem];
|
||||
if (multipleCheck && item->isChecked ())
|
||||
[nsItem setState:NSControlStateValueOn];
|
||||
else
|
||||
[nsItem setState:NSControlStateValueOff];
|
||||
}
|
||||
else if (item->isSeparator ())
|
||||
{
|
||||
[nsMenu addItem:[NSMenuItem separatorItem]];
|
||||
}
|
||||
else
|
||||
{
|
||||
nsItem = [nsMenu addItemWithTitle:itemTitle
|
||||
action:@selector (menuItemSelected:)
|
||||
keyEquivalent:@""];
|
||||
if (item->isTitle ())
|
||||
[nsItem setIndentationLevel:1];
|
||||
[nsItem setTarget:nsMenu];
|
||||
[nsItem setTag:index];
|
||||
if (multipleCheck && item->isChecked ())
|
||||
[nsItem setState:NSControlStateValueOn];
|
||||
else
|
||||
[nsItem setState:NSControlStateValueOff];
|
||||
NSString* keyEquivalent = nil;
|
||||
if (!item->getKeycode ().empty ())
|
||||
{
|
||||
keyEquivalent = fromUTF8String<NSString*> (item->getKeycode ());
|
||||
}
|
||||
else if (item->getVirtualKey () != VirtualKey::None)
|
||||
{
|
||||
keyEquivalent = GetVirtualKeyCodeString (item->getVirtualKey ());
|
||||
}
|
||||
if (keyEquivalent)
|
||||
{
|
||||
[nsItem setKeyEquivalent:keyEquivalent];
|
||||
uint32_t keyModifiers = 0;
|
||||
if (item->getKeyModifiers () & kControl)
|
||||
keyModifiers |= MacEventModifier::CommandKeyMask;
|
||||
if (item->getKeyModifiers () & kShift)
|
||||
keyModifiers |= MacEventModifier::ShiftKeyMask;
|
||||
if (item->getKeyModifiers () & kAlt)
|
||||
keyModifiers |= MacEventModifier::AlternateKeyMask;
|
||||
if (item->getKeyModifiers () & kApple)
|
||||
keyModifiers |= MacEventModifier::ControlKeyMask;
|
||||
[nsItem setKeyEquivalentModifierMask:keyModifiers];
|
||||
}
|
||||
}
|
||||
if (nsItem)
|
||||
{
|
||||
if (auto nsImage = bitmapToNSImage (item->getIcon ()))
|
||||
{
|
||||
[nsItem setImage:nsImage];
|
||||
[nsImage release];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void setVar (id self, Var* var)
|
||||
{
|
||||
if (auto v = makeInstance (self).getVariable<Var*> (privateVarName))
|
||||
{
|
||||
if (auto old = v->get ())
|
||||
delete old;
|
||||
v->set (var);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Var* getVar (id self)
|
||||
{
|
||||
if (auto v = makeInstance (self).getVariable<Var*> (privateVarName); v.has_value ())
|
||||
return v->get ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void Dealloc (id self, SEL _cmd)
|
||||
{
|
||||
instance ().menuClassCount--;
|
||||
|
||||
setVar (self, nullptr);
|
||||
makeInstance (self).callSuper<void (id, SEL)> (_cmd);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static BOOL ValidateMenuItem (id self, SEL _cmd, id item)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
if (var && var->_optionMenu)
|
||||
{
|
||||
CMenuItem* menuItem = var->_optionMenu->getEntry ((int32_t)[item tag]);
|
||||
if (!menuItem->isEnabled () || menuItem->isTitle ())
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void MenuItemSelected (id self, SEL _cmd, id item)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
if (var)
|
||||
{
|
||||
id menu = self;
|
||||
while ([menu supermenu])
|
||||
menu = [menu supermenu];
|
||||
[menu performSelector:@selector (setSelectedMenu:) withObject:(id)var->_optionMenu];
|
||||
[menu performSelector:@selector (setSelectedItem:) withObject:(id)[item tag]];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void* OptionMenu (id self, SEL _cmd)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
return var ? var->_optionMenu : nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void* SelectedMenu (id self, SEL _cmd)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
return var ? var->_selectedMenu : nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static int32_t SelectedItem (id self, SEL _cmd)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
return var ? var->_selectedItem : 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void SetSelectedMenu (id self, SEL _cmd, void* menu)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
if (var)
|
||||
var->_selectedMenu = (COptionMenu*)menu;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
static void SetSelectedItem (id self, SEL _cmd, int32_t item)
|
||||
{
|
||||
Var* var = getVar (self);
|
||||
if (var)
|
||||
var->_selectedItem = item;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void NSViewOptionMenu::popup (COptionMenu* optionMenu, const Callback& callback)
|
||||
{
|
||||
vstgui_assert (optionMenu && callback, "arguments are required");
|
||||
|
||||
PlatformOptionMenuResult result = {};
|
||||
|
||||
CFrame* frame = optionMenu->getFrame ();
|
||||
if (!frame || !frame->getPlatformFrame ())
|
||||
{
|
||||
callback (optionMenu, result);
|
||||
return;
|
||||
}
|
||||
|
||||
NSViewFrame* nsViewFrame = dynamic_cast<NSViewFrame*> (frame->getPlatformFrame ());
|
||||
nsViewFrame->setMouseCursor (kCursorDefault);
|
||||
|
||||
CRect globalSize = optionMenu->translateToGlobal (optionMenu->getViewSize ());
|
||||
globalSize.offset (-frame->getViewSize ().getTopLeft ());
|
||||
|
||||
bool multipleCheck = optionMenu->isMultipleCheckStyle ();
|
||||
NSView* view = nsViewFrame->getNSView ();
|
||||
NSMenu* nsMenu = [VSTGUI_NSMenu::alloc () initWithOptionMenu:(id)optionMenu];
|
||||
CPoint p = globalSize.getTopLeft ();
|
||||
NSRect cellFrameRect = {};
|
||||
cellFrameRect.origin = nsPointFromCPoint (p);
|
||||
cellFrameRect.size.width = static_cast<CGFloat> (globalSize.getWidth ());
|
||||
cellFrameRect.size.height = static_cast<CGFloat> (globalSize.getHeight ());
|
||||
if (!optionMenu->isPopupStyle ())
|
||||
cellFrameRect.origin.y += cellFrameRect.size.height;
|
||||
if (!multipleCheck && optionMenu->isCheckStyle ())
|
||||
{
|
||||
[[nsMenu itemWithTag:static_cast<NSInteger> (optionMenu->getCurrentIndex (true))]
|
||||
setState:NSControlStateValueOn];
|
||||
}
|
||||
nsMenu.minimumWidth = cellFrameRect.size.width;
|
||||
|
||||
NSView* menuContainer = [[NSView alloc] initWithFrame:cellFrameRect];
|
||||
[view addSubview:menuContainer];
|
||||
|
||||
NSMenuItem* selectedItem = nil;
|
||||
if (optionMenu->isPopupStyle ())
|
||||
selectedItem = [nsMenu itemAtIndex:optionMenu->getValue ()];
|
||||
[nsMenu popUpMenuPositioningItem:selectedItem
|
||||
atLocation:NSMakePoint (0, menuContainer.frame.size.height)
|
||||
inView:menuContainer];
|
||||
|
||||
[menuContainer removeFromSuperviewWithoutNeedingDisplay];
|
||||
[menuContainer release];
|
||||
result.menu = (COptionMenu*)[nsMenu performSelector:@selector (selectedMenu)];
|
||||
result.index = (int32_t) (intptr_t)[nsMenu performSelector:@selector (selectedItem)];
|
||||
[nsMenu release];
|
||||
|
||||
callback (optionMenu, result);
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC_COCOA
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/message.h>
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
struct ObjCVariable
|
||||
{
|
||||
ObjCVariable (__unsafe_unretained id obj, Ivar ivar) : obj (obj), ivar (ivar) {}
|
||||
ObjCVariable (ObjCVariable&& o) { *this = std::move (o); }
|
||||
|
||||
ObjCVariable& operator= (ObjCVariable&& o)
|
||||
{
|
||||
obj = o.obj;
|
||||
ivar = o.ivar;
|
||||
o.obj = nullptr;
|
||||
o.ivar = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T get () const
|
||||
{
|
||||
auto offset = ivar_getOffset (ivar);
|
||||
return *reinterpret_cast<T*> (((__bridge uintptr_t)obj) + offset);
|
||||
}
|
||||
|
||||
void set (const T& value)
|
||||
{
|
||||
auto offset = ivar_getOffset (ivar);
|
||||
auto storage = reinterpret_cast<T*> (((__bridge uintptr_t)obj) + offset);
|
||||
*storage = value;
|
||||
}
|
||||
|
||||
private:
|
||||
__unsafe_unretained id obj;
|
||||
Ivar ivar {nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
struct ObjCInstance
|
||||
{
|
||||
ObjCInstance (__unsafe_unretained id obj, Class superClass = nullptr) : obj (obj)
|
||||
{
|
||||
os.super_class = superClass;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<ObjCVariable<T>> getVariable (const char* name) const
|
||||
{
|
||||
if (__strong auto ivar = class_getInstanceVariable (object_getClass (obj), name))
|
||||
{
|
||||
return {ObjCVariable<T> (obj, ivar)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Func, typename... T>
|
||||
void callSuper (SEL selector, T... args) const
|
||||
{
|
||||
void (*f) (__unsafe_unretained id, SEL, T...) =
|
||||
(void (*) (__unsafe_unretained id, SEL, T...))objc_msgSendSuper;
|
||||
f (getSuper (), selector, args...);
|
||||
}
|
||||
|
||||
template<typename Func, typename R, typename... T>
|
||||
R callSuper (SEL selector, T... args) const
|
||||
{
|
||||
R (*f)
|
||||
(__unsafe_unretained id, SEL, T...) =
|
||||
(R (*) (__unsafe_unretained id, SEL, T...))objc_msgSendSuper;
|
||||
return f (getSuper (), selector, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
id getSuper () const
|
||||
{
|
||||
if (os.receiver == nullptr)
|
||||
{
|
||||
os.receiver = obj;
|
||||
}
|
||||
if (os.super_class == nullptr)
|
||||
{
|
||||
os.super_class = class_getSuperclass (object_getClass (obj));
|
||||
}
|
||||
return (__bridge id) (&os);
|
||||
}
|
||||
|
||||
__unsafe_unretained id obj;
|
||||
mutable objc_super os {};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
struct RuntimeObjCClass
|
||||
{
|
||||
using Base = RuntimeObjCClass<T>;
|
||||
|
||||
static id alloc ()
|
||||
{
|
||||
static auto allocSel = sel_registerName ("alloc");
|
||||
if (auto method = class_getClassMethod (instance ().cl, allocSel))
|
||||
{
|
||||
if (auto methodImpl = method_getImplementation (method))
|
||||
{
|
||||
using fn2 = id (*) (id, SEL);
|
||||
auto alloc = (fn2)methodImpl;
|
||||
return alloc (instance ().cl, allocSel);
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
RuntimeObjCClass ()
|
||||
{
|
||||
cl = T::CreateClass ();
|
||||
superClass = class_getSuperclass (cl);
|
||||
}
|
||||
|
||||
virtual ~RuntimeObjCClass () noexcept
|
||||
{
|
||||
if (cl)
|
||||
objc_disposeClassPair (cl);
|
||||
}
|
||||
|
||||
static ObjCInstance makeInstance (__unsafe_unretained id obj)
|
||||
{
|
||||
return ObjCInstance (obj, instance ().superClass);
|
||||
}
|
||||
|
||||
protected:
|
||||
static T& instance ()
|
||||
{
|
||||
static T gInstance;
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
Class getClass () const { return cl; }
|
||||
Class getSuperClass () const { return superClass; }
|
||||
|
||||
private:
|
||||
Class cl {nullptr};
|
||||
Class superClass {nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
struct ObjCClassBuilder
|
||||
{
|
||||
ObjCClassBuilder& init (const char* name, Class baseClass);
|
||||
|
||||
template<typename Func>
|
||||
ObjCClassBuilder& addMethod (SEL selector, Func imp);
|
||||
template<typename T>
|
||||
ObjCClassBuilder& addIvar (const char* name);
|
||||
|
||||
ObjCClassBuilder& addProtocol (const char* name);
|
||||
ObjCClassBuilder& addProtocol (Protocol* proto);
|
||||
|
||||
Class finalize ();
|
||||
|
||||
private:
|
||||
static Class generateUniqueClass (const std::string& inClassName, Class baseClass);
|
||||
|
||||
template<typename Func>
|
||||
ObjCClassBuilder& addMethod (SEL selector, Func imp, const char* types);
|
||||
|
||||
ObjCClassBuilder& addIvar (const char* name, size_t size, uint8_t alignment, const char* types);
|
||||
|
||||
template<typename R, typename... T>
|
||||
static constexpr std::tuple<R, T...> functionArgs (R (*) (T...))
|
||||
{
|
||||
return std::tuple<R, T...> ();
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
static constexpr std::tuple<T...> functionArgs (void (*) (T...))
|
||||
{
|
||||
return std::tuple<T...> ();
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
static constexpr bool isVoidReturnType (R (*) (T...))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
static constexpr bool isVoidReturnType (void (*) (T...))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
static std::string encodeFunction (Proc proc)
|
||||
{
|
||||
std::string result;
|
||||
if (isVoidReturnType (proc))
|
||||
result = "v";
|
||||
std::apply ([&] (auto&&... args) { ((result += @encode (decltype (args))), ...); },
|
||||
functionArgs (proc));
|
||||
return result;
|
||||
}
|
||||
|
||||
Class cl {nullptr};
|
||||
Class baseClass {nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline ObjCClassBuilder& ObjCClassBuilder::init (const char* name, Class bc)
|
||||
{
|
||||
baseClass = bc;
|
||||
cl = generateUniqueClass (name, baseClass);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
inline Class ObjCClassBuilder::generateUniqueClass (const std::string& inClassName, Class baseClass)
|
||||
{
|
||||
std::string className (inClassName);
|
||||
int32_t iteration = 0;
|
||||
while (objc_lookUpClass (className.data ()) != nil)
|
||||
{
|
||||
iteration++;
|
||||
className = inClassName + "_" + std::to_string (iteration);
|
||||
}
|
||||
Class resClass = objc_allocateClassPair (baseClass, className.data (), 0);
|
||||
return resClass;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline Class ObjCClassBuilder::finalize ()
|
||||
{
|
||||
objc_registerClassPair (cl);
|
||||
|
||||
auto res = cl;
|
||||
baseClass = cl = nullptr;
|
||||
return res;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename Func>
|
||||
inline ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp, const char* types)
|
||||
{
|
||||
auto res = class_addMethod (cl, selector, IMP (imp), types);
|
||||
assert (res == true);
|
||||
(void)res;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename Func>
|
||||
ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp)
|
||||
{
|
||||
return addMethod (selector, imp, encodeFunction (imp).data ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name)
|
||||
{
|
||||
return addIvar (name, sizeof (T), static_cast<uint8_t> (std::log2 (sizeof (T))), @encode (T));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name, size_t size,
|
||||
uint8_t alignment, const char* types)
|
||||
{
|
||||
auto res = class_addIvar (cl, name, size, alignment, types);
|
||||
assert (res == true);
|
||||
(void)res;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (const char* name)
|
||||
{
|
||||
if (auto protocol = objc_getProtocol (name))
|
||||
return addProtocol (protocol);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (Protocol* proto)
|
||||
{
|
||||
auto res = class_addProtocol (cl, proto);
|
||||
assert (res == true);
|
||||
(void)res;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../iplatformgraphicsdevice.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <CoreText/CoreText.h>
|
||||
#else
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
class CoreGraphicsDevice;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CoreGraphicsDeviceContext : public IPlatformGraphicsDeviceContext,
|
||||
public IPlatformGraphicsDeviceContextBitmapExt,
|
||||
public IPlatformGraphicsDeviceContextGradientExt
|
||||
{
|
||||
public:
|
||||
CoreGraphicsDeviceContext (const CoreGraphicsDevice& device, void* cgContext);
|
||||
~CoreGraphicsDeviceContext () noexcept override;
|
||||
|
||||
const IPlatformGraphicsDevice& getDevice () const override;
|
||||
PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const override;
|
||||
|
||||
bool beginDraw () const override;
|
||||
bool endDraw () const override;
|
||||
// draw commands
|
||||
bool drawLine (LinePair line) const override;
|
||||
bool drawLines (const LineList& lines) const override;
|
||||
bool drawPolygon (const PointList& polygonPointList,
|
||||
PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawArc (CRect rect, double startAngle1, double endAngle2,
|
||||
PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override;
|
||||
bool drawPoint (CPoint point, CColor color) const override;
|
||||
bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha,
|
||||
BitmapInterpolationQuality quality) const override;
|
||||
bool clearRect (CRect rect) const override;
|
||||
bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode,
|
||||
TransformMatrix* transformation) const override;
|
||||
bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient,
|
||||
CPoint startPoint, CPoint endPoint, bool evenOdd,
|
||||
TransformMatrix* transformation) const override;
|
||||
bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient,
|
||||
CPoint center, CCoord radius, CPoint originOffset, bool evenOdd,
|
||||
TransformMatrix* transformation) const override;
|
||||
// state
|
||||
void saveGlobalState () const override;
|
||||
void restoreGlobalState () const override;
|
||||
void setLineStyle (const CLineStyle& style) const override;
|
||||
void setLineWidth (CCoord width) const override;
|
||||
void setDrawMode (CDrawMode mode) const override;
|
||||
void setClipRect (CRect clip) const override;
|
||||
void setFillColor (CColor color) const override;
|
||||
void setFrameColor (CColor color) const override;
|
||||
void setGlobalAlpha (double newAlpha) const override;
|
||||
void setTransformMatrix (const TransformMatrix& tm) const override;
|
||||
|
||||
// extension
|
||||
const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const override;
|
||||
|
||||
// IPlatformGraphicsDeviceContextBitmapExt
|
||||
bool drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dest,
|
||||
const CNinePartTiledDescription& desc, double alpha,
|
||||
BitmapInterpolationQuality quality) const override;
|
||||
bool fillRectWithBitmap (IPlatformBitmap& bitmap, CRect srcRect, CRect dstRect, double alpha,
|
||||
BitmapInterpolationQuality quality) const override;
|
||||
|
||||
// IPlatformGraphicsDeviceContextGradientExt
|
||||
bool drawLinearGradientLine (const PointList& line, const IPlatformGradient& gradient,
|
||||
CCoord lineWidth, LineCap lineCap,
|
||||
LineJoin lineJoin) const override;
|
||||
|
||||
// private
|
||||
void drawCTLine (CTLineRef line, CGPoint cgPoint, CTFontRef fontRef, CColor color,
|
||||
bool underline, bool strikeThrough, bool antialias) const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CoreGraphicsBitmapContext : public CoreGraphicsDeviceContext
|
||||
{
|
||||
public:
|
||||
using EndDrawFunc = std::function<void ()>;
|
||||
CoreGraphicsBitmapContext (const CoreGraphicsDevice& device, void* cgContext, EndDrawFunc&& f);
|
||||
|
||||
bool endDraw () const override;
|
||||
|
||||
private:
|
||||
EndDrawFunc endDrawFunc;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CoreGraphicsDevice : public IPlatformGraphicsDevice
|
||||
{
|
||||
public:
|
||||
PlatformGraphicsDeviceContextPtr
|
||||
createBitmapContext (const PlatformBitmapPtr& bitmap) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CoreGraphicsDeviceFactory : public IPlatformGraphicsDeviceFactory
|
||||
{
|
||||
public:
|
||||
PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
+1040
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../iplatformopenglview.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class UIView, GLKView, NSRecursiveLock;
|
||||
#else
|
||||
struct GLKView;
|
||||
struct UIView;
|
||||
struct NSRecursiveLock;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
class GLKitOpenGLView : public IPlatformOpenGLView
|
||||
{
|
||||
public:
|
||||
GLKitOpenGLView (UIView* parent);
|
||||
~GLKitOpenGLView ();
|
||||
|
||||
bool init (IOpenGLView* view, PixelFormat* pixelFormat = nullptr) override;
|
||||
void remove () override;
|
||||
|
||||
void invalidRect (const CRect& rect) override;
|
||||
void viewSizeChanged (const CRect& visibleSize) override;
|
||||
|
||||
bool makeContextCurrent () override;
|
||||
bool lockContext () override;
|
||||
bool unlockContext () override;
|
||||
|
||||
void swapBuffers () override;
|
||||
|
||||
void doDraw (const CRect& r);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
protected:
|
||||
UIView* parent;
|
||||
GLKView* platformView;
|
||||
IOpenGLView* view;
|
||||
NSRecursiveLock* lock;
|
||||
PixelFormat pixelFormat;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // VSTGUI_OPENGL_SUPPORT
|
||||
#endif // TARGET_OS_IPHONE
|
||||
@@ -0,0 +1,185 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "uiopenglview.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
|
||||
#import <GLKit/GLKit.h>
|
||||
|
||||
#if __has_feature(objc_arc) && __clang_major__ >= 3
|
||||
#define ARC_ENABLED 1
|
||||
#endif // __has_feature(objc_arc)
|
||||
|
||||
@interface VSTGUI_GLKView : GLKView
|
||||
{
|
||||
VSTGUI::GLKitOpenGLView* view;
|
||||
}
|
||||
@end
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@implementation VSTGUI_GLKView
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (id)initWithFrame:(CGRect)frame context:(EAGLContext *)context GLKitOpenGLView:(VSTGUI::GLKitOpenGLView*)_view
|
||||
{
|
||||
self = [super initWithFrame:frame context:context];
|
||||
if (self)
|
||||
{
|
||||
view = _view;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
VSTGUI::CRect r (rect.origin.x, rect.origin.y, 0, 0);
|
||||
r.setWidth (rect.size.width);
|
||||
r.setHeight (rect.size.height);
|
||||
view->doDraw (r);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
GLKitOpenGLView::GLKitOpenGLView (UIView* parent)
|
||||
: parent (parent)
|
||||
, platformView (nil)
|
||||
, view (nullptr)
|
||||
, lock (nil)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
GLKitOpenGLView::~GLKitOpenGLView ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GLKitOpenGLView::init (IOpenGLView* _view, PixelFormat* _pixelFormat)
|
||||
{
|
||||
if (platformView || parent == nil)
|
||||
return false;
|
||||
|
||||
EAGLContext* glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
|
||||
if (glContext == nil)
|
||||
return false;
|
||||
|
||||
#if !ARC_ENABLED
|
||||
[glContext autorelease];
|
||||
#endif
|
||||
lock = [NSRecursiveLock new];
|
||||
|
||||
CGRect r = CGRectMake (0, 0, 100, 100);
|
||||
platformView = [[VSTGUI_GLKView alloc] initWithFrame:r context:glContext GLKitOpenGLView:this];
|
||||
if (platformView == nil)
|
||||
return false;
|
||||
if (_pixelFormat)
|
||||
{
|
||||
pixelFormat = *_pixelFormat;
|
||||
}
|
||||
if (pixelFormat.kMultiSample)
|
||||
platformView.drawableMultisample = GLKViewDrawableMultisample4X;
|
||||
if (pixelFormat.depthSize == 16)
|
||||
platformView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
|
||||
else if (pixelFormat.depthSize == 24)
|
||||
platformView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void GLKitOpenGLView::remove ()
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
[platformView removeFromSuperview];
|
||||
#if !ARC_ENABLED
|
||||
[platformView release];
|
||||
[lock release];
|
||||
#endif
|
||||
platformView = nil;
|
||||
view = nullptr;
|
||||
lock = nil;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void GLKitOpenGLView::invalidRect (const CRect& rect)
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
CGRect r = CGRectMake (rect.left, rect.top, rect.getWidth (), rect.getHeight ());
|
||||
[platformView setNeedsDisplayInRect:r];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void GLKitOpenGLView::viewSizeChanged (const CRect& visibleSize)
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
[lock lock];
|
||||
CGRect r = CGRectMake (visibleSize.left, visibleSize.top, visibleSize.getWidth (), visibleSize.getHeight ());
|
||||
platformView.frame = r;
|
||||
if ([platformView superview] == nil)
|
||||
{
|
||||
[parent addSubview:platformView];
|
||||
}
|
||||
[lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GLKitOpenGLView::makeContextCurrent ()
|
||||
{
|
||||
if (platformView)
|
||||
{
|
||||
return [EAGLContext setCurrentContext:platformView.context] ? true : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GLKitOpenGLView::lockContext ()
|
||||
{
|
||||
[lock lock];
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GLKitOpenGLView::unlockContext ()
|
||||
{
|
||||
[lock unlock];
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void GLKitOpenGLView::swapBuffers ()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void GLKitOpenGLView::doDraw (const CRect& r)
|
||||
{
|
||||
if (view)
|
||||
{
|
||||
lockContext ();
|
||||
view->drawOpenGL (r);
|
||||
unlockContext ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // VSTGUI_OPENGL_SUPPORT
|
||||
#endif // TARGET_OS_IPHONE
|
||||
@@ -0,0 +1,42 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../iplatformtextedit.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class UIView;
|
||||
@class UITextField;
|
||||
@class VSTGUI_UITextFieldDelegate;
|
||||
#else
|
||||
struct UIView;
|
||||
struct UITextField;
|
||||
struct VSTGUI_UITextFieldDelegate;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
class UITextEdit : public IPlatformTextEdit
|
||||
{
|
||||
public:
|
||||
UITextEdit (UIView* parent, IPlatformTextEditCallback* textEdit);
|
||||
~UITextEdit ();
|
||||
|
||||
UTF8String getText () override;
|
||||
bool setText (const UTF8String& text) override;
|
||||
bool updateSize () override;
|
||||
bool drawsPlaceholder () const override { return true; }
|
||||
|
||||
protected:
|
||||
UITextField* platformControl;
|
||||
VSTGUI_UITextFieldDelegate* delegate;
|
||||
UIView* parent;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // TARGET_OS_IPHONE
|
||||
@@ -0,0 +1,182 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "uitextedit.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#import "../cfontmac.h"
|
||||
#import "../macglobals.h"
|
||||
#import "../macstring.h"
|
||||
|
||||
#if __has_feature(objc_arc) && __clang_major__ >= 3
|
||||
#define ARC_ENABLED 1
|
||||
#endif // __has_feature(objc_arc)
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
@interface VSTGUI_UITextFieldDelegate : NSObject<UITextFieldDelegate>
|
||||
//------------------------------------------------------------------------------------
|
||||
{
|
||||
VSTGUI::IPlatformTextEditCallback* textEdit;
|
||||
}
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
@implementation VSTGUI_UITextFieldDelegate
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
- (id)initWithUITextEdit:(VSTGUI::IPlatformTextEditCallback*)_textEdit
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
textEdit = _textEdit;
|
||||
return self;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
- (void)looseFocus
|
||||
{
|
||||
if (textEdit)
|
||||
{
|
||||
textEdit->platformLooseFocus (false);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
|
||||
{
|
||||
if (textEdit)
|
||||
{
|
||||
[self performSelector:@selector(looseFocus) withObject:nil afterDelay:0];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
||||
{
|
||||
VSTGUI::IPlatformTextEditCallback* tmp = textEdit;
|
||||
textEdit = 0;
|
||||
tmp->platformLooseFocus (true);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
UITextEdit::UITextEdit (UIView* parent, IPlatformTextEditCallback* textEdit)
|
||||
: IPlatformTextEdit (textEdit)
|
||||
, platformControl (0)
|
||||
, parent (parent)
|
||||
{
|
||||
delegate = [[VSTGUI_UITextFieldDelegate alloc] initWithUITextEdit:textEdit];
|
||||
|
||||
CRect rect (textEdit->platformGetSize ());
|
||||
CPoint textInset = textEdit->platformGetTextInset ();
|
||||
CGRect r = CGRectFromCRect (rect);
|
||||
r.origin.x += textInset.x / 2.;
|
||||
r.origin.y += textInset.y / 2.;
|
||||
r.size.width -= textInset.x / 2;
|
||||
r.size.height -= textInset.y / 2;
|
||||
platformControl = [[UITextField alloc] initWithFrame:r];
|
||||
|
||||
bool fontSet = false;
|
||||
CoreTextFont* ctf = textEdit->platformGetFont ()->getPlatformFont ().cast<CoreTextFont> ();
|
||||
if (ctf)
|
||||
{
|
||||
CTFontRef fontRef = ctf->getFontRef ();
|
||||
if (fontRef)
|
||||
{
|
||||
CTFontDescriptorRef fontDesc = CTFontCopyFontDescriptor (fontRef);
|
||||
[platformControl setFont:[UIFont fontWithDescriptor:(__bridge UIFontDescriptor *)fontDesc size:0]];
|
||||
CFRelease (fontDesc);
|
||||
fontSet = true;
|
||||
}
|
||||
}
|
||||
if (!fontSet)
|
||||
{
|
||||
NSString* fontName = [NSString stringWithCString:textEdit->platformGetFont ()->getName () encoding:NSUTF8StringEncoding];
|
||||
[platformControl setFont:[UIFont fontWithName:fontName size:static_cast<CGFloat> (textEdit->platformGetFont ()->getSize ())]];
|
||||
}
|
||||
CColor fontColor = textEdit->platformGetFontColor ();
|
||||
platformControl.textColor = [UIColor colorWithRed:fontColor.red / 255.f green:fontColor.green / 255.f blue:fontColor.red / 255.f alpha:fontColor.alpha / 255.f];
|
||||
platformControl.borderStyle = UITextBorderStyleNone;
|
||||
platformControl.opaque = NO;
|
||||
platformControl.clearsContextBeforeDrawing = YES;
|
||||
NSTextAlignment textAlignment;
|
||||
switch (textEdit->platformGetHoriTxtAlign ())
|
||||
{
|
||||
case kLeftText: textAlignment = NSTextAlignmentLeft; break;
|
||||
case kCenterText: textAlignment = NSTextAlignmentCenter; break;
|
||||
case kRightText:textAlignment = NSTextAlignmentRight; break;
|
||||
}
|
||||
platformControl.textAlignment = textAlignment;
|
||||
platformControl.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
|
||||
platformControl.clearButtonMode = UITextFieldViewModeNever;
|
||||
platformControl.returnKeyType = UIReturnKeyDefault;
|
||||
platformControl.enablesReturnKeyAutomatically = YES;
|
||||
platformControl.delegate = delegate;
|
||||
|
||||
setText (textEdit->platformGetText ());
|
||||
|
||||
[parent addSubview:platformControl];
|
||||
|
||||
[platformControl becomeFirstResponder];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
UITextEdit::~UITextEdit ()
|
||||
{
|
||||
if (platformControl)
|
||||
{
|
||||
[platformControl removeFromSuperview];
|
||||
#if !ARC_ENABLED
|
||||
[platformControl release];
|
||||
#endif
|
||||
platformControl = nil;
|
||||
}
|
||||
#if !ARC_ENABLED
|
||||
[delegate release];
|
||||
#endif
|
||||
delegate = nil;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
UTF8String UITextEdit::getText ()
|
||||
{
|
||||
if (platformControl)
|
||||
{
|
||||
NSString* text = [platformControl text];
|
||||
return [text UTF8String];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
bool UITextEdit::setText (const UTF8String& text)
|
||||
{
|
||||
if (platformControl == nullptr)
|
||||
return false;
|
||||
if (NSString* t = fromUTF8String<NSString*> (text))
|
||||
{
|
||||
[platformControl setText:t];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
bool UITextEdit::updateSize ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../itouchevent.h"
|
||||
|
||||
#if VSTGUI_TOUCH_EVENT_HANDLING
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class UITouch;
|
||||
#else
|
||||
struct UITouch;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
class UITouchEvent : public ITouchEvent
|
||||
{
|
||||
public:
|
||||
typedef std::map<UITouch*, int32_t> NativeTouches;
|
||||
|
||||
int32_t touchCounter;
|
||||
double currentTime;
|
||||
NativeTouches nativeTouches;
|
||||
|
||||
UITouchEvent () : touchCounter (0) {}
|
||||
|
||||
TouchMap& getTouchMap () { return touches; }
|
||||
double getTimeStamp () const override { return currentTime; }
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // VSTGUI_TOUCH_EVENT_HANDLING
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../vstguifwd.h"
|
||||
#include "../../../cview.h"
|
||||
#include "../../iplatformframe.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
struct UIView;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class UIViewFrame : public IPlatformFrame
|
||||
{
|
||||
public:
|
||||
UIViewFrame (IPlatformFrameCallback* frame, const CRect& size, UIView* parent);
|
||||
~UIViewFrame ();
|
||||
|
||||
UIView* getPlatformControl () const { return uiView; }
|
||||
IPlatformFrameCallback* getFrame () const { return frame; }
|
||||
|
||||
// IPlatformFrame
|
||||
bool getGlobalPosition (CPoint& pos) const override;
|
||||
bool setSize (const CRect& newSize) override;
|
||||
bool getSize (CRect& size) const override;
|
||||
bool getCurrentMousePosition (CPoint& mousePosition) const override { return false; };
|
||||
bool getCurrentMouseButtons (CButtonState& buttons) const override { return false; };
|
||||
bool getCurrentModifiers (Modifiers& modifiers) const override { return false; }
|
||||
bool setMouseCursor (CCursorType type) override { return false; };
|
||||
bool invalidRect (const CRect& rect) override;
|
||||
bool scrollRect (const CRect& src, const CPoint& distance) override;
|
||||
bool showTooltip (const CRect& rect, const char* utf8Text) override { return false; };
|
||||
bool hideTooltip () override { return false; };
|
||||
void* getPlatformRepresentation () const override { return (__bridge void*)uiView; }
|
||||
SharedPointer<IPlatformTextEdit> createPlatformTextEdit (IPlatformTextEditCallback* textEdit) override;
|
||||
SharedPointer<IPlatformOptionMenu> createPlatformOptionMenu () override;
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
SharedPointer<IPlatformOpenGLView> createPlatformOpenGLView () override;
|
||||
#endif
|
||||
SharedPointer<IPlatformViewLayer> createPlatformViewLayer (IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer) override;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) override;
|
||||
#endif
|
||||
bool doDrag (const DragDescription& dragDescription, const SharedPointer<IDragCallback>& callback) override;
|
||||
|
||||
PlatformType getPlatformType () const override { return PlatformType::kUIView; }
|
||||
void onFrameClosed () override {}
|
||||
Optional<UTF8String> convertCurrentKeyEventToText () override { return {}; }
|
||||
bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) override { return false; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
UIView* uiView;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // TARGET_OS_IPHONE
|
||||
@@ -0,0 +1,294 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "uiviewframe.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "../../../cfileselector.h"
|
||||
#import "../../../idatapackage.h"
|
||||
#import "../../iplatformoptionmenu.h"
|
||||
#import "../coregraphicsdevicecontext.h"
|
||||
#import "../cgbitmap.h"
|
||||
#import "../quartzgraphicspath.h"
|
||||
#import "../caviewlayer.h"
|
||||
#import "uitouchevent.h"
|
||||
#import "uitextedit.h"
|
||||
#import "uiopenglview.h"
|
||||
#import <vector>
|
||||
|
||||
#if __has_feature(objc_arc) && __clang_major__ >= 3
|
||||
#define ARC_ENABLED 1
|
||||
#endif // __has_feature(objc_arc)
|
||||
|
||||
using namespace VSTGUI;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@interface VSTGUI_UIView : UIView
|
||||
//-----------------------------------------------------------------------------
|
||||
{
|
||||
UIViewFrame* uiViewFrame;
|
||||
UITouchEvent touchEvent;
|
||||
}
|
||||
- (id)initWithUIViewFrame:(UIViewFrame*)viewFrame parent:(UIView*)parent size:(const CRect*)size;
|
||||
|
||||
@end
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@implementation VSTGUI_UIView
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (id)initWithUIViewFrame:(UIViewFrame*)viewFrame parent:(UIView*)parent size:(const CRect*)size
|
||||
{
|
||||
self = [super initWithFrame:CGRectFromCRect (*size)];
|
||||
if (self)
|
||||
{
|
||||
self.multipleTouchEnabled = YES;
|
||||
self.exclusiveTouch = YES;
|
||||
uiViewFrame = viewFrame;
|
||||
[parent addSubview:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
auto device = getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen (
|
||||
DefaultScreenIdentifier);
|
||||
if (!device)
|
||||
return;
|
||||
auto cgDevice = std::static_pointer_cast<CoreGraphicsDevice> (device);
|
||||
auto deviceContext = std::make_shared<CoreGraphicsDeviceContext> (
|
||||
*cgDevice.get (), UIGraphicsGetCurrentContext ());
|
||||
|
||||
uiViewFrame->getFrame ()->platformDrawRects (deviceContext, self.layer.contentsScale,
|
||||
{CRectFromCGRect (rect)});
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
uiViewFrame->getFrame ()->platformOnActivate (self.window ? true : false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)updateTouchEvent:(NSSet*)touches
|
||||
{
|
||||
ITouchEvent::TouchMap& touchMap = touchEvent.getTouchMap ();
|
||||
for (UITouch* touch in touches)
|
||||
{
|
||||
auto it = touchEvent.nativeTouches.find (touch);
|
||||
if (it != touchEvent.nativeTouches.end ())
|
||||
{
|
||||
auto iTouch = touchMap.find (it->second);
|
||||
vstgui_assert (iTouch != touchMap.end ());
|
||||
if (touch.phase == UITouchPhaseStationary)
|
||||
{
|
||||
iTouch->second.state = ITouchEvent::kNoChange;
|
||||
}
|
||||
else
|
||||
{
|
||||
iTouch->second.location = CPointFromCGPoint ([touch locationInView:self]);
|
||||
switch (touch.phase)
|
||||
{
|
||||
case UITouchPhaseMoved: iTouch->second.state = ITouchEvent::kMoved; break;
|
||||
case UITouchPhaseEnded: iTouch->second.state = ITouchEvent::kEnded; break;
|
||||
case UITouchPhaseCancelled: iTouch->second.state = ITouchEvent::kCanceled; break;
|
||||
default: iTouch->second.state = ITouchEvent::kNoChange; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (touch.phase == UITouchPhaseBegan)
|
||||
{
|
||||
int32_t touchID = touchEvent.touchCounter++;
|
||||
touchEvent.nativeTouches.insert (std::make_pair (touch, touchID));
|
||||
ITouchEvent::Touch t;
|
||||
t.timeStamp = touchEvent.currentTime;
|
||||
t.state = ITouchEvent::kBegan;
|
||||
t.location = CPointFromCGPoint ([touch locationInView:self]);
|
||||
t.tapCount = static_cast<uint32_t> (touch.tapCount);
|
||||
touchMap.insert (std::make_pair (touchID, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)removeTouches:(NSSet*)touches
|
||||
{
|
||||
ITouchEvent::TouchMap& touchMap = touchEvent.getTouchMap ();
|
||||
for (UITouch* touch in touches)
|
||||
{
|
||||
auto it = touchEvent.nativeTouches.find (touch);
|
||||
vstgui_assert (it != touchEvent.nativeTouches.end ());
|
||||
if (it != touchEvent.nativeTouches.end ())
|
||||
{
|
||||
touchMap.erase (it->second);
|
||||
touchEvent.nativeTouches.erase (it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
|
||||
{
|
||||
if (event.type == UIEventTypeTouches)
|
||||
{
|
||||
touchEvent.currentTime = event.timestamp;
|
||||
[self updateTouchEvent:[event allTouches]];
|
||||
uiViewFrame->getFrame()->platformOnTouchEvent (touchEvent);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
|
||||
{
|
||||
if (event.type == UIEventTypeTouches)
|
||||
{
|
||||
touchEvent.currentTime = event.timestamp;
|
||||
[self updateTouchEvent:[event allTouches]];
|
||||
uiViewFrame->getFrame()->platformOnTouchEvent (touchEvent);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
|
||||
{
|
||||
if (event.type == UIEventTypeTouches)
|
||||
{
|
||||
touchEvent.currentTime = event.timestamp;
|
||||
[self updateTouchEvent:[event allTouches]];
|
||||
uiViewFrame->getFrame()->platformOnTouchEvent (touchEvent);
|
||||
[self removeTouches:touches];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
|
||||
{
|
||||
if (event.type == UIEventTypeTouches)
|
||||
{
|
||||
touchEvent.currentTime = event.timestamp;
|
||||
[self updateTouchEvent:[event allTouches]];
|
||||
uiViewFrame->getFrame()->platformOnTouchEvent (touchEvent);
|
||||
[self removeTouches:touches];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
UIViewFrame::UIViewFrame (IPlatformFrameCallback* frame, const CRect& size, UIView* parent)
|
||||
: IPlatformFrame (frame)
|
||||
, uiView (nullptr)
|
||||
{
|
||||
uiView = [[VSTGUI_UIView alloc] initWithUIViewFrame:this parent:parent size:&size];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
UIViewFrame::~UIViewFrame ()
|
||||
{
|
||||
[uiView removeFromSuperview];
|
||||
#if !ARC_ENABLED
|
||||
[uiView release];
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool UIViewFrame::getGlobalPosition (CPoint& pos) const
|
||||
{
|
||||
if (uiView)
|
||||
{
|
||||
CGPoint p = [uiView convertPoint:[uiView bounds].origin toView:nil];
|
||||
pos.x = p.x;
|
||||
pos.y = p.y;
|
||||
// TODO: check if this is correct in all cases
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool UIViewFrame::setSize (const CRect& newSize)
|
||||
{
|
||||
if (uiView)
|
||||
{
|
||||
uiView.frame = CGRectFromCRect (newSize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool UIViewFrame::getSize (CRect& size) const
|
||||
{
|
||||
if (uiView)
|
||||
{
|
||||
size = CRectFromCGRect (uiView.frame);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool UIViewFrame::invalidRect (const CRect& rect)
|
||||
{
|
||||
[uiView setNeedsDisplayInRect:CGRectFromCRect (rect)];
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool UIViewFrame::scrollRect (const CRect& src, const CPoint& distance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformTextEdit> UIViewFrame::createPlatformTextEdit (IPlatformTextEditCallback* textEdit)
|
||||
{
|
||||
return owned <IPlatformTextEdit> (new UITextEdit (uiView, textEdit));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformOptionMenu> UIViewFrame::createPlatformOptionMenu ()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if VSTGUI_OPENGL_SUPPORT
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformOpenGLView> UIViewFrame::createPlatformOpenGLView ()
|
||||
{
|
||||
return owned<IPlatformOpenGLView> (new GLKitOpenGLView (uiView));
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IPlatformViewLayer> UIViewFrame::createPlatformViewLayer (IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer)
|
||||
{
|
||||
CAViewLayer* parentViewLayer = dynamic_cast<CAViewLayer*> (parentLayer);
|
||||
auto layer = owned (new CAViewLayer (parentViewLayer ? parentViewLayer->getCALayer () : [uiView layer]));
|
||||
layer->init (drawDelegate);
|
||||
return shared<IPlatformViewLayer> (layer);
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
DragResult UIViewFrame::doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) { return kDragError; }
|
||||
#endif
|
||||
bool UIViewFrame::doDrag (const DragDescription& dragDescription, const SharedPointer<IDragCallback>& callback) { return false; }
|
||||
|
||||
}
|
||||
|
||||
#endif // TARGET_OS_IPHONE
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../vstguibase.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class NSPasteboard;
|
||||
#else
|
||||
struct NSPasteboard;
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
class IDataPackage;
|
||||
|
||||
namespace MacClipboard {
|
||||
|
||||
extern SharedPointer<IDataPackage> createClipboardDataPackage ();
|
||||
extern SharedPointer<IDataPackage> createDragDataPackage (NSPasteboard* pasteboard);
|
||||
extern void setClipboard (const SharedPointer<IDataPackage>& data);
|
||||
extern const char* getPasteboardBinaryType ();
|
||||
|
||||
}} // namespaces
|
||||
@@ -0,0 +1,360 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#import "macclipboard.h"
|
||||
#import "macglobals.h"
|
||||
#import "../../cdropsource.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <vector>
|
||||
#import <string>
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_14
|
||||
#define MAC_OS_X_VERSION_10_14 101400
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
namespace MacClipboard {
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
|
||||
//------------------------------------------------------------------------
|
||||
class Pasteboard : public IDataPackage
|
||||
{
|
||||
public:
|
||||
Pasteboard (NSPasteboard* pb) : pb (pb) { entries.resize ([pb pasteboardItems].count); }
|
||||
|
||||
uint32_t getCount () const override { return static_cast<uint32_t> (entries.size ()); }
|
||||
uint32_t getDataSize (uint32_t index) const override
|
||||
{
|
||||
if (index >= getCount ())
|
||||
return 0;
|
||||
prepareEntryAtIndex (index);
|
||||
return static_cast<uint32_t> (entries[index].data.size ());
|
||||
}
|
||||
Type getDataType (uint32_t index) const override
|
||||
{
|
||||
if (index >= getCount ())
|
||||
return Type::kError;
|
||||
prepareEntryAtIndex (index);
|
||||
return entries[index].type;
|
||||
}
|
||||
uint32_t getData (uint32_t index, const void*& buffer, Type& type) const override
|
||||
{
|
||||
if (index >= getCount ())
|
||||
return 0;
|
||||
prepareEntryAtIndex (index);
|
||||
buffer = entries[index].data.data ();
|
||||
type = entries[index].type;
|
||||
return static_cast<uint32_t> (entries[index].data.size ());
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
Type type {Type::kError};
|
||||
};
|
||||
|
||||
void prepareEntryAtIndex (uint32_t index) const
|
||||
{
|
||||
if (entries[index].type == Type::kError)
|
||||
entries[index] = makeEntry (pb.pasteboardItems[index]);
|
||||
}
|
||||
|
||||
static Entry makeEntry (NSPasteboardItem* item)
|
||||
{
|
||||
auto DefaultPBItemTypes =
|
||||
@[NSPasteboardTypeString, NSPasteboardTypeFileURL, NSPasteboardTypeColor];
|
||||
Entry result;
|
||||
if (auto availableType = [item availableTypeFromArray:DefaultPBItemTypes])
|
||||
{
|
||||
if ([availableType isEqualToString:NSPasteboardTypeFileURL])
|
||||
{
|
||||
result.type = Type::kFilePath;
|
||||
NSString* fileUrlStr = [item stringForType:NSPasteboardTypeFileURL];
|
||||
NSURL* url = [NSURL URLWithString:fileUrlStr];
|
||||
std::string pathStr = url.path.UTF8String;
|
||||
result.data.resize (pathStr.size ());
|
||||
memcpy (result.data.data (), pathStr.data (), pathStr.size ());
|
||||
}
|
||||
else if ([availableType isEqualToString:NSPasteboardTypeColor])
|
||||
{
|
||||
result.type = Type::kText;
|
||||
if (NSData* nsColorData = [item dataForType:NSPasteboardTypeColor])
|
||||
{
|
||||
if (NSColor* nsColor =
|
||||
[NSKeyedUnarchiver unarchivedObjectOfClass:[NSColor class]
|
||||
fromData:nsColorData
|
||||
error:nil])
|
||||
{
|
||||
nsColor = [nsColor
|
||||
colorUsingColorSpace:[[[NSColorSpace alloc]
|
||||
initWithCGColorSpace:GetCGColorSpace ()]
|
||||
autorelease]];
|
||||
int32_t red = static_cast<int32_t> ([nsColor redComponent] * 255.);
|
||||
int32_t green = static_cast<int32_t> ([nsColor greenComponent] * 255.);
|
||||
int32_t blue = static_cast<int32_t> ([nsColor blueComponent] * 255.);
|
||||
int32_t alpha = static_cast<int32_t> ([nsColor alphaComponent] * 255.);
|
||||
char str[10];
|
||||
snprintf (str, 10, "#%02x%02x%02x%02x", red, green, blue, alpha);
|
||||
result.data.resize (10);
|
||||
memcpy (result.data.data (), str, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert ([availableType isEqualToString:NSPasteboardTypeString]);
|
||||
result.type = Type::kText;
|
||||
if (auto data = [item dataForType:availableType])
|
||||
{
|
||||
result.data.resize (data.length);
|
||||
memcpy (result.data.data (), data.bytes, data.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.type = Type::kBinary;
|
||||
auto data = [item dataForType:item.types[0]];
|
||||
result.data.resize (data.length);
|
||||
memcpy (result.data.data (), data.bytes, data.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NSPasteboard* pb;
|
||||
mutable std::vector<Entry> entries;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class Pasteboard : public IDataPackage
|
||||
{
|
||||
public:
|
||||
Pasteboard (NSPasteboard* pb);
|
||||
~Pasteboard () noexcept override;
|
||||
|
||||
uint32_t getCount () const override;
|
||||
uint32_t getDataSize (uint32_t index) const override;
|
||||
Type getDataType (uint32_t index) const override;
|
||||
uint32_t getData (uint32_t index, const void*& buffer, Type& type) const override;
|
||||
protected:
|
||||
NSPasteboard* pb;
|
||||
uint32_t nbItems;
|
||||
bool stringsAreFiles;
|
||||
std::vector<std::string> strings;
|
||||
NSMutableArray* dataArray;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Pasteboard::Pasteboard (NSPasteboard* pb)
|
||||
: pb (pb)
|
||||
, nbItems (0)
|
||||
, stringsAreFiles (false)
|
||||
, dataArray (nullptr)
|
||||
{
|
||||
NSArray *supportedTypes = [NSArray arrayWithObjects: NSStringPboardType, nil];
|
||||
NSString* hasString = [pb availableTypeFromArray: supportedTypes];
|
||||
if (hasString)
|
||||
{
|
||||
nbItems = 1;
|
||||
NSString* unicodeText = [pb stringForType:NSStringPboardType];
|
||||
if (unicodeText)
|
||||
{
|
||||
strings.emplace_back ([unicodeText UTF8String]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
supportedTypes = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
|
||||
NSString* hasFilenames = [pb availableTypeFromArray: supportedTypes];
|
||||
if (hasFilenames)
|
||||
{
|
||||
stringsAreFiles = true;
|
||||
NSArray* fileNames = [pb propertyListForType:hasFilenames];
|
||||
nbItems = static_cast<uint32_t> ([fileNames count]);
|
||||
for (uint32_t i = 0; i < nbItems; i++)
|
||||
{
|
||||
NSString* str = [fileNames objectAtIndex:i];
|
||||
if (str)
|
||||
strings.emplace_back ([str UTF8String]);
|
||||
}
|
||||
}
|
||||
else if ([pb availableTypeFromArray:[NSArray arrayWithObject:NSColorPboardType]])
|
||||
{
|
||||
NSColor* nsColor = [NSColor colorFromPasteboard:pb];
|
||||
if (nsColor)
|
||||
{
|
||||
nsColor = [nsColor colorUsingColorSpace:[[[NSColorSpace alloc] initWithCGColorSpace:GetCGColorSpace ()] autorelease]];
|
||||
int32_t red = static_cast<int32_t> ([nsColor redComponent] * 255.);
|
||||
int32_t green = static_cast<int32_t> ([nsColor greenComponent] * 255.);
|
||||
int32_t blue = static_cast<int32_t> ([nsColor blueComponent] * 255.);
|
||||
int32_t alpha = static_cast<int32_t> ([nsColor alphaComponent] * 255.);
|
||||
char str[10];
|
||||
snprintf (str, 10, "#%02x%02x%02x%02x", red, green, blue, alpha);
|
||||
strings.emplace_back (str);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nbItems = static_cast<uint32_t> ([[pb types] count]);
|
||||
dataArray = [[NSMutableArray alloc] initWithCapacity:nbItems];
|
||||
for (uint32_t i = 0; i < nbItems; i++)
|
||||
{
|
||||
NSData* nsData = [pb dataForType:[[pb types] objectAtIndex:i]];
|
||||
[dataArray addObject:nsData];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Pasteboard::~Pasteboard () noexcept
|
||||
{
|
||||
if (dataArray)
|
||||
[dataArray release];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t Pasteboard::getCount () const
|
||||
{
|
||||
return nbItems;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t Pasteboard::getDataSize (uint32_t index) const
|
||||
{
|
||||
if (dataArray)
|
||||
{
|
||||
return static_cast<uint32_t> ([[dataArray objectAtIndex:index] length]);
|
||||
}
|
||||
if (index < strings.size ())
|
||||
{
|
||||
return static_cast<uint32_t> (strings[index].length ());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Pasteboard::Type Pasteboard::getDataType (uint32_t index) const
|
||||
{
|
||||
if (dataArray)
|
||||
return kBinary;
|
||||
else if (index < strings.size ())
|
||||
{
|
||||
if (stringsAreFiles)
|
||||
return kFilePath;
|
||||
return kText;
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t Pasteboard::getData (uint32_t index, const void*& buffer, Pasteboard::Type& type) const
|
||||
{
|
||||
if (dataArray)
|
||||
{
|
||||
buffer = [[dataArray objectAtIndex:index] bytes];
|
||||
type = kBinary;
|
||||
return static_cast<uint32_t> ([[dataArray objectAtIndex:index] length]);
|
||||
}
|
||||
if (index < strings.size ())
|
||||
{
|
||||
buffer = strings[index].c_str ();
|
||||
type = stringsAreFiles ? kFilePath : kText;
|
||||
return static_cast<uint32_t> (strings[index].length ());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IDataPackage> createClipboardDataPackage ()
|
||||
{
|
||||
return makeOwned<Pasteboard> ([NSPasteboard generalPasteboard]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SharedPointer<IDataPackage> createDragDataPackage (NSPasteboard* pasteboard)
|
||||
{
|
||||
return makeOwned<Pasteboard> (pasteboard);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void setClipboard (const SharedPointer<IDataPackage>& dataSource)
|
||||
{
|
||||
NSPasteboard* pb = [NSPasteboard generalPasteboard];
|
||||
if (dataSource)
|
||||
{
|
||||
[pb clearContents];
|
||||
|
||||
uint32_t nbItems = dataSource->getCount ();
|
||||
NSMutableArray* fileArray = nullptr;
|
||||
IDataPackage::Type type;
|
||||
const void* data;
|
||||
uint32_t length;
|
||||
for (uint32_t i = 0; i < nbItems; i++)
|
||||
{
|
||||
if ((length = dataSource->getData (i, data, type)) > 0)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case IDataPackage::kBinary:
|
||||
{
|
||||
[pb declareTypes:[NSArray arrayWithObject:[NSString stringWithCString:MacClipboard::getPasteboardBinaryType () encoding:NSASCIIStringEncoding]] owner:nil];
|
||||
[pb setData:[NSData dataWithBytes:data length:length] forType:[NSString stringWithCString:MacClipboard::getPasteboardBinaryType () encoding:NSASCIIStringEncoding]];
|
||||
return;
|
||||
}
|
||||
case IDataPackage::kText:
|
||||
{
|
||||
[pb declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
|
||||
[pb setString:[[[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding] autorelease] forType:NSPasteboardTypeString];
|
||||
return;
|
||||
}
|
||||
case IDataPackage::kFilePath:
|
||||
{
|
||||
if (fileArray == nullptr)
|
||||
fileArray = [[[NSMutableArray alloc] init] autorelease];
|
||||
auto fileStr =
|
||||
[NSString stringWithUTF8String:static_cast<const char*> (data)];
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
|
||||
[fileArray addObject:[NSURL fileURLWithPath:fileStr]];
|
||||
#else
|
||||
[fileArray addObject:fileStr];
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case IDataPackage::kError:
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fileArray)
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
|
||||
[pb writeObjects:fileArray];
|
||||
#else
|
||||
[pb declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
|
||||
[pb setPropertyList:fileArray forType:NSFilenamesPboardType];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[pb clearContents];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char* getPasteboardBinaryType ()
|
||||
{
|
||||
return "net.sourceforge.vstgui.pasteboard.type.binary";
|
||||
}
|
||||
|
||||
}} // namespaces
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../platformfactory.h"
|
||||
|
||||
typedef struct __CFBundle* CFBundleRef;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class MacFactory final : public IPlatformFactory
|
||||
{
|
||||
public:
|
||||
MacFactory (CFBundleRef bundle);
|
||||
~MacFactory () noexcept override;
|
||||
|
||||
CFBundleRef getBundle () const noexcept;
|
||||
|
||||
void setUseAsynchronousLayerDrawing (bool state) const noexcept;
|
||||
bool getUseAsynchronousLayerDrawing () const noexcept;
|
||||
|
||||
void enableVisualizeRedrawAreas (bool state) const noexcept;
|
||||
bool enableVisualizeRedrawAreas () const noexcept;
|
||||
|
||||
void finalize () noexcept final;
|
||||
/** Return platform ticks (millisecond resolution)
|
||||
* @return ticks
|
||||
*/
|
||||
uint64_t getTicks () const noexcept final;
|
||||
|
||||
/** Create a platform frame object
|
||||
* @param frame callback
|
||||
* @param size size
|
||||
* @param parent platform parent object
|
||||
* @param parentType type of platform parent object
|
||||
* @param config optional config object
|
||||
* @return platform frame or nullptr on failure
|
||||
*/
|
||||
PlatformFramePtr createFrame (IPlatformFrameCallback* frame, const CRect& size, void* parent,
|
||||
PlatformType parentType,
|
||||
IPlatformFrameConfig* config = nullptr) const noexcept final;
|
||||
|
||||
/** Create a platform font object
|
||||
* @param name name of the font
|
||||
* @param size font size
|
||||
* @param style font style
|
||||
* @return platform font or nullptr on failure
|
||||
*/
|
||||
PlatformFontPtr createFont (const UTF8String& name, const CCoord& size,
|
||||
const int32_t& style) const noexcept final;
|
||||
/** Query all platform font families
|
||||
* @param callback callback called for every font
|
||||
* @return true on success
|
||||
*/
|
||||
bool getAllFontFamilies (const FontFamilyCallback& callback) const noexcept final;
|
||||
|
||||
/** Create an empty platform bitmap object
|
||||
* @param size size of the bitmap
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmap (const CPoint& size) const noexcept final;
|
||||
/** Create a platform bitmap object from a resource description
|
||||
* @param desc description where to find the bitmap
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmap (const CResourceDescription& desc) const noexcept final;
|
||||
/** Create a platform bitmap object from a file
|
||||
* @param absolutePath the absolute path of the bitmap file location
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmapFromPath (UTF8StringPtr absolutePath) const noexcept final;
|
||||
/** Create a platform bitmap object from memory
|
||||
* @param ptr memory location
|
||||
* @param memSize memory size
|
||||
* @return platform bitmap or nullptr on failure
|
||||
*/
|
||||
PlatformBitmapPtr createBitmapFromMemory (const void* ptr,
|
||||
uint32_t memSize) const noexcept final;
|
||||
/** Create a memory representation of the platform bitmap in PNG format.
|
||||
* @param bitmap the platform bitmap object
|
||||
* @return memory buffer containing the PNG representation of the bitmap
|
||||
*/
|
||||
PNGBitmapBuffer
|
||||
createBitmapMemoryPNGRepresentation (const PlatformBitmapPtr& bitmap) const noexcept final;
|
||||
|
||||
/** Create a platform resource input stream
|
||||
* @param desc description where to find the file to open
|
||||
* @return platform resource input stream or nullptr if not found
|
||||
*/
|
||||
PlatformResourceInputStreamPtr
|
||||
createResourceInputStream (const CResourceDescription& desc) const noexcept final;
|
||||
|
||||
/** Create a platform string object
|
||||
* @param utf8String optional initial UTF-8 encoded string
|
||||
* @return platform string object or nullptr on failure
|
||||
*/
|
||||
PlatformStringPtr createString (UTF8StringPtr utf8String = nullptr) const noexcept final;
|
||||
|
||||
/** Create a platform timer object
|
||||
* @param callback timer callback object
|
||||
* @return platform timer object or nullptr on failure
|
||||
*/
|
||||
PlatformTimerPtr createTimer (IPlatformTimerCallback* callback) const noexcept final;
|
||||
|
||||
/** Set clipboard data
|
||||
* @param data data to put on the clipboard
|
||||
* @return true on success
|
||||
*/
|
||||
bool setClipboard (const DataPackagePtr& data) const noexcept final;
|
||||
|
||||
/** Get clipboard data
|
||||
* @return data package pointer
|
||||
*/
|
||||
DataPackagePtr getClipboard () const noexcept final;
|
||||
|
||||
/** Create a platform gradient object
|
||||
* @return platform gradient object or nullptr on failure
|
||||
*/
|
||||
PlatformGradientPtr createGradient () const noexcept final;
|
||||
|
||||
/** Create a platform file selector
|
||||
* @param style file selector style
|
||||
* @param frame frame
|
||||
* @return platform file selector or nullptr on failure
|
||||
*/
|
||||
PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style,
|
||||
IPlatformFrame* frame) const noexcept final;
|
||||
|
||||
/** Get the graphics device factory
|
||||
*
|
||||
* @return platform graphics device factory
|
||||
*/
|
||||
const IPlatformGraphicsDeviceFactory& getGraphicsDeviceFactory () const noexcept final;
|
||||
|
||||
const IPlatformTaskExecutor& getTaskExecutor () const noexcept final;
|
||||
bool replaceTaskExecutor (const ReplaceTaskExecFunc& replaceFunc) const noexcept final;
|
||||
|
||||
const LinuxFactory* asLinuxFactory () const noexcept final;
|
||||
const MacFactory* asMacFactory () const noexcept final;
|
||||
const Win32Factory* asWin32Factory () const noexcept final;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,279 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "../common/fileresourceinputstream.h"
|
||||
#include "../iplatformfont.h"
|
||||
#include "../iplatformframe.h"
|
||||
#include "../iplatformframecallback.h"
|
||||
#include "../iplatformresourceinputstream.h"
|
||||
#include "../iplatformstring.h"
|
||||
#include "../iplatformtimer.h"
|
||||
#include "cfontmac.h"
|
||||
#include "cgbitmap.h"
|
||||
#include "quartzgraphicspath.h"
|
||||
#include "coregraphicsdevicecontext.h"
|
||||
#include "cocoa/nsviewframe.h"
|
||||
#include "ios/uiviewframe.h"
|
||||
#include "macclipboard.h"
|
||||
#include "mactaskexecutor.h"
|
||||
#include "macfactory.h"
|
||||
#include "macfileselector.h"
|
||||
#include "macglobals.h"
|
||||
#include "macstring.h"
|
||||
#include "mactimer.h"
|
||||
#include <list>
|
||||
#include <mach/mach_time.h>
|
||||
#include <memory>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct MacFactory::Impl
|
||||
{
|
||||
struct mach_timebase_info timebaseInfo;
|
||||
CFBundleRef bundle {nullptr};
|
||||
bool useAsynchronousLayerDrawing {true};
|
||||
bool visualizeRedrawAreas {false};
|
||||
CoreGraphicsDeviceFactory graphicsDeviceFactory;
|
||||
PlatformTaskExecutorPtr taskExecutor;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
MacFactory::MacFactory (CFBundleRef bundle)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
impl->bundle = bundle;
|
||||
mach_timebase_info (&impl->timebaseInfo);
|
||||
impl->taskExecutor = std::make_unique<MacTaskExecutor> ();
|
||||
}
|
||||
|
||||
MacFactory::~MacFactory () noexcept = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MacFactory::finalize () noexcept { impl->taskExecutor->waitAllTasksExecuted (); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CFBundleRef MacFactory::getBundle () const noexcept
|
||||
{
|
||||
return impl->bundle;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void MacFactory::setUseAsynchronousLayerDrawing (bool state) const noexcept
|
||||
{
|
||||
impl->useAsynchronousLayerDrawing = state;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool MacFactory::getUseAsynchronousLayerDrawing () const noexcept
|
||||
{
|
||||
return impl->useAsynchronousLayerDrawing;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void MacFactory::enableVisualizeRedrawAreas (bool state) const noexcept
|
||||
{
|
||||
impl->visualizeRedrawAreas = state;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool MacFactory::enableVisualizeRedrawAreas () const noexcept
|
||||
{
|
||||
return impl->visualizeRedrawAreas;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint64_t MacFactory::getTicks () const noexcept
|
||||
{
|
||||
uint64_t absTime = mach_absolute_time ();
|
||||
auto d = ((absTime * impl->timebaseInfo.numer) / impl->timebaseInfo.denom) / 1000000;
|
||||
return d;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFramePtr MacFactory::createFrame (IPlatformFrameCallback* frame, const CRect& size,
|
||||
void* parent, PlatformType parentType,
|
||||
IPlatformFrameConfig* config) const noexcept
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
return makeOwned<UIViewFrame> (frame, size, (__bridge UIView*)parent);
|
||||
#else
|
||||
return makeOwned<NSViewFrame> (frame, size, reinterpret_cast<NSView*> (parent), config);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFontPtr MacFactory::createFont (const UTF8String& name, const CCoord& size,
|
||||
const int32_t& style) const noexcept
|
||||
{
|
||||
auto font = makeOwned<CoreTextFont> (name, size, style);
|
||||
if (font->getFontRef ())
|
||||
return std::move (font);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool MacFactory::getAllFontFamilies (const FontFamilyCallback& callback) const noexcept
|
||||
{
|
||||
return CoreTextFont::getAllFontFamilies (callback);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr MacFactory::createBitmap (const CPoint& size) const noexcept
|
||||
{
|
||||
return CGBitmap::create (&const_cast<CPoint&> (size));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr MacFactory::createBitmap (const CResourceDescription& desc) const noexcept
|
||||
{
|
||||
if (auto bitmap = makeOwned<CGBitmap> ())
|
||||
{
|
||||
if (bitmap->load (desc))
|
||||
return bitmap;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr MacFactory::createBitmapFromPath (UTF8StringPtr absolutePath) const noexcept
|
||||
{
|
||||
return CGBitmap::createFromPath (absolutePath);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformBitmapPtr MacFactory::createBitmapFromMemory (const void* ptr,
|
||||
uint32_t memSize) const noexcept
|
||||
{
|
||||
return CGBitmap::createFromMemory (ptr, memSize);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PNGBitmapBuffer
|
||||
MacFactory::createBitmapMemoryPNGRepresentation (const PlatformBitmapPtr& bitmap) const noexcept
|
||||
{
|
||||
return CGBitmap::createMemoryPNGRepresentation (bitmap);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformResourceInputStreamPtr
|
||||
MacFactory::createResourceInputStream (const CResourceDescription& desc) const noexcept
|
||||
{
|
||||
if (desc.type == CResourceDescription::kIntegerType)
|
||||
return nullptr;
|
||||
if (auto bundle = getBundleRef ())
|
||||
{
|
||||
PlatformResourceInputStreamPtr result;
|
||||
CFStringRef cfStr = CFStringCreateWithCString (nullptr, desc.u.name, kCFStringEncodingUTF8);
|
||||
if (cfStr)
|
||||
{
|
||||
CFURLRef url = CFBundleCopyResourceURL (bundle, cfStr, nullptr, nullptr);
|
||||
if (url)
|
||||
{
|
||||
char filePath[PATH_MAX];
|
||||
if (CFURLGetFileSystemRepresentation (url, true, (UInt8*)filePath, PATH_MAX))
|
||||
{
|
||||
result = FileResourceInputStream::create (filePath);
|
||||
}
|
||||
CFRelease (url);
|
||||
}
|
||||
CFRelease (cfStr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformStringPtr MacFactory::createString (UTF8StringPtr utf8String) const noexcept
|
||||
{
|
||||
return makeOwned<MacString> (utf8String);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformTimerPtr MacFactory::createTimer (IPlatformTimerCallback* callback) const noexcept
|
||||
{
|
||||
return makeOwned<MacTimer> (callback);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool MacFactory::setClipboard (const DataPackagePtr& data) const noexcept
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
return false;
|
||||
#else
|
||||
MacClipboard::setClipboard (data);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
auto MacFactory::getClipboard () const noexcept -> DataPackagePtr
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
return nullptr;
|
||||
#else
|
||||
return MacClipboard::createClipboardDataPackage ();
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformGradientPtr MacFactory::createGradient () const noexcept
|
||||
{
|
||||
return std::make_unique<QuartzGradient> ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFileSelectorPtr MacFactory::createFileSelector (PlatformFileSelectorStyle style,
|
||||
IPlatformFrame* frame) const noexcept
|
||||
{
|
||||
#if !TARGET_OS_IPHONE
|
||||
auto nsViewFrame = dynamic_cast<NSViewFrame*> (frame);
|
||||
return createCocoaFileSelector (style, nsViewFrame);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const IPlatformGraphicsDeviceFactory& MacFactory::getGraphicsDeviceFactory () const noexcept
|
||||
{
|
||||
return impl->graphicsDeviceFactory;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const IPlatformTaskExecutor& MacFactory::getTaskExecutor () const noexcept
|
||||
{
|
||||
return *impl->taskExecutor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool MacFactory::replaceTaskExecutor (const ReplaceTaskExecFunc& replaceFunc) const noexcept
|
||||
{
|
||||
if (!replaceFunc)
|
||||
return false;
|
||||
impl->taskExecutor = replaceFunc (std::move (impl->taskExecutor));
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const LinuxFactory* MacFactory::asLinuxFactory () const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const MacFactory* MacFactory::asMacFactory () const noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Win32Factory* MacFactory::asWin32Factory () const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,21 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
|
||||
#include "../iplatformfileselector.h"
|
||||
#include "cocoa/nsviewframe.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFileSelectorPtr createCocoaFileSelector (PlatformFileSelectorStyle style,
|
||||
NSViewFrame* frame);
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
#import "../../cstring.h"
|
||||
#import "cocoa/cocoahelpers.h"
|
||||
#import "macfileselector.h"
|
||||
#import "macstring.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
|
||||
#if defined(VSTGUI_USE_OBJC_UTTYPE)
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
#else
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
class CocoaFileSelector
|
||||
: public IPlatformFileSelector
|
||||
, public std::enable_shared_from_this<CocoaFileSelector>
|
||||
{
|
||||
public:
|
||||
CocoaFileSelector (PlatformFileSelectorStyle style, NSViewFrame* frame);
|
||||
~CocoaFileSelector () override = default;
|
||||
|
||||
bool run (const PlatformFileSelectorConfig& config) override;
|
||||
bool cancel () override;
|
||||
|
||||
void openPanelDidEnd (NSSavePanel* panel, NSInteger resultCode);
|
||||
|
||||
protected:
|
||||
static void initClass ();
|
||||
|
||||
void setupInitalDir (const PlatformFileSelectorConfig& config);
|
||||
|
||||
PlatformFileSelectorStyle style;
|
||||
NSViewFrame* frame {nullptr};
|
||||
NSSavePanel* savePanel {nullptr};
|
||||
PlatformFileSelectorConfig::CallbackFunc callback;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatformFileSelectorPtr createCocoaFileSelector (PlatformFileSelectorStyle style,
|
||||
NSViewFrame* frame)
|
||||
{
|
||||
return std::make_shared<CocoaFileSelector> (style, frame);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaFileSelector::initClass ()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CocoaFileSelector::CocoaFileSelector (PlatformFileSelectorStyle style, NSViewFrame* frame)
|
||||
: style (style), frame (frame)
|
||||
{
|
||||
initClass ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaFileSelector::openPanelDidEnd (NSSavePanel* panel, NSInteger res)
|
||||
{
|
||||
std::vector<UTF8String> result;
|
||||
if (res == NSModalResponseOK)
|
||||
{
|
||||
if (style == PlatformFileSelectorStyle::SelectSaveFile)
|
||||
{
|
||||
NSURL* url = [panel URL];
|
||||
const char* utf8Path = url ? [[url path] UTF8String] : nullptr;
|
||||
if (utf8Path)
|
||||
{
|
||||
result.emplace_back (utf8Path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSOpenPanel* openPanel = (NSOpenPanel*)panel;
|
||||
NSArray* urls = [openPanel URLs];
|
||||
for (NSUInteger i = 0; i < [urls count]; i++)
|
||||
{
|
||||
NSURL* url = [urls objectAtIndex:i];
|
||||
if (url == nullptr || [url path] == nullptr)
|
||||
continue;
|
||||
const char* utf8Path = [[url path] UTF8String];
|
||||
if (utf8Path)
|
||||
{
|
||||
result.emplace_back (utf8Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callback)
|
||||
callback (std::move (result));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaFileSelector::cancel ()
|
||||
{
|
||||
if (savePanel)
|
||||
{
|
||||
[savePanel cancel:nil];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CocoaFileSelector::run (const PlatformFileSelectorConfig& config)
|
||||
{
|
||||
NSWindow* parentWindow = nil;
|
||||
|
||||
if (!hasBit (config.flags, PlatformFileSelectorFlags::RunModal) && frame)
|
||||
parentWindow = [(frame->getNSView ()) window];
|
||||
|
||||
callback = config.doneCallback;
|
||||
|
||||
NSOpenPanel* openPanel = nil;
|
||||
NSMutableArray* typesArray = nil;
|
||||
if (config.extensions.empty () == false)
|
||||
{
|
||||
typesArray = [[[NSMutableArray alloc] init] autorelease];
|
||||
for (auto& ext : config.extensions)
|
||||
{
|
||||
#ifdef VSTGUI_USE_OBJC_UTTYPE
|
||||
UTType* uti = nullptr;
|
||||
if (ext.uti.empty () == false)
|
||||
uti = [UTType typeWithIdentifier:fromUTF8String<NSString*> (ext.uti)];
|
||||
if (uti == nullptr && ext.mimeType.empty() == false)
|
||||
uti = [UTType typeWithMIMEType:fromUTF8String<NSString*> (ext.mimeType)];
|
||||
if (uti == nullptr && ext.extension.empty () == false)
|
||||
uti = [UTType typeWithFilenameExtension:fromUTF8String<NSString*> (ext.extension)];
|
||||
if (uti)
|
||||
[typesArray addObject:uti];
|
||||
#else
|
||||
NSString* uti = nullptr;
|
||||
if (ext.uti.empty () == false)
|
||||
uti = [fromUTF8String<NSString*> (ext.uti) retain];
|
||||
if (uti == nullptr && ext.mimeType.empty () == false)
|
||||
uti = (NSString*)UTTypeCreatePreferredIdentifierForTag (
|
||||
kUTTagClassMIMEType, fromUTF8String<CFStringRef> (ext.mimeType), kUTTypeData);
|
||||
if (uti == nullptr && ext.macType)
|
||||
{
|
||||
NSString* osType =
|
||||
(NSString*)UTCreateStringForOSType (static_cast<OSType> (ext.macType));
|
||||
if (osType)
|
||||
{
|
||||
uti = (NSString*)UTTypeCreatePreferredIdentifierForTag (
|
||||
kUTTagClassOSType, (CFStringRef)osType, kUTTypeData);
|
||||
[osType release];
|
||||
}
|
||||
}
|
||||
if (uti == nullptr && ext.extension.empty () == false)
|
||||
uti = [fromUTF8String<NSString*> (ext.extension) retain];
|
||||
if (uti)
|
||||
{
|
||||
[typesArray addObject:uti];
|
||||
[uti release];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (style == PlatformFileSelectorStyle::SelectSaveFile)
|
||||
{
|
||||
savePanel = [NSSavePanel savePanel];
|
||||
if (typesArray)
|
||||
{
|
||||
#ifdef VSTGUI_USE_OBJC_UTTYPE
|
||||
[savePanel setAllowedContentTypes:typesArray];
|
||||
#else
|
||||
[savePanel setAllowedFileTypes:typesArray];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
savePanel = openPanel = [NSOpenPanel openPanel];
|
||||
if (style == PlatformFileSelectorStyle::SelectFile)
|
||||
{
|
||||
bool allowMultiFileSelection =
|
||||
hasBit (config.flags, PlatformFileSelectorFlags::MultiFileSelection);
|
||||
[openPanel setAllowsMultipleSelection:allowMultiFileSelection ? YES : NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[openPanel setCanChooseDirectories:YES];
|
||||
}
|
||||
}
|
||||
if (!config.title.empty () && savePanel)
|
||||
{
|
||||
#if 0 // Apple broke this again with macOS 12. Disable this now and always use the message to
|
||||
// display the title.
|
||||
if (@available (macOS 11, *))
|
||||
{
|
||||
if (parentWindow)
|
||||
[savePanel setMessage:fromUTF8String<NSString*> (config.title)];
|
||||
else
|
||||
[savePanel setTitle:fromUTF8String<NSString*> (config.title)];
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (@available (macOS 10.11, *))
|
||||
{
|
||||
[savePanel setMessage:fromUTF8String<NSString*> (config.title)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[savePanel setTitle:fromUTF8String<NSString*> (config.title)];
|
||||
}
|
||||
}
|
||||
if (openPanel)
|
||||
{
|
||||
#ifdef VSTGUI_USE_OBJC_UTTYPE
|
||||
openPanel.allowedContentTypes = typesArray;
|
||||
#else
|
||||
openPanel.allowedFileTypes = typesArray;
|
||||
#endif
|
||||
if (parentWindow)
|
||||
{
|
||||
setupInitalDir (config);
|
||||
auto This = shared_from_this ();
|
||||
[openPanel beginSheetModalForWindow:parentWindow
|
||||
completionHandler:^(NSInteger result) {
|
||||
This->openPanelDidEnd (openPanel, result);
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
setupInitalDir (config);
|
||||
NSInteger res = [openPanel runModal];
|
||||
openPanelDidEnd (openPanel, res);
|
||||
return res == NSModalResponseOK;
|
||||
}
|
||||
}
|
||||
else if (savePanel)
|
||||
{
|
||||
#ifdef VSTGUI_USE_OBJC_UTTYPE
|
||||
savePanel.allowedContentTypes = typesArray;
|
||||
#else
|
||||
savePanel.allowedFileTypes = typesArray;
|
||||
#endif
|
||||
if (parentWindow)
|
||||
{
|
||||
setupInitalDir (config);
|
||||
auto This = shared_from_this ();
|
||||
[savePanel beginSheetModalForWindow:parentWindow
|
||||
completionHandler:^(NSInteger result) {
|
||||
This->openPanelDidEnd (savePanel, result);
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
setupInitalDir (config);
|
||||
NSInteger res = [savePanel runModal];
|
||||
openPanelDidEnd (savePanel, res);
|
||||
return res == NSModalResponseOK;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CocoaFileSelector::setupInitalDir (const PlatformFileSelectorConfig& config)
|
||||
{
|
||||
if (!config.initialPath.empty ())
|
||||
{
|
||||
NSURL* dirURL = [NSURL fileURLWithPath:fromUTF8String<NSString*> (config.initialPath)];
|
||||
NSNumber* isDir;
|
||||
if ([dirURL getResourceValue:&isDir forKey:NSURLIsDirectoryKey error:nil])
|
||||
{
|
||||
if ([isDir boolValue] == NO)
|
||||
{
|
||||
savePanel.nameFieldStringValue =
|
||||
[[dirURL.path lastPathComponent] stringByDeletingPathExtension];
|
||||
dirURL = [NSURL fileURLWithPath:[dirURL.path stringByDeletingLastPathComponent]];
|
||||
}
|
||||
savePanel.directoryURL = dirURL;
|
||||
}
|
||||
}
|
||||
if (!config.defaultSaveName.empty ())
|
||||
{
|
||||
savePanel.nameFieldStringValue = fromUTF8String<NSString*> (config.defaultSaveName);
|
||||
}
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,131 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "macglobals.h"
|
||||
|
||||
#if MAC
|
||||
#include "../../cframe.h"
|
||||
#include "../../ccolor.h"
|
||||
#include "macfactory.h"
|
||||
#include "../iplatformframe.h"
|
||||
#include "../common/fileresourceinputstream.h"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGColorMap
|
||||
{
|
||||
public:
|
||||
struct ColorHash
|
||||
{
|
||||
size_t operator() (const CColor& c) const
|
||||
{
|
||||
return static_cast<size_t> (c.red | (c.green << 8) | (c.blue << 16) | (c.alpha << 24));
|
||||
}
|
||||
};
|
||||
|
||||
using Map = std::unordered_map<CColor, CGColorRef, ColorHash>;
|
||||
|
||||
static CGColorMap& instance ()
|
||||
{
|
||||
static CGColorMap gInstance;
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
~CGColorMap () noexcept
|
||||
{
|
||||
std::for_each (map.begin (), map.end (), [] (auto& el) { CFRelease (el.second); });
|
||||
}
|
||||
|
||||
CGColorRef getColor (const CColor& color)
|
||||
{
|
||||
mutex.lock ();
|
||||
auto it = map.find (color);
|
||||
if (it != map.end ())
|
||||
{
|
||||
auto result = it->second;
|
||||
mutex.unlock ();
|
||||
return result;
|
||||
}
|
||||
const CGFloat components[] = {color.normRed<CGFloat> (), color.normGreen<CGFloat> (),
|
||||
color.normBlue<CGFloat> (), color.normAlpha<CGFloat> ()};
|
||||
auto result = CGColorCreate (GetCGColorSpace (), components);
|
||||
map.emplace (color, result);
|
||||
mutex.unlock ();
|
||||
return result;
|
||||
}
|
||||
|
||||
Map map;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGColorRef getCGColor (const CColor& color) { return CGColorMap::instance ().getColor (color); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class GenericMacColorSpace
|
||||
{
|
||||
public:
|
||||
GenericMacColorSpace ()
|
||||
{
|
||||
colorspace = CreateMainDisplayColorSpace ();
|
||||
}
|
||||
|
||||
~GenericMacColorSpace ()
|
||||
{
|
||||
CGColorSpaceRelease (colorspace);
|
||||
}
|
||||
|
||||
static GenericMacColorSpace& instance ()
|
||||
{
|
||||
static GenericMacColorSpace gInstance;
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static CGColorSpaceRef CreateMainDisplayColorSpace ()
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
return CGColorSpaceCreateDeviceRGB ();
|
||||
|
||||
#else
|
||||
ColorSyncProfileRef csProfileRef = ColorSyncProfileCreateWithDisplayID (CGMainDisplayID ());
|
||||
if (csProfileRef)
|
||||
{
|
||||
CGColorSpaceRef colorSpace = {};
|
||||
#if defined(MAC_OS_VERSION_12_0) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0)
|
||||
if (__builtin_available (macOS 12.0, *))
|
||||
{
|
||||
colorSpace = CGColorSpaceCreateWithColorSyncProfile (csProfileRef, nullptr);
|
||||
}
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0)
|
||||
else
|
||||
{
|
||||
colorSpace = CGColorSpaceCreateWithPlatformColorSpace (csProfileRef);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
colorSpace = CGColorSpaceCreateWithPlatformColorSpace (csProfileRef);
|
||||
#endif
|
||||
CFRelease (csProfileRef);
|
||||
return colorSpace;
|
||||
}
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorspace;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CGColorSpaceRef GetCGColorSpace ()
|
||||
{
|
||||
return GenericMacColorSpace::instance ().colorspace;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
#endif // MAC
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user