Curl Global Community
グリッドでもESCキーのイベントを透過させたい - Printable Version

+- Curl Global Community (https://communities.curl.com)
+-- Forum: Discussions (https://communities.curl.com/forumdisplay.php?fid=1)
+--- Forum: General Curl questions (https://communities.curl.com/forumdisplay.php?fid=2)
+--- Thread: グリッドでもESCキーのイベントを透過させたい (/showthread.php?tid=1102)



グリッドでもESCキーのイベントを透過させたい - umemura - 08-27-2014

表示されているViewに対して、ESCキーを押されたら閉じる、という処理を考えています。
ただ、RecordGrid のセルにフォーカスが入っている場合、
ESCキーを押しても、Viewが閉じません。

TextField などの場合は問題なく閉じるので、
グリッドにESCのキープレスイベントが握りつぶされているのではないかと思うのですが、
これを回避するにはどうすればよいでしょうか。

Code:
{def grid =
    {RecordGrid
        record-source =
            {RecordSet
                {RecordFields
                    {RecordField "a", nullable? = true}
                }
                ,
                {RecordData a = ""}
            },
        {RecordGridColumn "a"}
    }
}

||View の FocusManager に、ESC キーで閉じるイベントを設定する
{define-proc {add-esc v:View}:void

    {if-non-null fm = {v.get-focus-manager} then
        def ka =
            {KeyAccel
                key-accel-string = "Esc",
                {on Action do
                    {v.close}
                }
            }
        {fm.add-key-accel
            ka
        }
    }
}

{CommandButton
    label = "RecordGried の View",
    {on Action do
        def v = {View grid}
        {add-esc v}
        {v.show}
    }
}

{CommandButton
    label = "TextField の View",
    {on Action do
        def v = {View {TextField }}
        {add-esc v}
        {v.show}
    }
}



RE: グリッドでもESCキーのイベントを透過させたい - dyoshida - 08-27-2014

オープンコントロールのRecordGridのコードをみると、
セルにフォーカスが入っている(編集中)状態でのESCキー
押下は入力中の文字の取消として使われているようです。

SkinnedSRGTextFieldFeelクラスのon-key-pressメソッド内に
該当する処理があり、ご推察の通りここでESCキーイベントを
consumeしているようです。
(controls/feels/record-grid-feels.scurl 163行目付近)

既に機能に割り当てられているキーはショートカットキー
として使用しない方がよいと思いますので、別のキーで
Viewを閉じるのはいかがでしょうか?
("Ctrl+Q" など)



RE: グリッドでもESCキーのイベントを透過させたい - umemura - 08-27-2014

dyoshidaさん、ありがとうございます。

ただ、SkinnedSRGTextFieldFeel の中でコンシュームするのをやめても、
View にまではESCのキーイベントは透過されないようです。

こうなると、RecordGrid が怪しくなってくるわけですが・・・。



Code:
{define-class public open CustomSkinnedSRGTextFieldFeel
  {inherits SkinnedSRGTextFieldFeel}

||--  {method public open {on-start-composition-event
||--                          e:StartCompositionEvent
||--                      }:void
||--    {if-non-null ui = self.ui-object asa #SkinnableTextFieldUI then
||--        {if not e.consumed? then
||--            {type-switch {get-grid-cell ui}
||--             case srgc:StandardRecordGridCell do
||--                {srgc.reveal-if-hidden}
||--                {if not srgc.edit-active? then
||--                    {ui.control.become-active-from-traversal}
||--                    set srgc.edit-active? = true
||--                }
||--            }
||--        }
||--    }
||--    || FIXME: why don't we call into super here?
||--  }

  {method public open {on-key-press ev:KeyPress}:void
    {if-non-null ui = self.ui-object asa #SkinnableTextFieldUI then

        let constant cell:StandardRecordGridCell =
            {non-null {get-grid-cell ui}}
        {cell.reveal-if-hidden}

        || Toggle edit mode if necessary.
        {if not cell.edit-active? and
            (ev.insertable? or
             ev.value == KeyPressValue.backspace or
             ev.value == KeyPressValue.delete or
             ev.value == KeyPressValue.enter or
             ev.value == KeyPressValue.f2)
         then
            set cell.edit-active? = true
            {ui.control.become-active-from-traversal}
        }

        let constant rng:StringDataModelWritableRange =
            (ui.control asa TextField).selected-range

        let constant ui-reserved?:bool =
            {if-non-null grid = cell.grid then
                {grid.ui.reserved-key? ev}
             else
                false
            }

        || Squelch grid keys, except right/left/home/end
        || when not fully selected.
        {if cell.edit-active? and
            (not ui-reserved? or
             ((ev.value == KeyPressValue.right or
               ev.value == KeyPressValue.left or
               ev.value == KeyPressValue.home or
               ev.value == KeyPressValue.end) and
              rng.size != rng.data-model.size))
         then
            {switch ev.value
             case KeyPressValue.esc do
                || Escape reverts the cell.
                {cell.refresh-data}
                
                ||↓ コンシュームしないようにしてもView は閉じない
                ||   (RecordGrid に握りつぶされている?)
               || {ev.consume}
             case KeyPressValue.f2 do
                || f2 used by windows to collapse selection for editing.
                {if rng.size == rng.data-model.size then
                    set rng.point = rng.max-index
                    {rng.collapse-to-point}
                    {ev.consume}
                }
            }

            || Suppress editing keys when non-editable.
            {if ev.insertable? or
                ev.value == KeyPressValue.delete or
                ev.value == KeyPressValue.backspace
             then
                {if {grid-cell-can-update? ui} then
                    {super.on-key-press ev}
                }
             else
                {super.on-key-press ev}
            }

        }

        {forward-to-grid-cell ui, ev}

        set ev.test-recorded? = true
    else
        {super.on-key-press ev}
    }
  }

}




||--{define-class public open CustomSkinnedSRGTextFieldFeel
||--  {inherits SkinnedSRGTextFieldFeel}
||--
||--  {method public open {on-key-press ev:KeyPress}:void
||--
||--    {super.on-key-press ev}
||--    || ESC で View を閉じる・・・のはさすがに無理やりすぎて嫌だなぁ
||--    {if-non-null ui = self.ui-object asa #SkinnableTextFieldUI then
||--        {if-non-null cell =  {get-grid-cell ui} then
||--            {switch ev.value
||--             case KeyPressValue.esc do
||--
||--                {if-non-null v =  {cell.get-view} then
||--                    {v.destroy}
||--                }
||--            }
||--        }
||--    }
||--  }
||--}



{define-class public CustomSRGTextFieldUI  {inherits SRGSkinnableTextFieldUI}}

{do
    {type-switch
        the-default-look-and-feel.target-look-and-feel
    case slaf:StyledLookAndFeel do
        {slaf.register-control-feel
            CustomSRGTextFieldUI,
            CustomSkinnedSRGTextFieldFeel
        }
    }
}



{define-class public CustomCell {inherits  StandardStringCell }
  {constructor public {default}
    {construct-super}
  }

  {method protected open {create-editor}:TextField
    def tf = {super.create-editor}
    set tf.ui-object = {CustomSRGTextFieldUI}
    {return tf}

  }
}

{def grid =
    {RecordGrid
        record-source =
            {RecordSet
                {RecordFields
                    {RecordField "a", nullable? = true}
                }
                ,
                {RecordData a = ""}
            },
        {RecordGridColumn "a" , cell-spec =CustomCell}
    }
}

||View の FocusManager に、ESC キーで閉じるイベントを設定する
{define-proc {add-esc v:View}:void

    {if-non-null fm = {v.get-focus-manager} then
        def ka =
            {KeyAccel
                key-accel-string = "Esc",
                {on Action do
                    {v.close}
                }
            }
        {fm.add-key-accel
            ka
        }
    }
}

{CommandButton
    label = "RecordGried の View",
    {on Action do
        def v = {View grid}
        {add-esc v}
        {v.show}
    }
}

{CommandButton
    label = "TextField の View",
    {on Action do
        def v = {View {TextField }}
        {add-esc v}
        {v.show}
    }
}



RE: グリッドでもESCキーのイベントを透過させたい - umemura - 08-27-2014

やっぱり、consume されないようにすれば、View を閉じれました。
イベントの中を正しく追えていなかった(super.on-key-press を見落としていた)です。失礼。

こんなことをしていいのかどうか、という問題はありますが・・・。

Code:
{define-class public open CustomSkinnedSRGTextFieldFeel
  {inherits SkinnedSRGTextFieldFeel}

  {method public open {on-key-press ev:KeyPress}:void
{super.on-key-press ev}
    {if-non-null ui = self.ui-object asa #SkinnableTextFieldUI then
        {switch ev.value
         case KeyPressValue.esc do
            set ev.consumed? = false
        }
    }
  }
}

あと、セルの振る舞い(キープレス)を変えるのは、このControlFeel を使うのが正しいのでしょうかね?
ルックアンドフィール(LookAndFeel)を取得して、レジストして・・・というのは、
アスペクト志向のようで、便利ではあるのですが、あとからコードを探しにくいという欠点もあるので、
クラスの継承のみで完結させたい気もあります。
このあたり詳しい人いたらコメント下さい。