Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions .idea/xframes-ruby.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added .ruby-version
Binary file not shown.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ I recommend to install Ruby via [Scoop](https://scoop.sh/), then

- `gem install ffi`
- `gem install eventmachine`
- `gem install rxruby`

`RUBY_DLL_PATH` must be set:

Expand All @@ -20,6 +21,7 @@ I recommend to install Ruby via [Scoop](https://scoop.sh/), then
- `sudo apt install ruby-full`
- `sudo gem install ffi`
- `sudo gem install eventmachine`
- `sudo gem install rxruby`

### Run the application

Expand Down
96 changes: 5 additions & 91 deletions main.rb
Original file line number Diff line number Diff line change
@@ -1,63 +1,11 @@
require 'ffi'
require 'json'
require 'eventmachine'

ImGuiCol = {
Text: 0,
TextDisabled: 1,
WindowBg: 2,
ChildBg: 3,
PopupBg: 4,
Border: 5,
BorderShadow: 6,
FrameBg: 7,
FrameBgHovered: 8,
FrameBgActive: 9,
TitleBg: 10,
TitleBgActive: 11,
TitleBgCollapsed: 12,
MenuBarBg: 13,
ScrollbarBg: 14,
ScrollbarGrab: 15,
ScrollbarGrabHovered: 16,
ScrollbarGrabActive: 17,
CheckMark: 18,
SliderGrab: 19,
SliderGrabActive: 20,
Button: 21,
ButtonHovered: 22,
ButtonActive: 23,
Header: 24,
HeaderHovered: 25,
HeaderActive: 26,
Separator: 27,
SeparatorHovered: 28,
SeparatorActive: 29,
ResizeGrip: 30,
ResizeGripHovered: 31,
ResizeGripActive: 32,
Tab: 33,
TabHovered: 34,
TabActive: 35,
TabUnfocused: 36,
TabUnfocusedActive: 37,
PlotLines: 38,
PlotLinesHovered: 39,
PlotHistogram: 40,
PlotHistogramHovered: 41,
TableHeaderBg: 42,
TableBorderStrong: 43,
TableBorderLight: 44,
TableRowBg: 45,
TableRowBgAlt: 46,
TextSelectedBg: 47,
DragDropTarget: 48,
NavHighlight: 49,
NavWindowingHighlight: 50,
NavWindowingDimBg: 51,
ModalWindowDimBg: 52,
COUNT: 53
}
require_relative 'theme'
require_relative 'sampleapp'
require_relative 'services'
require_relative 'treetraversal'
require_relative 'xframes'

# Colors for theme generation
theme2Colors = {
Expand Down Expand Up @@ -181,40 +129,6 @@ def to_json(*options)
end


module XFrames
extend FFI::Library
if RUBY_PLATFORM =~ /win32|mingw|cygwin/
ffi_lib './xframesshared.dll'
else
ffi_lib './libxframesshared.so'
end

# Define callback types
callback :OnInitCb, [:pointer], :void
callback :OnTextChangedCb, [:int, :string], :void
callback :OnComboChangedCb, [:int, :int], :void
callback :OnNumericValueChangedCb, [:int, :float], :void
callback :OnBooleanValueChangedCb, [:int, :int], :void
callback :OnMultipleNumericValuesChangedCb, [:int, :pointer, :int], :void
callback :OnClickCb, [:int], :void

attach_function :init, [
:string, # assetsBasePath
:string, # rawFontDefinitions
:string, # rawStyleOverrideDefinitions
:OnInitCb,
:OnTextChangedCb,
:OnComboChangedCb,
:OnNumericValueChangedCb,
:OnBooleanValueChangedCb,
:OnMultipleNumericValuesChangedCb,
:OnClickCb
], :void

attach_function :setElement, [:string], :void

attach_function :setChildren, [:int, :string], :void
end

on_init = FFI::Function.new(:void, []) do
puts "OnInit called!"
Expand Down
95 changes: 95 additions & 0 deletions sampleapp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
require 'rx'
require_relative 'theme'
require_relative 'widgetnode'

Comment on lines +1 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the dependencies are properly installed.

Please ensure that all required gems are listed in the project's Gemfile:

gem 'rx'
gem 'concurrent-ruby'

Run this script to check the dependencies:


🏁 Script executed:

#!/bin/bash
# Description: Check if required gems are installed and their versions.

# Check if gems are installed
gem list | grep -E "^(rx|concurrent-ruby)"

# Check latest versions on RubyGems
curl -s https://rubygems.org/api/v1/versions/rx.json | jq '.[0].number'
curl -s https://rubygems.org/api/v1/versions/concurrent-ruby.json | jq '.[0].number'

Length of output: 245


Missing Dependency: "rx" gem Not Installed

• The dependency check confirms that "concurrent-ruby" (v1.3.5) is properly installed.
• However, the gem "rx" required by the file (via require 'rx') is missing from the installed gems list.
• Please ensure that the project's Gemfile includes gem 'rx' (expected latest version "0.0.3") and install it accordingly.

class TodoItem
attr_accessor :text, :done

def initialize(text, done)
@text = text
@done = done
end
end

class AppState
attr_accessor :todo_text, :todo_items

def initialize(todo_text, todo_items)
@todo_text = todo_text
@todo_items = todo_items
end
end

sample_app_state = Rx::BehaviorSubject.new(AppState.new("", []))

def on_click
new_todo_item = TodoItem.new("New Todo", false)

current_state = sample_app_state.value

new_state = AppState.new(
current_state.todo_text,
current_state.todo_items + [new_todo_item]
)

sample_app_state.on_next(new_state)
end
Comment on lines +27 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve async handling and state management.

The current implementation has several issues:

  1. Direct access to global state
  2. Blocking UI with promise.wait
  3. Hardcoded "New Todo" text

Consider this improved implementation:

-def on_click
-  promise = Concurrent::Promise.execute do
-    new_todo_item = TodoItem.new("New Todo", false)
-    current_state = $sample_app_state.value
-    new_state = AppState.new(
-      current_state.todo_text,
-      current_state.todo_items + [new_todo_item]
-    )
-    $sample_app_state.on_next(new_state)
-  end
-
-  promise.wait
-end

+def on_click(text = nil)
+  Concurrent::Promise.execute do
+    service = TodoService.instance
+    current_state = service.state.value
+    new_todo_item = TodoItem.new(text || current_state.todo_text || "New Todo", false)
+    new_state = AppState.new(
+      "",  # Reset input after adding
+      current_state.todo_items + [new_todo_item]
+    )
+    service.state.on_next(new_state)
+  end
+end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def on_click
promise = Concurrent::Promise.execute do
new_todo_item = TodoItem.new("New Todo", false)
current_state = $sample_app_state.value
new_state = AppState.new(
current_state.todo_text,
current_state.todo_items + [new_todo_item]
)
$sample_app_state.on_next(new_state)
end
promise.wait
end
def on_click(text = nil)
Concurrent::Promise.execute do
service = TodoService.instance
current_state = service.state.value
new_todo_item = TodoItem.new(text || current_state.todo_text || "New Todo", false)
new_state = AppState.new(
"", # Reset input after adding
current_state.todo_items + [new_todo_item]
)
service.state.on_next(new_state)
end
end


text_style = WidgetStyle.new(
style: WidgetStyleDef.new(
style_rules: StyleRules.new(
font: FontDef.new(name: "roboto-regular", size: 32)
)
)
)

button_style = WidgetStyle.new(
style: WidgetStyleDef.new(
style_rules: StyleRules.new(
font: FontDef.new(name: "roboto-regular", size: 32)
),
layout: YogaStyle.new(
width: "50%",
padding: {Edge[:Vertical] => 10},
margin: {Edge[:Left] => 140}
)
)
)

class App < BaseComponent
def initialize
super({})

@app_state_subscription = sample_app_state.subscribe do |latest_app_state|
props.on_next({
"todo_text" => latest_app_state.todo_text,
"todo_items" => latest_app_state.todo_items
})
end
end

def render
children = [button("Add todo", method(:on_click), button_style)]

props.value["todo_items"].each do |todo_item|
text = "#{todo_item.text} (#{todo_item.done ? 'done' : 'to do'})."
children << unformatted_text(text, text_style)
end

node(children)
end

def dispose
@app_state_subscription.dispose
end
end
Comment on lines +62 to +105
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix state management and async rendering issues.

Several issues need attention:

  1. Direct access to global state
  2. Blocking promises in render method
  3. Subscription variable @app_state_subscription is never set

Apply these improvements:

 class App < BaseComponent
   def initialize
     super({})
 
-    promise = Concurrent::Promise.execute do
-      $sample_app_state.subscribe do |latest_app_state|
-        puts "app state changed"
+    service = TodoService.instance
+    @app_state_subscription = service.state.subscribe do |latest_app_state|
+      puts "app state changed"
 
-        @props.on_next({
-          "todo_text" => latest_app_state.todo_text,
-          "todo_items" => latest_app_state.todo_items
-        })
-      end
+      @props.on_next({
+        "todo_text" => latest_app_state.todo_text,
+        "todo_items" => latest_app_state.todo_items
+      })
     end
 
-    promise.wait
-
     @props.on_next({
       "todo_text" => "",
       "todo_items" => [TodoItem.new("New Todo", false)]
     })
   end
 
   def render
-    children = [button("Add todo", Proc.new {
-      on_click()
-    }, $button_style)]
+    children = [button("Add todo", -> { on_click() }, Theme::Styles.button)]
 
-    promise = Concurrent::Promise.execute do
-      @props.value["todo_items"].each do |todo_item|
-        text = "#{todo_item.text} (#{todo_item.done ? 'done' : 'to do'})."
-        children << unformatted_text(text, $text_style)
-      end
+    @props.value["todo_items"].each do |todo_item|
+      text = "#{todo_item.text} (#{todo_item.done ? 'done' : 'to do'})."
+      children << unformatted_text(text, Theme::Styles.text)
     end
 
-    promise.wait
-
     node(children)
   end
 
   def dispose
     @app_state_subscription.dispose
   end
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class App < BaseComponent
def initialize
super({})
promise = Concurrent::Promise.execute do
$sample_app_state.subscribe do |latest_app_state|
puts "app state changed"
@props.on_next({
"todo_text" => latest_app_state.todo_text,
"todo_items" => latest_app_state.todo_items
})
end
end
promise.wait
@props.on_next({
"todo_text" => "",
"todo_items" => [TodoItem.new("New Todo", false)]
})
end
def render
children = [button("Add todo", Proc.new {
on_click()
}, $button_style)]
promise = Concurrent::Promise.execute do
@props.value["todo_items"].each do |todo_item|
text = "#{todo_item.text} (#{todo_item.done ? 'done' : 'to do'})."
children << unformatted_text(text, $text_style)
end
end
promise.wait
node(children)
end
def dispose
@app_state_subscription.dispose
end
end
class App < BaseComponent
def initialize
super({})
service = TodoService.instance
@app_state_subscription = service.state.subscribe do |latest_app_state|
puts "app state changed"
@props.on_next({
"todo_text" => latest_app_state.todo_text,
"todo_items" => latest_app_state.todo_items
})
end
@props.on_next({
"todo_text" => "",
"todo_items" => [TodoItem.new("New Todo", false)]
})
end
def render
children = [button("Add todo", -> { on_click() }, Theme::Styles.button)]
@props.value["todo_items"].each do |todo_item|
text = "#{todo_item.text} (#{todo_item.done ? 'done' : 'to do'})."
children << unformatted_text(text, Theme::Styles.text)
end
node(children)
end
def dispose
@app_state_subscription.dispose
end
end


class Root < BaseComponent
def initialize
super({})
end

def render
root_node([App.new])
end
end
Loading