使用事件处理程序

到目前为止,我们刚刚画了一些画。但现在,我们希望以交互方式响应用户输入。为此,我们需要使用 事件处理程序 .

初始窗口

让我们先制作一个如下所示的窗口:

_images/gui04a.jpeg

我们像往常一样处理这个代码:

include "gui/__init__";
open FlxGui;
FlxGui::init();

var w = create_resizable_window("Felix:gui_04_wm_01",100,100,400,400);
w.add$ mk_drawable FlxGui::clear lightgrey;

var font_name = dflt_sans_serif_font();
var font : font_t = get_font(font_name, 12);
var lineskip = get_lineskip font;

w.add$ mk_drawable FlxGui::write (100,100,font,black,"Basic Event Handler Test");
w.add$ mk_drawable draw_line (RGB(0,0,255), 100,110,200,110);
w.add$ mk_drawable draw_line (RGB(0,0,255), 100,210,200,210);
w.add$ mk_drawable draw_line (RGB(0,0,255), 100,110,100,210);
w.add$ mk_drawable draw_line (RGB(0,0,255), 200,110,200,210);

w.update();
w.show();

事件处理程序

现在,下一件事是我们要 chip 它可以处理事件:

chip event_displayer (w:window_t)
  connector pins
    pin inevent : %<event_t
    pin quit: %>int
{
  while true do
   var e = read pins.inevent;
   var s =
      match e with
      | WINDOWEVENT we =>
        we.type.SDL_EventType.str + ": " +
           we.event.SDL_WindowEventID.str +
           " wid=" + we.windowID.str
      | MOUSEMOTION me =>
        me.type.SDL_EventType.str
      | _ => ""
    ;
    var linepos = 100 + 4 * lineskip;
    if s != "" do
      println$ s;
      var r = rect_t (100,linepos - 2*lineskip,300,4*lineskip);
      w.remove "evt";
      w.add$ mk_drawable "evt" 100u32 fill (r, green);
      w.add$ mk_drawable "evt" 100u32 FlxGui::write (100,linepos,font,black,"EVENT: "+ s);
      w.update();
    done
  done
}

我们正在使用一个强大的新习惯用法:fibritedprogramming。你看到的是一种叫做 coroutine . 让我们看看它能做什么。

首先,界面告诉我们它在窗口上显示事件 w . 现在我们的芯片有一个名为 pins ,在那个连接器上,我们有两个管脚 ineventquit .

别针 inevent 是类型为的数据的输入引脚 event_t 当大头针 quit 是int的输出引脚。我可以从通道类型判断引脚的方向: %< 用于输入,并且 %> 用于输出。接下来是管脚处理的数据类型。

现在让我们看看代码。我们可以立即看到这个芯片在无限循环中运行。它首先从 inevent 别针。

接下来,我们分析事件,看看它是什么,使用模式匹配。有两种活动我们感兴趣:a WINDOWEVENT 和A MOUSEMOTION .

目前,这些事件的奇怪代码只是将一些事件信息转换为字符串 s 我们可以在窗口上显示,让我们不必担心它到底是什么意思(你会看到,当你尝试它!)。

现在下一位计算我们绘制的框内的位置以显示字符串,然后,如果事件描述 s 不是空字符串,我们将字符串打印到标准输出。

现在我们为字符串计算一个边界矩形。不太准确!

现在有趣的一点来了!接下来我们要做的就是 去除 窗户上所有的拉绳都贴上了标签 "evt" . 然后我们添加两个drawable,第一个用绿色填充边框,第二个写一些文本。然后我们更新窗口。

那有什么魔力 100u32 你问?答案是,这是 z 绘图操作的坐标,它是一个32位无符号整数。当Felix在一个表面上画画时,它画的是最小的 z 先协调。然后画出下一个最小的,以此类推。在任何特定的地方 z 坐标,它将按您将可绘制对象添加到可绘制对象列表中的顺序绘制。

默认情况下,绘图发生在 z=0u32 . 为什么要指定z坐标呢?答案是:窗户的背景画在 z=0 . 它没有被赋予一个标记,所以它有默认的标记 "" . 重要的是,我们没有删除带有该标记的drawables,因此背景drawable仍然在drawable列表中。

问题是,我们想画画 顶上 所以我们必须确保我们画的是更高的 z 协调。

主线

现在,正如承诺的那样,是时候安装我们的事件处理程序了:

begin
  var qin,qout = mk_ioschannel_pair[int]();
  device windisp = event_displayer w;
  circuit
    connect windisp.inevent, event_source.src
    wire qout to windisp.quit
  endcircuit

  C_hack::ignore(read qin);
  println$ "QUIT EVENT";
end

这个 beginend 这里有一些重要的原因,稍后会解释,现在只要知道您需要这样做,以确保当您单击退出时,我们创建的schannel变得不可访问,以便程序实际终止。

我们要做的第一件事是创建一个 int 有两个终点: qinqout . 第一个, quin 是一个读通道,第二个通道, qout 是一个写通道。

接下来,我们做一个 device 打电话 windisp 通过将事件处理程序函数应用于要对其进行操作的窗口。

然后,我们通过将事件处理程序连接到事件源,并将quit通道的输出端连接到事件处理程序来构建电路。我们的电路立即开始运行。

现在我们要等到用户单击关闭窗口,或者按quit键。在Mac上,Apple-Q是退出键。我们使用 C_hack::ignore 因为我们不在乎退出的原因是什么。

你应该看到这样的画面:

_images/gui04b.jpeg