Skip to content

Instantly share code, notes, and snippets.

@FrankHB
Last active December 20, 2015 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FrankHB/6097283 to your computer and use it in GitHub Desktop.
Save FrankHB/6097283 to your computer and use it in GitHub Desktop.
YFramework base test.
#define YB_T_NO_MATH 1
//#define YB_T_NO_IOSTREAM 1
//#define YB_T_NO_TIMING 1
#define YTEST_B 4
#include "../../Temp/test.h"
#include <ysbuild.h>
#include <Helper/HostWindow.h>
#include <windows.h>
#include "../HelperEx/Shells.h"
#include <NPL/SContext.h>
#include <YSLib/Core/ValueNode.h>
#include <sstream>
#define TEST_GUI 1
#define TEST_IMAGE 0
#include <iostream>
#include <stdexcept>
#include <YCLib/COM.h>
#include <shlobj.h>
#include <shlwapi.h>
//#include <Windows.Foundation.h>
#include <vector>
#include <string>
#include <algorithm>
#include <ystdex/functional.hpp>
#include <ystdex/iterator.hpp>
#if TEST_IMAGE
# include <YSLib/Adaptor/Image.h>
#endif
using namespace std;
using namespace placeholders;
using namespace ystdex;
using namespace ytest::timing;
using namespace NPL;
using namespace YSLib;
using namespace Drawing;
#if 0
using YSLib::Timers::HighResolutionClock;
#endif
typedef std::chrono::steady_clock HighResolutionClock;
using UI::Widget;
using UI::Button;
using Host::Window;
using Host::NativeWindowHandle;
using Host::HostRenderer;
#if TEST_GUI == 1
class TestWnd : public UI::Window
{
public:
Button b1, b2;
Host::Window* p_wnd;
Widget w1;
#if TEST_IMAGE
HBitmap pixmap;
#endif
TestWnd(const Rect& r)
: UI::Window(r),
b1({5, 5, 80, 32}), b2({90, 5, 80, 32}), p_wnd(),
w1({40, 40, 640, 480})
#if TEST_IMAGE
, pixmap(u8R"(T:\a.png)")
#endif
{
using namespace UI;
#if TEST_IMAGE
auto buf(ImageCodec::Convert(pixmap));
auto p_img(ystdex::make_shared<Image>(buf.GetBufferPtr(),
buf.GetWidth(), buf.GetHeight()));
printf("bmp size:%s\n", to_string(buf.GetSize()).c_str());
SetSizeOf(w1, buf.GetSize());
printf("wgt size:%s\n", to_string(GetSizeOf(w1)).c_str());
#endif
*this += b1,
*this += b2,
*this += w1,
yunseq(
Background = SolidBrush(ColorSpace::Red),
b1.Text = "改变大小",
b2.Text = "还原大小",
FetchEvent<Click>(b1) += std::bind(&TestWnd::AdjustClient,
this, Size(640, 480), std::placeholders::_1),
FetchEvent<Click>(b2) += std::bind(&TestWnd::AdjustClient,
this, Size(480, 320), std::placeholders::_1),
#if TEST_IMAGE
FetchEvent<Paint>(w1) += ImageBrush(std::move(p_img)),
#endif
w1.Background = SolidBrush(ColorSpace::Green)
);
}
private:
void
AdjustClient(const Size& s, UI::CursorEventArgs&&)
{
if(p_wnd)
p_wnd->ResizeClient(s);
}
};
#endif
#if TEST_GUI == 2
namespace platform_ex
{
inline std::string
ToANSI(const std::wstring& str, int cp = 936)
{
return WCSToMBCS(str.c_str(), str.length(), cp);
}
string
SToMBCS(String str, int cp)
{
return platform_ex::WCSToMBCS(reinterpret_cast<const wchar_t*>(str.c_str()),
str.length(), cp);
}
string
UTF8ToGBK(string str)
{
return SToMBCS(str, 936);
}
using ystdex::indirect_input_iterator;
typedef ::IShellFolder IShellFolder;
typedef ::IEnumIDList IEnumIDList;
typedef COMPtr<IShellFolder> ShellFolderPtr;
typedef COMPtr<IEnumIDList> EnumIDListPtr;
// 关于内存管理和 OLE32 库的链接:
// http://blogs.msdn.com/b/oldnewthing/archive/2004/07/05/173226.aspx
class YF_API ShellPathFailure : public YSLib::LoggedEvent
{
public:
ShellPathFailure(const std::string& msg) ynothrow
: LoggedEvent(msg)
{}
};
class ShellPath
{
private:
::LPITEMIDLIST p_idl;
public:
ShellPath(::LPITEMIDLIST p = nullptr) ynothrow
: p_idl(p)
{}
ShellPath(const ShellPath& pth)
: p_idl(::ILClone(pth.p_idl))
{
if(pth.p_idl && !p_idl)
throw std::bad_alloc();
}
ShellPath(ShellPath&& pth) ynothrow
: p_idl(pth.p_idl)
{
pth.p_idl = nullptr;
}
~ShellPath() ynothrow
{
::CoTaskMemFree(p_idl);
}
ShellPath&
operator=(ShellPath&& pth) ynothrow
{
std::swap(p_idl, pth.p_idl);
return *this;
}
ShellPath&
operator+=(const ::SHITEMID& id) ynothrow
{
if(!::ILAppendID(p_idl, &id, TRUE))
throw ShellPathFailure("Appending ID failed.");
return *this;
}
::ITEMIDLIST&
operator*() const ynothrow
{
yconstraint(p_idl);
return *p_idl;
}
explicit DefCvt(const ynothrow, bool, bool(p_idl))
DefGetter(const ynothrow, ::LPITEMIDLIST, , p_idl)
DefGetter(const, ::ITEMIDLIST&, Object, EnsureNonNull(p_idl), *p_idl)
DefGetter(ynothrow, ::LPITEMIDLIST&, Ref, p_idl)
};
ShellPath
LocateFolder(int csidl, ::HANDLE token = nullptr)
ythrow(COMException)
{
::LPITEMIDLIST p{};
CheckHResult(::SHGetFolderLocation(nullptr, csidl, token, 0, &p));
return p;
}
template<typename... _tParams>
ShellPath
GetNext(IEnumIDList& lst, _tParams&&...) ythrow(COMException)
{
::LPITEMIDLIST p{};
CheckHResult(lst.Next(1, &p, nullptr));
return p;
}
ShellFolderPtr
GetDesktopFolder() ythrow(COMException)
{
IShellFolder* p{};
CheckHResult(::SHGetDesktopFolder(&p));
return p;
}
template<typename... _tParams>
ShellFolderPtr
BindToObject(IShellFolder& folder, _tParams&&... args) ythrow(COMException)
{
IShellFolder* p{};
CheckHResult(folder.BindToObject(yforward(args)...,
reinterpret_cast<void**>(&p)));
return p;
}
ShellFolderPtr
BindPath(IShellFolder& folder, const ShellPath& p_pth,
::IBindCtx* p_ctx = nullptr)
ythrow(COMException)
{
return BindToObject(folder, p_pth.Get(), p_ctx, ::IID_IShellFolder);
}
EnumIDListPtr
EnumObjects(IShellFolder& folder, ::SHCONTF flags = SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS, ::HWND h_own = nullptr)
ythrow(COMException)
{
IEnumIDList* p{};
CheckHResult(folder.EnumObjects(h_own, flags, &p));
return p;
}
std::wstring
GetDisplayName(IShellFolder& folder, const ShellPath& p_pth)
{
::STRRET strret;
wchar_t buf[MAX_PATH];
CheckHResult(folder.GetDisplayNameOf(p_pth.Get(), SHGDN_INFOLDER, &strret));
::StrRetToBufW(&strret, p_pth.Get(), buf, MAX_PATH);
return buf;
}
ShellPath
ParseDisplayName(IShellFolder& folder, const wchar_t* name,
::ULONG* p_attr = nullptr)
{
::LPITEMIDLIST p{};
std::wstring str(name);
CheckHResult(folder.ParseDisplayName(nullptr, nullptr, &str[0], nullptr,
&p, p_attr));
return p;
}
class YF_API DirectorySession
{
public:
typedef ShellFolderPtr NativeHandle;
private:
NativeHandle dir;
protected:
EnumIDListPtr pList;
public:
// explicit
// DirectorySession(const wchar_t* name)
// {}
explicit
DirectorySession(NativeHandle p_folder = {},
::SHCONTF flags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)
: dir(std::move(p_folder)),
pList(dir ? EnumObjects(*dir, flags) : nullptr)
{}
DirectorySession(DirectorySession&& h)
: dir(std::move(h.dir)), pList(std::move(h.pList))
{
yunseq(h.dir = nullptr, pList = nullptr);
}
~DirectorySession() ynothrow = default;
NativeHandle
GetNativeHandle() const ynothrow
{
return dir;
}
void
Rewind() ythrow(COMException)
{
if(pList)
CheckHResult(pList->Reset());
}
};
class YF_API HDirectory final : private DirectorySession
{
private:
mutable ShellPath p_path;
mutable std::string utf8_name;
public:
template<typename... _tParams>
explicit
HDirectory(_tParams&&... args)
: DirectorySession(yforward(args)...), p_path(), utf8_name()
{}
HDirectory&
operator*() ynothrow
{
return *this;
}
const HDirectory&
operator*() const ynothrow
{
return *this;
}
operator string() const ynothrow
{
return GetName();
}
HDirectory&
operator++()
{
p_path = GetNext(pList.GetObject());
return *this;
}
explicit
operator bool() const ynothrow
{
return bool(p_path);
}
bool
IsDirectory() const ynothrow
{
::ULONG uAttr(SFGAO_FOLDER);
GetNativeHandle()->GetAttributesOf(1, const_cast< ::LPCITEMIDLIST*>(
&p_path.GetRef()), &uAttr);
return uAttr & SFGAO_FOLDER;
}
const char*
GetName() const ynothrow
{
if(p_path)
{
try
{
const auto h(GetNativeHandle());
yassume(h);
utf8_name = ToANSI(GetDisplayName(*h, p_path));
return &utf8_name[0];
}
catch(...)
{}
}
return ".";
}
using DirectorySession::GetNativeHandle;
const ShellPath&
GetPathPtr() const ynothrow
{
return p_path;
}
ShellPath&
GetPathPtrRef() ynothrow
{
return p_path;
}
using DirectorySession::Rewind;
};
typedef indirect_input_iterator<HDirectory*> FileIterator;
}
#endif
namespace YSLib
{
using namespace UI;
template<typename _fCreator>
using GWidgetCreatorMap = unordered_map<string, _fCreator>;
template<typename _fCreator>
GWidgetCreatorMap<_fCreator>&
FetchWidgetMapping()
{
static GWidgetCreatorMap<_fCreator> widget_map;
return widget_map;
}
template<class _tWidget>
class GWidgetFactory
{
private:
template<typename... _tParams>
static unique_ptr<IWidget>
CreateWidget(_tParams&&... args)
{
return make_unique<_tWidget>(yforward(args)...);
}
template<typename... _tParams>
static int
Register(const string& key)
{
FetchWidgetMapping<unique_ptr<IWidget>(*)(_tParams...)>().emplace(
key, &CreateWidget<_tParams...>);
return 0;
}
public:
GWidgetFactory(const string& key)
{
static int create_def(Register<>(key));
static int create_bounds(Register<const Rect&>(key));
static_cast<void>(create_def);
static_cast<void>(create_bounds);
}
};
const Size def_size(80, 24);
const Size def_size2(120, 36);
const Size def_size3(160, 48);
template<typename... _tParams>
unique_ptr<IWidget>
CreateWidget(const string& type_str, _tParams&&... args)
{
if(const auto f = FetchWidgetMapping<unique_ptr<IWidget>(*)(_tParams...)>()[
type_str])
{
YTraceDe(Notice, "Found widget creator: %s.\n", type_str.c_str());
auto p(f(yforward(args)...));
auto p1 = dynamic_cast<Widget*>(p.get());
if(type_str == "Widget")
p1->Background = SolidBrush(ColorSpace::Yellow);
else if(type_str == "Control")
p1->Background = SolidBrush(ColorSpace::Aqua);
else if(type_str == "Panel")
p1->Background = SolidBrush(ColorSpace::Blue);
return std::move(p);
}
return {};
}
Rect
ParseRect(const string& str)
{
std::istringstream iss(str);
int buf[4];
for(size_t i(0); i < 4; ++i)
if(iss)
iss >> buf[i];
else
throw std::invalid_argument("Parse 'Rect' failed: bad state.");
// FIXME: Complete max value checking.
if(buf[2] < 0 || buf[3] < 0)
throw std::invalid_argument("Parse 'Rect' failed: underflow.");
Rect res(buf[0], buf[1], buf[2], buf[3]);
YTraceDe(Informative, "ParseRect: %s.\n", to_string(res).c_str());
return res;
}
unique_ptr<IWidget>
DetectWidgetNode(const ValueNode& node)
{
try
{
const auto& type_str(AccessChild<string>(node, "$type"));
if(const auto* p_bounds_str = AccessChildPtr<string>(node, "$bounds"))
try
{
const Rect& bounds(ParseRect(*p_bounds_str));
return CreateWidget(type_str, bounds);
}
catch(std::invalid_argument&)
{}
return CreateWidget(type_str);
}
catch(ystdex::bad_any_cast&)
{}
return {};
}
bool
CheckChildName(const string& str)
{
return str.size() != 0 && str[0] != '$';
}
ValueNode
TransformUILayout(const ValueNode& node)
{
if(unique_ptr<IWidget> p_new_widget{DetectWidgetNode(node)})
{
ValueNode res(0, node.GetName());
if(auto p_pnl = dynamic_cast<Panel*>(p_new_widget.get()))
{
auto p_cont(make_unique<ValueNode::Container>());
for(const auto& vn : node)
if(CheckChildName(vn.GetName()))
try
{
if(ValueNode res_child{TransformUILayout(vn)})
{
auto& p_wgt(*AccessChild<shared_ptr<IWidget>>(
res_child, "$pointer"));
if(p_cont->insert(std::move(res_child)).second)
*p_pnl += p_wgt;
}
}
catch(ystdex::bad_any_cast&)
{}
res += ValueNode{0, "$children", p_cont.release(), PointerTag()};
}
res += ValueNode{0, "$pointer",
shared_ptr<IWidget>(std::move(p_new_widget))};
return std::move(res);
}
return {};
}
ValueNode
ConvertUILayout(const string& str)
{
ValueNode root;
try
{
root = TransformConfiguration(SContext::Analyze(Session(str)));
}
catch(ystdex::bad_any_cast& e)
{
// TODO: Avoid memory allocation.
throw LoggedEvent(ystdex::sfmt(
"Bad configuration found: cast failed from [%s] to [%s] .",
e.from(), e.to()), Warning);
}
// return std::move(root);
return TransformUILayout(std::move(root));
}
const char tu_test[] = u8R"NPL(
node_a
($type "Panel")
($bounds "128 64 160 48")
(
node_c
($type "Control")
($bounds "4 4 120 36")
)
(
node_b
($type "Widget")
($bounds "0 0 80 24")
)
)NPL";
void
NPLUITest(Panel& pnl)
{
GWidgetFactory<Widget>("Widget");
GWidgetFactory<Control>("Control");
GWidgetFactory<Panel>("Panel");
static auto node(ConvertUILayout(tu_test));
YTraceDe(Notice, "Size of children nodes: %u\n", unsigned(node.GetSize()));
#if 0
{
TextFile tf("node.txt", ios::out | ios::trunc);
tf << NPL::Configuration(node);
}
std::system("notepad node.txt");
#endif
pnl += *AccessChild<shared_ptr<IWidget>>(node, "$pointer");
}
}
int
main()
{
using namespace YSLib;
using namespace platform_ex;
#if TEST_GUI == 2
// YSLib::InitializeInstalled();
COM com;
while(wcin)
{
wstring ws;
getline(wcin, ws);
try
{
auto p_dsk = GetDesktopFolder();
auto& dsk(p_dsk.GetObject());
auto path = ParseDisplayName(dsk, ws.c_str());
// auto path = ParseDisplayName(dsk, reinterpret_cast<const wchar_t*>(
// String(in, Text::CharSet::GBK).c_str()));
cout << ToANSI(GetDisplayName(dsk, path)) << endl
<< "====" << endl;
ShellFolderPtr psfFirstFolder;
{
// HDirectory h_dir(BindPath(GetDesktopFolder().GetObject(),
// LocateFolder(CSIDL_PROGRAM_FILES)));
// HDirectory h_dir(GetDesktopFolder().GetObject());
HDirectory h_dir(BindPath(dsk, path));
for(FileIterator i(&h_dir); i != FileIterator(); ++i)
#if 0
for(auto i(ystdex::make_transform(FileIterator(&h_dir),
[](const FileIterator& i){
return i.get()->GetName();
})); i != FileIterator(); ++i)
#endif
{
cout << string(*i) << '\n';
const auto& h(i.get());
if(!psfFirstFolder && h->IsDirectory())
psfFirstFolder = BindPath(*h->GetNativeHandle(),
h->GetPathPtr());
}
}
#if 0
cout << "\n\n";
if(psfFirstFolder)
{
HDirectory h_dir(std::move(psfFirstFolder));
#if 0
vector<string> vec;
// vector<size_t> vec;
copy(FileIterator(&h_dir), FileIterator(), back_inserter(vec));
for(const auto& x : vec)
cout << x << endl;
#endif
for(FileIterator i(&h_dir); i != FileIterator(); ++i)
cout << string(*i) << endl;
}
#endif
}
catch(COMException&)
{}
}
cin.get();
#endif
#if TEST_GUI == 1
GUIApplication app;
#if TEST_IMAGE
ImageCodec codec;
#endif
#if 1
TestWnd wgt(Rect(0, 0, 800, 600));
NPLUITest(wgt);
Host::ShowTopLevel(wgt, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 0);
wgt.p_wnd = &Host::WaitForHostWindow(wgt);
#else
UI::Label wgt(Drawing::Size(120, 24));
wgt.Text = "Hello world!";
Host::Show(wgt, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
#endif
Execute(app);
#endif
}
//#undef NDEBUG
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>
#if !YB_T_NO_MATH
#include <cmath>
#include <ccomplex>
#include <complex>
#endif
#if !_MSC_VER
#include <type_traits>
#include <cstdio>
#include <ctime>
#include <tuple>
#include <iterator>
#include <functional>
#include <algorithm>
#include <bitset>
#include <chrono>
#include <random>
#endif
#if !YB_T_NO_IOSTREAM
#include <iostream>
#include <iomanip>
#endif
#if !YB_T_NO_CONT
#include <array>
#include <vector>
#include <list>
#include <forward_list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <stack>
#include <queue>
#endif
#if YTEST_B == 1
#include <ydef.h>
#include <YSLib/Core/ybasemac.h>
typedef std::int16_t s16;
typedef std::uint16_t u16;
typedef std::uint32_t u32;
typedef s16 SPos;
typedef u16 SDst;
#elif YTEST_B == 2
#include <ystdex/any.h>
#include <YSLib/Core/YObject.h>
#elif YTEST_B == 3
#include <YSLib/Core/ValueNode.h>
#elif YTEST_B == 4
#include <ysbuild.h>
#endif
#if !YB_T_NO_TIMING
#include <ytest/timing.hpp>
#endif
#include <typeinfo>
#include <string>
#include <cxxabi.h>
namespace ytest
{
char*
demangle_c(const std::type_info& ti)
{
int status;
return abi::__cxa_demangle(ti.name(), {}, {}, &status);
// Warning: don't forget free!
// See http://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html .
}
std::string
demangle(const std::type_info& ti)
{
const auto p(demangle_c(ti));
std::string str(p);
std::free(p);
return std::move(str);
}
}
using namespace ytest;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment