C++程序设计

前面的三章,我们说了多态的一些技术内幕还有一些关于C++对象模型的内容,所以我就在想是要继续深入C++的知识点呢还是就目前的内容我们来聊聊如何来设计一个应用程序,最后选择了后者,这一章的内容我们来说说如何搭建一个GUI框架,由于GUI框架涉及到方方面面,所以我们这里只能算是一个简单的切入点,不涉及详细的编码的实现。

GUI框架很多,在windows上面C++有MFC,WTL,还有跨平台的Qt等等,我们可以随便找一个来作为参考,有了参考之后我们还需要对我们的框架的模块规划。

我们打算写一个DirectUI框架, 所以我们需要一个窗口——CDxWindowWnd。

创一个小群,供大家学习交流聊天

如果有对学C++方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交流学习一起进步呀。

也希望大家对学C++能够持之以恒

C++爱好群,

如果你想要学好C++最好加入一个组织,这样大家学习的话就比较方便,还能够共同交流和分享资料,给你推荐一个学习的组织:快乐学习C++组织 可以点击组织二字,可以直达


CDxWindowWnd,作为我们的基本窗口类,该类我们只需要对HWND进行简单的包装。

//+--------------------------

//

// class CDxWindowWnd

// windows的基本窗口、

//

//

class CDxWindowWnd{

public:

CDxWindowWnd();

virtual ~CDxWindowWnd();

operator HWND() const;

void ShowWindow(bool bShow = true, bool bTakeFocus = true);

UINT ShowModal();

virtual void CloseHwnd(UINT nRet = IDOK);

void CenterWindow();

protected:

virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);

private:

HWND m_Hwnd{nullptr};

};

//+-------------------------------

窗口有了,我们需要一个消息循环,对于windows应用程序来说写个消息循环很简单:

//+--------------------------------

MSG msg = { 0 };

while (::GetMessage(&msg, NULL, 0, 0)) {

::TranslateMessage(&msg);

::DispatchMessage(&msg);

if (msg.message == WM_CLOSE && ::GetParent(msg.hwnd) == NULL)

break;

}

//+-------------------------------

虽然这几句代码可以实现我们所要的消息循环,但是我们可以将该消息循环进行封装,并且将该模块作为单例,这样一样我们可以在里面进行更多的操作:

//+-------------------------------

//

// class CDxApplication 

// 负责窗口程序的消息循环

// 以即一些公有资料的管理

//

class CDxApplication{

public:

CDxApplication(HINSTANCE __hInstance = nullptr);

~CDxApplication();

static CDxApplication* InstanceWithArgs(HINSTANCE __hInstance);

static CDxApplication* Instance();

static void Destroy();

static void SetFont(const MString& fontName, unsigned fSize, bool isBold = false, bool isItalic = false);

static HINSTANCE GetHInstance();

static HFONT GetFont();

static DXFontInfo GetFontInfo();

static MString GetExePath();

static MString GetCurrentTime();

static MString GetUUIDStr();

//

// 消息循环

//

void Quit();

int Run();

protected:

static CDxApplication*  __sPtr;

};

//

// 现在我们可以直接创建窗口并显示出来

//

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow)

{

DxUI::CDxApplication* App = DxUI::CDxApplication::InstanceWithArgs(hInstance);

DxUI::CDxWindowWnd WndWindow;

WndWindow.Create(nullptr, L"TestWindow", DXUI_WNDSTYLE_FRAME, 0);

WndWindow.ShowWindow();

App->Run();

DxUI::CDxApplication::Destroy();

return 0;

}

//+--------------------------------

窗口我们创建出来了,但不符我们的DirectUI的预期,我们需要将标题栏去掉,所以我们可以在CDxWindowWnd的基础上进一步修改:

//+--------------------------------

class CDxWindowImpl : public CDxWindowWnd

{

public:

CDxWindowImpl();

~CDxWindowImpl();

virtual LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);

LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); // 从基类基础而来

//

// 其他

//

protected:

int mIdentifyId{ DX_InvalidID };

bool bIsVisible{ true };

bool bIsZoomable{ true };

RECT mCaptionBox;

RECT mSizeBox;

SIZE mMaxSize;

SIZE mMinSize;

SIZE mRoundRectSize;

};

//+-------------------------------

修改窗口风格我们放在OnCreate函数中进行实现:

//+------------------------------

LRESULT CDxWindowImpl::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

//+-------------------

//

// 调整窗口样式

//

//+--------------------

LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);

styleValue &= ~WS_CAPTION;

::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

return 0;

}

//+------------------------------

当然,如果我们就这样把标题栏去掉之后,窗口就没法拉动,也没法关闭,就一直停在桌面上,一动不动,所以为了解决这个问题,我们必须换种方式把标题栏给重新绘制出来,这就是 OnNcHitTest 的功劳了。

//+-----------------------------

LRESULT CDxWindowImpl::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

POINT pt;

pt.x = GET_X_LPARAM(lParam);

pt.y = GET_Y_LPARAM(lParam);

::ScreenToClient(*this, &pt);

RECT rcClient;

::GetClientRect(*this, &rcClient);

if (!::IsZoomed(*this) && bIsZoomable)

{

RECT rcSizeBox = mSizeBox;

if (pt.y < rcClient.top + rcSizeBox.top)

{

if (pt.x < rcClient.left + rcSizeBox.left) return HTTOPLEFT;

if (pt.x > rcClient.right - rcSizeBox.right) return HTTOPRIGHT;

return HTTOP;

}

else if (pt.y > rcClient.bottom - rcSizeBox.bottom)

{

if (pt.x < rcClient.left + rcSizeBox.left) return HTBOTTOMLEFT;

if (pt.x > rcClient.right - rcSizeBox.right) return HTBOTTOMRIGHT;

return HTBOTTOM;

}

if (pt.x < rcClient.left + rcSizeBox.left) return HTLEFT;

if (pt.x > rcClient.right - rcSizeBox.right) return HTRIGHT;

}

RECT rcCaption = mCaptionBox;

if (-1 == rcCaption.bottom)

{

rcCaption.bottom = rcClient.bottom;

}

if (pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right

&& pt.y >= rcCaption.top && pt.y < rcCaption.bottom)

{

return HTCAPTION;

}

return HTCLIENT;

}

//+-------------------------------------

标题栏的关键在于mCaptionBox的bottom的值,所以我们可以设置mCaptionBox来修改标题栏的高度。

我们现在虽然得到了我们想要的无标题栏窗口,但是这只是一张白板,所以我们还需要对齐进行绘制,我们称该层为绘制资源管理层:

//+-------------------------------------

class CDxRendImpl : public CDxWindowImpl

{

public:

CDxRendImpl();

~CDxRendImpl();

virtual bool OnInitResource2D();

virtual bool OnInitResource3D();

virtual void UnInitResource();

virtual void OnRender();

virtual void OnRender2D();

virtual void OnRender3D();

virtual void SaveToFile(const MString& fileName);

virtual void OnRendWindow(IPainterInterface* painter);

};

//+--------------------------------------

我们想要绘制那么我们就需要一个绘制模块,绘制的时候我们还需要效果,所以我们还需要两个模块:

//+-------------------------------------

//

// 效果接口

//

class CDxEffects

{

///

/// 多种效果

///

};

//

// 二维平面变换矩阵

//

struct TransformMatrix{

FLOAT _11;

FLOAT _12;

FLOAT _21;

FLOAT _22;

FLOAT _31;

FLOAT _32;

};

//

// 绘图接口

//

class IPainterInterface{

public:

//+-------------------------------------------

//

// 为绘制方便,下面的接口都得实现

// 当然如果实际没有用处的可以简单的实现即可

//

//+-------------------------------------------

virtual ~IPainterInterface(){};

virtual void BeginDraw() = 0;  // 开始绘制

virtual void Clear(const DxColor& col) = 0; // 使用特定色彩擦除背景

virtual void EndDraw() = 0;  // 结束绘制

virtual void DrawRectangle(const RECT& rc, const DxColor& col,double size) = 0;

virtual void DrawRoundedRectangle(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void DrawEllipse(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void DrawDashRectangle(const RECT& rc, const DxColor& col, double size) = 0;

virtual void DrawDashRoundedRectangle(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void DrawDashEllipse(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void FillRectangle(const RECT& rc, const DxColor& col) = 0;

virtual void FillRoundedRectangle(const RECT& rc, const SIZE& radius, const DxColor& col) = 0;

virtual void FillEllipse(const RECT& rc, const SIZE& radius, const DxColor& col) = 0;

virtual void FillRectangle(const RECT& rc, CDxEffects* pEffects) = 0;

virtual void FillRoundedRectangle(const RECT& rc, const SIZE& radius, CDxEffects* pEffects) = 0;

virtual void FillEllipse(const RECT& rc, const SIZE& radius, CDxEffects* pEffects) = 0;

virtual void DrawBitmap(const RECT& rc, CDxEffects* pEffects) = 0;

virtual void DrawBitmap(const RECT& rc, const MString& bitmap,int w = -1,int h = -1) = 0;

virtual void DrawText(const MString& Text, const RECT& rc, CDxEffects* pEffects) = 0;  // 只绘制文本,超出区域不管 效率更高

virtual void DrawText(const MString& Text, const RECT& rc, const DxColor& col, const DXFontInfo& font,DXAlignment alignt) = 0; // 不使用效果直接绘制

virtual void DrawTextWithClip(const MString& Text, const RECT& rc, CDxEffects* pEffects, const RECT& cliprc = { 0, 0, 0, 0 }) = 0; // 绘制文本,超出区域裁剪

virtual void BeginClipRect(const RECT& rc) = 0;  // 在绘制之前调用

virtual void BeginClipRect(const std::vector& points) = 0;  // 在绘制之前调用

virtual void EndClipRect() = 0; // 在绘制完成之后调用

//

// 绘制线体

// DrawLines效率更高

//

virtual void DrawLine(const DxPointD& first, const DxPointD& second, const DxColor& col, double Size) = 0;

virtual void DrawLines(const std::vector& points, const DxColor& col, double Size) = 0;

virtual void DrawDashLine(const DxPointD& first, const DxPointD& second, const DxColor& col, double Size) = 0;

virtual void DrawDashLines(const std::vector& points, const DxColor& col, double Size) = 0;

//

// 变换

//

virtual void SetTransform(const TransformMatrix& mat) = 0;

};

//+---------------------------

IPainterInterface 是一个纯虚类,换句话说就是接口类,该类没有对他的子类提供任何便利,反而要求子类必须实现自己定义的所有纯虚接口,而所谓纯虚接口就是虚函数等于0的函数,而只要含有纯虚函数的类就是纯虚类,也就是所谓的接口类,之所以我们这里要将绘制操作定义为纯虚类主要是考虑到以后可能会使用不同的图像引擎来绘制图形,比如我们这里可以使用D2D,也可以使用OpenGL,还可以使用GDI等等,那么我们为什么能够同时展示二维和三维图形,所以我们选择D2D:

//+---------------------------

class CDxPainter : public IPainterInterface

{

public:

typedef ID2D1RenderTarget* LPID2D1HwndRenderTarget;

public:

CDxPainter(ID2D1RenderTarget* render);

~CDxPainter();

ID2D1RenderTarget* getRenderTarget() const;

ID2D1RenderTarget* operator->() const;

operator LPID2D1HwndRenderTarget() const;

operator bool() const;

//

// 下面是 IPainterInterface 继承而来的所有接口

//

private:

ID2D1RenderTarget* pRenderTarget;

CDxCharaterFormat* pTextRender{ nullptr };

ID2D1Layer* p_ClipLayout{ nullptr };

ID2D1Geometry* p_ClipGeometry{ nullptr };

};

//+------------------------------

到现在我们已经拥有了窗口,效果,绘图三个模块,所以我们只需要将三个模块组合起来就可以进行图形绘制,这个操作我们放在CDxRendImpl::OnRender()中,不过CDxRendImpl::OnRender()中我们默认绘制2D平面,所以真正的操作我们放在CDxRendImpl::OnRender2D()中:

//+------------------------------

void CDxRendImpl::OnRender(){

OnRender2D();

}

void CDxRendImpl::OnRender2D(){

CDxPainter painter(pRendTarget);

painter.BeginDraw();

this->OnRendWindow(&painter);

painter.EndDraw();

}

void CDxRendImpl::OnRendWindow(IPainterInterface* painter){

;

}

//+-------------------------------

事实上我们并不对该层进行渲染,所以该层的OnRendWindow函数被实现为空,因为我们需要的是做一个DirectUI框架,而我们现在还是基于窗口HWND的,所以我们还差我们的DirectUI窗口,当然DirectUI至少需要一个持有HWND,所以我们必须继承至CDxRendImpl.

//+-------------------------------

//

// DirectUI 窗口类

//

class CDxWidget : public  CDxRendImpl

{

public:

CDxWidget();

~CDxWidget();

//+---------------------------

//

// 创建Hwnd

//

//+---------------------------

virtual void CreateHwnd();

virtual void CreateHwnd(HWND parent);

//

// 其他

//

};

//+---------------------------------

我们可以通过CDxWidget::CreateHwnd()决定是否需要创建HWND,我们只对主窗口创建HWND对于子窗口不创建,在渲染的时候我们先渲染当前窗口,再对子窗口进行渲染。

//+--------------------------------

//

// 绘制窗口

//

void CDxWidget::OnRendWindow(IPainterInterface* painter){

if (bIsVisible == false){

return;

}

mEffects.SetCurrentStatus(GetWindowStatus());

//+---------------

//

// 渲染Title

//

//+--------------

if (mHwnd && !pCaptionLabel&& !::GetParent(mHwnd)){

pCaptionLabel = new CDxCaption;

pCaptionLabel->SetParent(this);

RECT rc = mFrameArea;

rc.bottom =  mCaptionBox.bottom;

pCaptionLabel->SetGeomety(rc);

mRendArea = mFrameArea;

mRendArea.X(mFrameArea.X() + mSizeBox.left);

mRendArea.Y(mRendArea.Y() + mCaptionBox.bottom);

mRendArea.Width(mFrameArea.Width() - mSizeBox.left - mSizeBox.right);

mRendArea.Height(mFrameArea.Height() - mCaptionBox.bottom - mSizeBox.bottom);

if (!mIcon.empty()){

pCaptionLabel->GetIconEffects()->SetBitmaps(Dx_Normal, mIcon);

}

UpdateChildWindowPos();

}

if (pCaptionLabel){

RECT rc = mFrameArea;

rc.bottom = rc.top + pCaptionLabel->GetFrameRect().Height();

pCaptionLabel->SetGeomety(rc);

pCaptionLabel->SetText(mTitle);

pCaptionLabel->OnRendWindow(painter);

}

if (mEffects.GetEffectType() == CDxEffects::Dx_ImageType){

painter->DrawBitmap(mImageRendArea, &mEffects);

}

else if (mEffects.GetEffectType() == CDxEffects::Dx_ColorType){

DXShape shape = GetWindowShape();

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->FillRectangle(mRendArea, &mEffects);

break;

case DxUI::Dx_RoundedRectangle:

painter->FillRoundedRectangle(mRendArea, mRoundRectSize, &mEffects);

if (bIsNeedBorder && mBorderWidth > 0){

painter->DrawRoundedRectangle(mRendArea, mRoundRectSize, mBorderColor, mBorderWidth);

}

break;

case DxUI::Dx_Ellipse:

painter->FillEllipse(mRendArea, mRoundRectSize, &mEffects);

break;

default:

break;

}

}

else{

DXShape shape = GetWindowShape();

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->FillRectangle(mRendArea, &mEffects);

painter->DrawBitmap(mImageRendArea, &mEffects);

break;

case DxUI::Dx_RoundedRectangle:

painter->FillRoundedRectangle(mRendArea, mRoundRectSize, &mEffects);

painter->DrawBitmap(mImageRendArea, &mEffects);

break;

case DxUI::Dx_Ellipse:

painter->FillEllipse(mRendArea, mRoundRectSize, &mEffects);

painter->DrawBitmap(mImageRendArea, &mEffects);

break;

default:

break;

}

}

if (!mText.empty() ){

painter->DrawText(mText, mTextRendArea, &mEffects);

}

if (pLayout){

pLayout->OnRendWindow(painter);

}

//+-----------------------------

//

// 渲染子窗口

//

//+-----------------------------

if (!mChildList.empty()){

UpdateChildWindowPos();

for (auto& window : mChildList){

CDxWidget*& windowref = window.ref();

if (windowref->GetHwnd() == nullptr){

windowref->OnRendWindow(painter);

}

}

}

if (bIsNeedBorder){

RECT rc = mRendArea;

rc.left += 1;

rc.right -= 1;

rc.top += 1;

rc.bottom -= 1;

if (mEffects.GetEffectType() == CDxEffects::Dx_ImageType){

painter->DrawRectangle(rc, mBorderColor, 1);

}

else{

DXShape shape = GetWindowShape();

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->DrawRectangle(rc, mBorderColor, 1);

break;

case DxUI::Dx_RoundedRectangle:

painter->DrawRoundedRectangle(rc, mRoundRectSize, mBorderColor, 1);

break;

case DxUI::Dx_Ellipse:

painter->DrawEllipse(rc, mRoundRectSize, mBorderColor, 1);

break;

default:

break;

}

}

}

if (!bIsEnabel){

DXShape shape = GetWindowShape();

mEffects.SetCurrentStatus(Dx_Disable);

mEffects.SetDisabelColor(mDisabelColor);

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->FillRectangle(mRendArea, &mEffects);

break;

case DxUI::Dx_RoundedRectangle:

painter->FillRoundedRectangle(mRendArea, mRoundRectSize, &mEffects);

break;

case DxUI::Dx_Ellipse:

painter->FillEllipse(mRendArea, mRoundRectSize, &mEffects);

break;

default:

break;

}

}

}

//+----------------------------------

显然对CDxRendImpl::OnRender2D()进行完善:

//+---------------------------------

void CDxRendImpl::OnRender2D(){

CDxWidget* window = dynamic_cast(this);

if (window->IsNeedRender() == false)

return;

if (pRendTarget && window){

pRendTarget->BeginDraw();

pRendTarget->Clear(ToD2DColor(window->GetBackGroundColor()));

window->OnRendWindow(pPainter);

if(window->GetWindowSelfDesc() == Dx_PopWindow)

pPainter->DrawRoundedRectangle(window->GetFrameRect(), window->GetRoundRectSize(), RgbI(128, 128, 128), 2);

HRESULT hr = pRendTarget->EndDraw();

if (FAILED(hr)){

DxTRACE(L"渲染出错[%1]\n", hr);

}

}

else{

if (window->GetWindowSelfDesc() == Dx_Layout){

if (window->GetParent()){

window->GetParent()->OnRender2D();

return;

}

}

else if (window && window->IsVisible() ){

if (window->GetOpacity() < 0.99999999){

if (window->GetParent()){

window->GetParent()->OnRender2D();

return;

}

}

DxColor col = window->GetEraseColor();

CDxWidget* parent = window->GetParent();

if (col.rgb.a == 0 || (parent && parent->HasFloatWindow())){

if (parent){

parent->OnRender2D();

return;

}

}

auto render = GetRenderTarget();

if (render){

CDxPainter* painter = nullptr;

if (g_PainterMap.count(render)){

painter = g_PainterMap.at(render);

}

else{

painter = new CDxPainter(render);

g_PainterMap[render] = painter;

}

render->BeginDraw();

ID2D1Layer* p_ClipLayout{ nullptr };

ID2D1Geometry* p_ClipGeometry{ nullptr };

safe_release(p_ClipGeometry);

safe_release(p_ClipLayout);

render->CreateLayer(&p_ClipLayout);

RECT rc = window->GetInvalidateRect();

p_ClipGeometry = CDxResource::CreateRectGeometry(rc);

if (p_ClipLayout == nullptr || p_ClipGeometry == nullptr)

return;

render->PushLayer(D2D1::LayerParameters(

D2D1::InfiniteRect(),

p_ClipGeometry,

D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,

D2D1::IdentityMatrix(),

1.0f,

NULL,

D2D1_LAYER_OPTIONS_NONE

), p_ClipLayout);

render->Clear(ToD2DColor(col));

window->OnRendWindow(painter);

render->PopLayer();

safe_release(p_ClipGeometry);

safe_release(p_ClipLayout);

HRESULT hr = render->EndDraw();

if (FAILED(hr)){

DxTRACE(L"渲染出错[%1]\n", hr);

}

}

}

}

}

//+---------------------------------

我们现在我们可以通过子类化CDxWidget实现各种控件,实现不同的效果,最终我们可以很简单的编写出各种样式的界面。

这一章的重点并非是在这个GUI框架的实现上,而是主要让我们对class,继承和多态的使用有进一步的理解,以及对纯虚类的引入,当然至于对该框架感兴趣的同学,我们会在后续的内容里面一点点的引入,毕竟里面有很多模板的东西,现在一下子拿出来很多东西可能会比较吃力,所以这一章的内容还是在对C++程序设计上的一些理解,怎么组合class,怎么使用继承,怎么使用多态,怎么使用纯虚类等,这些其实是仁者见仁智者见智的问题,但在自己没有比较好的想法的时候有个参考总归是好的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容