dyn4j の実力を JetpackCompose wrapper Physics Layout で思い知る

やはり Java のライブラリ群を使えることは素晴らしいことです。

以下、サンプルコードを参考に Activity を書き換えます。


👉 KlassenKonstantin/ComposePhysicsLayout: A custom Compose layout backed by a physics engine hatena-bookmark





class MainActivity : ComponentActivity() {

  @SuppressLint("SourceLockedOrientationActivity")
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, false)
    requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    setContent {
      PhysicsLayoutTheme {
        Surface(
          modifier = Modifier.fillMaxSize(),
          color = MaterialTheme.colorScheme.background
        ) {

          val simulation = rememberSimulation()
          val balls = remember { mutableStateListOf<BallMeta>() }

          GravitySensor {
            simulation.setGravity(it.copy(x = -it.x).times(3f))
          }

          LaunchedEffect(Unit) {
            var i = 0
            val count = colors.count()
            while (true) {
              balls.add(BallMeta(color = colors[i % count]))
              delay(100)
              i++
            }
          }

          LaunchedEffect(Unit) {
            delay(5000)
            while (true) {
              balls.removeFirst()
              delay(100)
            }
          }

          Box {
            PhysicsLayout(
              modifier = Modifier.systemBarsPadding(),
              simulation = simulation
            ) {

              Block(   0, -200)
              Block(-125, -100)
              Block( 125, -100)
              Block(   0,    0)
              Block(-100,  100)
              Block( 100,  100)
              Block(   0,  200)

              balls.forEach { meta ->
                Ball(0, -350, meta = meta)
              }

            }
          }

        }
      }
    }
  }
}

@Composable
fun PhysicsLayoutScope.Block(x: Int, y: Int) {
  val offset = offsetDp(x, y)
  Card(
    modifier = Modifier
      .body(
        isStatic = true,
        initialTranslation =  offset
      )
  ) {
    Spacer(
      modifier = Modifier
        .size(62.dp)
        .background(color = Color.Gray)
    )
  }
}

@Composable
fun PhysicsLayoutScope.Ball(x: Int, y: Int, meta: BallMeta) {

  val initialOffset = offsetDp(x, y)

  Card(
    modifier = Modifier.body(
      id = meta.id,
      shape = RoundedCornerShape(meta.corner),
      initialTranslation = Offset(initialOffset.x, initialOffset.y),
      initialImpulse = Offset((Random.nextFloat() - 0.5f) * 2, (Random.nextFloat()) * 2),
    ),
    shape = RoundedCornerShape(meta.corner),
    colors = CardDefaults.cardColors(containerColor = meta.color)
  ) {
    Icon(
      modifier = Modifier
        .size(32.dp)
        .padding(4.dp),
      imageVector = Icons.Rounded.Rocket,
      contentDescription = null,
      tint = Color.White
    )
  }
}

@Composable
fun offsetDp(x: Int, y: Int) = with(LocalDensity.current) { Offset(x.dp.toPx(), y.dp.toPx()) }


@Immutable
data class BallMeta(
  val id: String = System.currentTimeMillis().toString(),
  val color: Color,
  val corner: Int = listOf(0, 50).shuffled()[0]
)

private val colors = listOf(
  Color(0xFFEF5350), Color(0xFFEC407A), Color(0xFFAB47BC), Color(0xFF7E57C2),
  Color(0xFF29B6FC), Color(0xFF26C6DA), Color(0xFF26A69A), Color(0xFF66BB6A),
  Color(0xFF9CCC65), Color(0xFFD4E157), Color(0xFFFFEE58), Color(0xFFFFCA28),
  Color(0xFFFFA726), Color(0xFFFF7043), Color(0xFF8D6E63), Color(0xFFBDBDBD),
  Color(0xFF78909C)
)

強力です。

👉 dyn4j hatena-bookmark
👉 【Jetpack Compose】dp / px / sp の相互変換 hatena-bookmark




【macOS】「⌫ (delete)」 と「⌦ (forward delete)」の キーボードショートカット設定

カーソルの左右の文字を消す

「⌫(delete)」「⌦(forward delete)」

の操作。

両手がキーボードのホームポジションから離したくないので、各エディタアプリごとの設定でキーボードショートカットやキーマップを設定しようとして困惑。

まあ、だるい。

設定できても衝突の話もある。

 

■ どうしたいのか

それぞれを 「⌃(control)」 を使ったショートカットに振り分けたい。


カーソルの左を消す delete-backward-char (Windows Backspace)
delete

→ ⌃(control) + H


カーソルの右を消す delete-char (Windows Delete)
fn (function) + delete

→ ⌃(control) + D

 

■ キーマップアプリで設定する

macOS 上で動くキーマップアプリでショートカットとして登録しちゃいます。

👉 ⌘英かな hatena-bookmark
👉 Karabiner-Elements hatena-bookmark

これだけです。

 

■ まとめ

macOS 上で動くテキストのエディタ系の操作はこれですべて統一できます。

もし、すでに 「⌃(control) + H/D」をショートカットすでに使用している場合は使えなくなりますが、最も頻繁に直感的に使うエディタ操作を優先します。

👉 【macOS】キーマップとキーボードショートカットの変更設定 hatena-bookmark
👉 Deletion (GNU Emacs Manual) hatena-bookmark


【macOS】キーマップとキーボードショートカットの変更設定

最近では、macOS ユーザーは、いくつもの重なって設定されたショートカット機能を「ほぼ無意識」にキーボードから操作することになっています。

当然、衝突したり、意図しない機能が発動されることも多くなります。

まずは、設定箇所をはっきり把握しておきたいです。

 

■ OS 設定 キーボードショートカット「修飾キー」

修飾キー (Modifier Keys) の、それぞれの機能を入れ替えたり、無効化することができます。


System Settings

  ↓

Keyboard

  ↓

Keyboard Shortcuts…

  ↓

Modifier Keys

👉 Macで修飾キーの動作を変更する - Apple サポート (日本) hatena-bookmark

 

■ キーマップ変更アプリ

修飾キー を含めたいろいろなショートカットを作成できます。⌘(command)⇧(shift) キーの左右個別の機能振り分けやカーソルキー、Fn キーなど細かく設定できるサードパーティのアプリです。

有名な2つを挙げておきます。ソースコードはともに GitHub で公開されています。無料です。

👉 Karabiner-Elements hatena-bookmark

👉 ⌘英かな hatena-bookmark

私は、⌘英かな を利用して、英数 ↔ かな の切り替えと カーソルキー のショートカット作成で使用しています。

👉 【macOS】IDE で 矢印 (カーソルキー) を使うと キーボード ホームポジション がずれる件 hatena-bookmark

 

■ OS 設定 キーボードショートカット「アプリショートカット」

OSの設定からアプリごとにショートカットを設定できます。


System Settings

  ↓

Keyboard

  ↓

Keyboard Shortcuts…

  ↓

App Shortcuts

  ↓

All Applications


👉 Mac のキーボードショートカット - Apple サポート (日本) hatena-bookmark

ショートカットに振り分ける機能の名称に関しては、スペースや大文字小文字などきちんと入力する必要があります。そこが面倒です。機能 - 名称 の一覧とかどこかにないでしょうかね。

👉 【macOS】キーボードショートカットの変更は言語設定別でイヤだ!! hatena-bookmark

 

■ 各アプリの設定

ブラウザ や エディタ、IDE など、インストールしたアプリ内の設定からもショートカットの設定ができます。

以下、Android Studio のデフォルトのショートカット。


👉 キーボード ショートカット  |  Android デベロッパー  |  Android Developers hatena-bookmark

Android Studio では、設定から、個別にショートカットの変更ができますが、それらの詰め合わせとなったIDEAプラグインをインストールしてまとめて変更することもできます。

👉 IdeaVim - IntelliJ IDEs Plugin | Marketplace hatena-bookmark
👉 VSCode Keymap - IntelliJ IDEs Plugin | Marketplace hatena-bookmark
👉 macOS For All - IntelliJ IDEs Plugin | Marketplace hatena-bookmark
👉 Emacs Keymap - IntelliJ IDEs Plugin | Marketplace hatena-bookmark

以下、Chrome のデフォルトのショートカット。

👉 Chrome のキーボード ショートカット - パソコン - Google Chrome ヘルプ hatena-bookmark

Chrome では、設定 - エクステンション - ショートカット


chrome://extensions/shortcuts

から、個別のアプリに対してのショートカットを変更できますが、これも以下のような Chrome エクステンションをインストールすると設定しやすいです。

👉 AutoControl: Keyboard shortcut, Mouse gesture - Chrome Web Store hatena-bookmark
👉 Shortkeys (Custom Keyboard Shortcuts) - Chrome Web Store hatena-bookmark
👉 Disable keyboard shortcuts - Chrome Web Store hatena-bookmark

 

■ まとめ

不必要にショートカットをカスタムするのはやめたほうが良さげ。

最小限のカスタムで今の段階でメモ。


1. os settings keyboard modifier keys

  caps lock → control


2. ⌘英かな (os app)

  left command → 英数
  right command → かな
  control + p → arrow up
  control + n → arrow down
  control + f → arrow right
  control + b → arrow left


3. os settings keyboard app shortcuts

  command + v → Paste and Match Style
  shift + command + v → Paste

  ※ Notes.app 向けに paste 2つを入れ替える。


4. app settings

  - Android Studio
    Settings… - Keymap
    → macOS のままにする

  - Chrome
    disable shortcut (chrome extension)
      command + p → Do nothing
      command + d → Do nothing
      command + s → Do nothing

    ※ 英かな切り替え直後の意図しない動作を防ぐ。

指がホームポジションから離れることがずっと気になっていました。

キーマップを自由にカスタマイズできる ⌘英かなKarabiner-Elements を使うことで カーソルキーを使った範囲選択が shift + control + p/n/f/b でできるようになったことで IDEA/Android Studio 上でエディタの Keymap を デフォルトである macOS のまま利用できるようになったことがうれしい。

衝突の警告が多少出ているが、追って調整していけば問題ないでしょう。