(五)IntelliJ 插件开发——Popups(弹出菜单)

官方文档

https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/popups.html
https://www.jetbrains.org/intellij/sdk/docs/tutorials/action_system/grouping_action.html

Github

https://github.com/kungyutucheng/my_gradle_plugin

参考

Intellij IDEA 插件开发 -- ListPopup

运行环境

macOS 10.14.5
IntelliJ idea 2019.2.4

1、ActionGroupPopupAction

最常用,创建一个普通的弹出菜单

ActionGroupPopupAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * @author wengyongcheng
 * @since 2020/3/3 10:48 下午
 */
public class ActionGroupPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        // 单选,action被选中可以触发对应的actionPerformed方法
        DefaultActionGroup actionGroup = (DefaultActionGroup) ActionManager.getInstance().getAction("popupGroup");
        ListPopup listPopup = JBPopupFactory.getInstance().createActionGroupPopup("popup",actionGroup,e.getDataContext(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,false);
        listPopup.showInFocusCenter();
        listPopup.addListSelectionListener(this);
        listPopup.addListener(this);

    }


    /**
     * 值改变事件监听,通过上下键选择时,可以通过getFirstIndex获取第一个选择的元素的索引,通过getLastIndex可以获取最后一个选择的元素的索引
     * @param e
     */
    @Override
    public void valueChanged(ListSelectionEvent e) {
        int firstIndex = e.getFirstIndex();
        int lastIndex = e.getLastIndex();
        boolean valueIsAdjusting = e.getValueIsAdjusting();
        System.out.println("firstIndex:" + firstIndex);
        System.out.println("lastIndex:" + lastIndex);
        System.out.println("valueIsAdjusting:" + valueIsAdjusting);

    }

    /**
     * popup 监听弹出菜单关闭动作
     * @param event
     */
    @Override
    public void onClosed(@NotNull LightweightWindowEvent event) {
        JBPopup jbPopup = event.asPopup();
        System.out.println("关闭popup");
        jbPopup.cancel();
    }
}

核心方法是:

public ListPopup createActionGroupPopup(@Nullable @Nls(capitalization = Nls.Capitalization.Title)String title,
                                          @NotNull ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions)

参数selectionAidMethod代表action动作的匹配方式,可选值如下:

  • NUMBERING:action前带有数字序号,通过选择对应数字即可触发对应action
  • ALPHA_NUMBERING:同NUMBERING,数字不够,字母来凑
  • SPEEDSEARCH:快速查找,通过输入字母实现模糊匹配,大小写不敏感
  • MNEMONICS:翻译是帮助记忆,不理解,实例也看不出来,求赐教
    其中,showInBestPositionFor代表由idea来选择最佳展示位置,也可以使用以下方法来自定义位置:
/**
 * Shows the popup at the bottom left corner of the specified component.
 *
 * @param componentUnder the component near which the popup should be displayed.
 */
void showUnderneathOf(@NotNull Component componentUnder);

展示在某个组件的左下角

/**
 * Shows the popup at the specified point.
 *
 * @param point the relative point where the popup should be displayed.
 */
 void show(@NotNull RelativePoint point);

指定具体某个位置

/**
 * Shows the popup in the center of the specified component.
 *
 * @param component the component at which the popup should be centered.
 */
void showInCenterOf(@NotNull Component component);

展示在某个组件的中间位置

/**
 * Shows the popups in the center of currently focused component
 */
void showInFocusCenter();

展示在已获取焦点的组件的中间位置

/**
 * Shows in best position with a given owner
 */
void show(@NotNull Component owner);

展示在某个组件的最佳位置

/**
 * Shows the popup in the center of the active window in the IDEA frame for the specified project.
 *
 * @param project the project in which the popup should be displayed.
 */
void showCenteredInCurrentWindow(@NotNull Project project);

展示在当前项目的所在窗口的中间位置

注册Action

<action id="com.kungyu.popup.ActionGroupPopupAction" class="com.kungyu.popup.ActionGroupPopupAction" text="ActionGroupPopupAction" description="ActionGroupPopupAction">
    <add-to-group group-id="PopupMenuActions" anchor="first"/>
    <keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
</action>
<group id="popupGroup"/>
<action class="com.kungyu.popup.CustomFirstAction" id="com.kungyu.popup.CustomFirstAction" text="CustomFirstAction" description="CustomFirstAction">
    <add-to-group group-id="popupGroup" anchor="first"/>
</action>
<action class="com.kungyu.popup.CustomSecondAction" id="com.kungyu.popup.CustomSecondAction" text="CustomSecondAction" description="CustomSecondAction">
    <add-to-group group-id="popupGroup" anchor="after" relative-to-action="CustomFirstAction"/>
</action>

CustomFirstAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;

/**
 * @author wengyongcheng
 * @since 2020/3/4 11:06 上午
 */
public class CustomFirstAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Messages.showMessageDialog("第一个action触发", "第一个action", Messages.getInformationIcon());
    }
}

CustomSecondAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;

/**
 * @author wengyongcheng
 * @since 2020/3/4 11:06 上午
 */
public class CustomSecondAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Messages.showMessageDialog("第二个action触发", "第二个action", Messages.getInformationIcon());
    }
}

效果

按住快捷键后效果

选中customFirstAction后效果

selectionAidMethod=NUMBERING效果

selectionAidMethod=SPEEDSEARCH效果

2、ComponentPopupAction

自定义弹出菜单样式

ComponentPopupAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;

/**
 * @author wengyongcheng
 * @since 2020/3/3 10:48 下午
 */
public class ComponentPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {

        // 自定义popup样式
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        JTextField textField = new JTextField();
        panel.add(textField, BorderLayout.NORTH);
        JButton button = new JButton("提交");
        panel.add(button, BorderLayout.CENTER);
        ComponentPopupBuilder componentPopupBuilder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, textField);
        JBPopup popup = componentPopupBuilder.createPopup();
        popup.showInBestPositionFor(e.getDataContext());
    }


    /**
     * 上下键选择事件,仅仅是改变,而不是按住回车之后的选择
     * @param e
     */
    @Override
    public void valueChanged(ListSelectionEvent e) {
        int firstIndex = e.getFirstIndex();
        int lastIndex = e.getLastIndex();
        boolean valueIsAdjusting = e.getValueIsAdjusting();
        System.out.println("firstIndex:" + firstIndex);
        System.out.println("lastIndex:" + lastIndex);
        System.out.println("valueIsAdjusting:" + valueIsAdjusting);

    }

    /**
     * popup 关闭监听
     * @param event
     */
    @Override
    public void onClosed(@NotNull LightweightWindowEvent event) {
        JBPopup jbPopup = event.asPopup();
        System.out.println("关闭popup");
        jbPopup.cancel();
    }
}

注册Action

        <action id="com.kungyu.popup.ComponentPopupAction" class="com.kungyu.popup.ComponentPopupAction"
                text="ComponentPopupAction" description="ComponentPopupAction">
            <add-to-group group-id="PopupMenuActions" anchor="first"/>
            <keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
        </action>

效果

ComponentPopupAction

然而,我们会发现,这个输入框是获取不到焦点的,这就很值得研究了,但是,没有什么是难得到我们的,下面我们一步步探究如何获取到输入框到焦点》?(此处,不得不吐槽一句:IntelliJ的插件开发资料是真的少啊,步步卡)

解决思路1:
试图让输入框手动获取焦点,修改代码,增加如下语句:

textField.grabFocus();

嗯,怀着期待到心情,重新运行,oh,上帝,他还是不行。

解决思路2:
从源码抓起,查看JBPopupFactory#createComponentPopupBuilder,找到实现类的代码:

  @NotNull
  @Override
  public ComponentPopupBuilder createComponentPopupBuilder(@NotNull JComponent content, JComponent preferableFocusComponent) {
    return new ComponentPopupBuilderImpl(content, preferableFocusComponent);
  }

继续跟踪ComponentPopupBuilderImpl类:

  public ComponentPopupBuilderImpl(@NotNull JComponent component, JComponent preferredFocusedComponent) {
    myComponent = component;
    myPreferredFocusedComponent = preferredFocusedComponent;
  }

追踪变量myPreferredFocusedComponent的引用,发现除了上述构造函数,只有另外一处使用:

  @Override
  @NotNull
  public JBPopup createPopup() {
    AbstractPopup popup = new AbstractPopup().init(
      myProject, myComponent, myPreferredFocusedComponent, myRequestFocus, myFocusable, myMovable, myDimensionServiceKey,
      myResizable, myTitle, myCallback, myCancelOnClickOutside, myListeners, myUseDimServiceForXYLocation, myCommandButton,
      myCancelButton, myCancelOnMouseOutCallback, myCancelOnWindow, myTitleIcon, myCancelKeyEnabled, myLocateByContent,
      myPlaceWithinScreen, myMinSize, myAlpha, myMaskProvider, myInStack, myModalContext, myFocusOwners, myAd, myAdAlignment,
      false, myKeyboardActions, mySettingsButtons, myPinCallback, myMayBeParent,
      myShowShadow, myShowBorder, myBorderColor, myCancelOnWindowDeactivation, myKeyEventHandler
    );

    popup.setNormalWindowLevel(myNormalWindowLevel);
    popup.setOkHandler(myOkHandler);

    if (myUserData != null) {
      popup.setUserData(myUserData);
    }
    Disposer.register(ApplicationManager.getApplication(), popup);
    return popup;
  }

很明显,init方法是核心:

  @NotNull
  protected AbstractPopup init(Project project,
                     @NotNull JComponent component,
                     @Nullable JComponent preferredFocusedComponent,
                     boolean requestFocus,
                     boolean focusable,
                     boolean movable,
                     String dimensionServiceKey,
                     boolean resizable,
                     @Nullable String caption,
                     @Nullable Computable<Boolean> callback,
                     boolean cancelOnClickOutside,
                     @Nullable Set<JBPopupListener> listeners,
                     boolean useDimServiceForXYLocation,
                     ActiveComponent commandButton,
                     @Nullable IconButton cancelButton,
                     @Nullable MouseChecker cancelOnMouseOutCallback,
                     boolean cancelOnWindow,
                     @Nullable ActiveIcon titleIcon,
                     boolean cancelKeyEnabled,
                     boolean locateByContent,
                     boolean placeWithinScreenBounds,
                     @Nullable Dimension minSize,
                     float alpha,
                     @Nullable MaskProvider maskProvider,
                     boolean inStack,
                     boolean modalContext,
                     @Nullable Component[] focusOwners,
                     @Nullable String adText,
                     int adTextAlignment,
                     boolean headerAlwaysFocusable,
                     @NotNull List<? extends Pair<ActionListener, KeyStroke>> keyboardActions,
                     Component settingsButtons,
                     @Nullable final Processor<? super JBPopup> pinCallback,
                     boolean mayBeParent,
                     boolean showShadow,
                     boolean showBorder,
                     Color borderColor,
                     boolean cancelOnWindowDeactivation,
                     @Nullable BooleanFunction<? super KeyEvent> keyEventHandler) {
// 略
    myPreferredFocusedComponent = preferredFocusedComponent;
    myRequestFocus = requestFocus;
// 略

可以看到我们关心的入参preferredFocusedComponent被赋值给了myPreferredFocusedComponent,查看引用:

myPreferredFocusedComponent引用

仔细观察,有个很明显的获取焦点语句,感觉离真相越来越近了,搞他:

    if (myRequestFocus) {
      if (myPreferredFocusedComponent != null) {
        myPreferredFocusedComponent.requestFocus();
      }

看到了myRequestFocus这个变量,也就是说,myRequestFocustrue的时候我们传入的textField才能获取到焦点。那么,myRequestFocus变量从何而来,如果足够细心,就会发现是在init方法传入的,而init是在createPopup中调用的,再次查看createPopup方法:

createPopup

到了这里,解决方案就很明显了,修改示例代码如下:

popup.setRequestFocus(true);

运行结果:


得到焦点后效果

完美解决

但凡官方文档齐全一点,也不至于学得这么辛苦


3、ConfirmPopupAction

确认取消式弹出菜单

ConfirmPopupAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * @author wengyongcheng
 * @since 2020/3/3 10:48 下午
 */
public class ConfirmPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {

        // 确认类型popup,展示俩个选项,对应执行俩种操作,TODO 这里不知道为什么选中其中一个之后必定会先弹出no信息框
        ListPopup confirmation = JBPopupFactory.getInstance().createConfirmation("confirm", "yes", "no", () -> Messages.showMessageDialog("yes", "yes", Messages.getInformationIcon()), () -> Messages.showMessageDialog("no", "no", Messages.getInformationIcon()), 0);
        confirmation.showInBestPositionFor(e.getDataContext());

    }


    /**
     * 上下键选择事件,仅仅是改变,而不是按住回车之后的选择
     * @param e
     */
    @Override
    public void valueChanged(ListSelectionEvent e) {
        int firstIndex = e.getFirstIndex();
        int lastIndex = e.getLastIndex();
        boolean valueIsAdjusting = e.getValueIsAdjusting();
        System.out.println("firstIndex:" + firstIndex);
        System.out.println("lastIndex:" + lastIndex);
        System.out.println("valueIsAdjusting:" + valueIsAdjusting);

    }

    /**
     * popup 关闭监听
     * @param event
     */
    @Override
    public void onClosed(@NotNull LightweightWindowEvent event) {
        JBPopup jbPopup = event.asPopup();
        System.out.println("关闭popup");
        jbPopup.cancel();
    }
}
注册Action
        <action id="com.kungyu.popup.ConfirmPopupAction" class="com.kungyu.popup.ConfirmPopupAction"
                text="ConfirmPopupAction" description="ConfirmPopupAction">
            <add-to-group group-id="PopupMenuActions" anchor="first"/>
            <keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
        </action>
ConfirmPopupAction

4、MultiChoosePopupAction

多选式弹出菜单

MultiChoosePopupAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author wengyongcheng
 * @since 2020/3/3 10:48 下午
 */
public class MultiChoosePopupAction extends AnAction implements ListSelectionListener, JBPopupListener {

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {

        // 可以多选,如果传入的list是action,选中不会触发对应的actionPerformed方法
        IPopupChooserBuilder<String> popupChooserBuilder = JBPopupFactory.getInstance().createPopupChooserBuilder(Stream.of("value1","value2").collect(Collectors.toList()));
        JBPopup popup = popupChooserBuilder.createPopup();
        popup.showInBestPositionFor(e.getDataContext());

    }


    /**
     * 上下键选择事件,仅仅是改变,而不是按住回车之后的选择
     * @param e
     */
    @Override
    public void valueChanged(ListSelectionEvent e) {
        int firstIndex = e.getFirstIndex();
        int lastIndex = e.getLastIndex();
        boolean valueIsAdjusting = e.getValueIsAdjusting();
        System.out.println("firstIndex:" + firstIndex);
        System.out.println("lastIndex:" + lastIndex);
        System.out.println("valueIsAdjusting:" + valueIsAdjusting);

    }

    /**
     * popup 关闭监听
     * @param event
     */
    @Override
    public void onClosed(@NotNull LightweightWindowEvent event) {
        JBPopup jbPopup = event.asPopup();
        System.out.println("关闭popup");
        jbPopup.cancel();
    }
}

注册Action

        <action id="com.kungyu.popup.MultiChoosePopupAction" class="com.kungyu.popup.MultiChoosePopupAction"
                text="MultiChoosePopupAction" description="MultiChoosePopupAction">
            <add-to-group group-id="PopupMenuActions" anchor="first"/>
            <keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
        </action>

效果

MultiChoosePopupAction

5、SubGroupPopupAction

子菜单式弹出菜单

SubGroupPopupAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * @author wengyongcheng
 * @since 2020/3/3 10:48 下午
 */
public class SubGroupPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        // 嵌套action
        DefaultActionGroup actionGroup = (DefaultActionGroup) ActionManager.getInstance().getAction("subOuterGroup");
        ListPopup listPopup = JBPopupFactory.getInstance().createActionGroupPopup("SubOuterGroup",actionGroup,e.getDataContext(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,false);
        listPopup.showInFocusCenter();
        listPopup.addListSelectionListener(this);
        listPopup.addListener(this);

    }


    /**
     * 上下键选择事件,仅仅是改变,而不是按住回车之后的选择
     * @param e
     */
    @Override
    public void valueChanged(ListSelectionEvent e) {
        int firstIndex = e.getFirstIndex();
        int lastIndex = e.getLastIndex();
        boolean valueIsAdjusting = e.getValueIsAdjusting();
        System.out.println("firstIndex:" + firstIndex);
        System.out.println("lastIndex:" + lastIndex);
        System.out.println("valueIsAdjusting:" + valueIsAdjusting);

    }

    /**
     * popup 关闭监听
     * @param event
     */
    @Override
    public void onClosed(@NotNull LightweightWindowEvent event) {
        JBPopup jbPopup = event.asPopup();
        System.out.println("关闭popup");
        jbPopup.cancel();
    }
}

注册Action

        <action id="com.kungyu.popup.SubGroupPopupAction" class="com.kungyu.popup.SubGroupPopupAction"
                text="SubGroupPopupAction" description="SubGroupPopupAction">
            <add-to-group group-id="PopupMenuActions" anchor="first"/>
            <keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
        </action>
        <group id="subOuterGroup"/>
        <!--    popup为true代表这是一个子菜单    -->
        <group id="subInnerGroup" popup="true" text="SubInnerGroup">
            <add-to-group group-id="subOuterGroup" anchor="first"/>
            <action class="com.kungyu.popup.SubAction" id="subAction"
                    text="SubAction" description="SubAction">
            </action>
        </group>

SubAction

package com.kungyu.popup;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;

/**
 * @author wengyongcheng
 * @since 2020/3/4 5:28 下午
 */
public class SubAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Messages.showMessageDialog("nest", "nest", Messages.getInformationIcon());

    }
}

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

推荐阅读更多精彩内容