How to design an interactive Sudoku grid in Racket? - scheme

I'm nowing trying to create a Sudoku game in Drracket. I've already managed to design the functions randomly generating Sudoku and checking if the users' answers are correct. But now I have some troubles with designing the interactive Suduko grid.
I want to create a Sudoku grid allows the user click a cell to "activate" it (it also means the user can't fill numbers in wrong places) and fill it with a number (1-9) or delete the number. At the same time, the situation of every cell can be detected by the "check-answer funtion".
The data type I use to represent the a List of SudokuNumber
; the ElementState of the test right solution *TME means test element
(define TRS (list TME-44 TME-34 TME-24 TME-14 TME04 TME14 TME24 TME34 TME44
TME-43 TME-33 TME-23 TME-13 TME03 TME13 TME23 TME33 TME43
TME-42 TME-32 TME-22 TME-12 TME02 TME12 TME22 TME32 TME42
TME-41 TME-31 TME-21 TME-11 TME01 TME11 TME21 TME31 TME41
TME-40 TME-30 TME-20 TME-10 TME00 TME10 TME20 TME30 TME40
TME-4-1 TME-3-1 TME-2-1 TME-1-1 TME0-1 TME1-1 TME2-1 TME3-1 TME4-1
TME-4-2 TME-3-2 TME-2-2 TME-1-2 TME0-2 TME1-2 TME2-2 TME3-2 TME4-2
TME-4-3 TME-3-3 TME-2-3 TME-1-3 TME0-3 TME1-3 TME2-3 TME3-3 TME4-3
TME-4-4 TME-3-4 TME-2-4 TME-1-4 TME0-4 TME1-4 TME2-4 TME3-4 TME4-4))
where a SudokuNumber a structure
(define-struct SudokuNumber [number position])
*For the position, I created a coordinate system by defining the position of
the center cell is (0,0), so every cell has a unique position from (-4.-4) to (4,4)
My current idea is when the user change the situation of a cell, the situation of the corresponding element of the List of SudokuNumber is also changed. So the "check-answer funtion" can work by checking the list.
So now my only problem is how to design the interactive Sudoku grid.

If you are doing a GUI you could use a text-field% or a canvas%. Any way you pick I suggest you to create a widget, so that you can reuse it anywhere (and makes testing easier). For creating a new widget do something like:
(module sudoku
mzscheme
(provide (all-defined))
(require (lib "mred.ss" "mred"))
(define my-sudoku%
(class canvas%
(init-field parent)
(init-field callback)
(super-new (parent parent)(min-width 500)(horiz-margin 2)(vert-margin 2)
(paint-callback (lambda (cnv dc)(PaintCallback cnv dc)))
(style '(control-border vscroll hscroll)))
...
The text-field accepts a callback, so you could validate/reject the new input at that point... Use horizontal and vertical panels to create the grid.
If you go with the canvas% way (personally my favorite) you must implement everything, but you have more flexibility. In this case, the Paint event must redraw the whole sudoku table each time, the on click event will change the active cell (if applies) and the on-char event will validate the key pressed and update the values if appropiate. As an example, here there is an example of a widget made with canvas%, that allows you to draw with mouse. Modifying it to implement sudoku should be easy.
(define canva-pintable%
(class canvas%
(init-field parent) ; parent of this control
(init-field color) ; color to use for painting
(super-new (parent parent)(min-width 400)(min-height 400)(horiz-margin 2)(vert-margin 2)
; In this example we don't need a redraw function. For sudoku you should implement it, so that the window is redraw each time you move/resize the window.
(paint-callback (lambda (cnv dc)(void)))
(style '(control-border vscroll hscroll)))
(send (send this get-dc) set-pen color 1 'solid)
;receives an mouse-event%
(define/override (on-event event)
(cond ((send event dragging?) (send (send this get-dc) draw-point (send event get-x) (send event get-y)))
)
)
(define/override (on-char event)
(send (send this get-dc) clear)
)
)
)
(define myWindow (new frame% (label "Draw on me!")))
(define myCanva (new canva-pintable% (parent myWindow)(color "black")))
(send myWindow show #t)

Related

How to disable a button after user clicks on it in Racket?

I'm having trouble figuring out how to disable a button after the user clicks on it in Racket.
Here is some sample code with explanation on what im doing:
Make a frame by instantiating the frame% class
(define frame (new frame% [label "Example"]))
Make a static text message in the frame
(define msg (new message% [parent frame]
[label "No events so far..."]))
Make a button in the frame
(new button% [parent frame]
[label "Click Me"]
; Callback procedure for a button click:
[callback (lambda (button event)
(send msg set-label "Button click")
)])
Show the frame by calling its show method
(send frame show #t)
I also understand that [enabled #f] will disable the button if I put it in the field but it disables the button before i even click on it and I want it to disable the button after I click on the button. I think the trick lies within the callback because the callback occurs when the button is clicked but I don't know how to fix this issue. Code would be greatly appreciated!
You can use the following method to disable a window:
(send a-window enable enable?)
So, in your case, callback becomes:
(send button enable #f)
For example, modifying new button to:
(new button% [parent frame]
[label "Click Me"]
[callback (lambda (button event)
(send msg set-label "Button click")
(send button enable #f))])
would disable the button upon user clicking.

How to wrap input text in text-field using the Racket GUI plugin

Just need a basic example of a text-field% that wraps input
(define blogPost%
(class horizontal-panel%
(super-new)
(define (callback button event)
(define title-new-value (send titleoutput get-value))
(define new-value (send output get-value))
(save title-new-value new-value))
;;(display title-new-value)
;;(display new-value))
(define button (new button% (label "Submit")
(vert-margin 0)
(parent this)
(callback callback)))
(define titleoutput (new text-field% (label " title")
(min-height 20)
(min-width 200)
(parent this)))
(define output (new text-field% (label "blog")
(min-height 450)
(min-width 400)
(stretchable-width 300)
(vert-margin 0)
(parent this)))
))
(define f (new frame% (label "prism blog post GUI") (min-width 400) (min-height 500)))
(define tib (new blogPost%
(parent f)))
(send f show #t)
there is more to this, basically it saves the user's input into a database which we are planning to have accessible and print to the screen. However, as is, the user when typing into the text field, just types horizontally on one line and it never word wraps and the enter button does not go to a new line. is this issue fixable?
To allow multi-line inputs into a text field, you need to add [style '(multiple)] to the initialization arguments like this:
(define output (new text-field% [label "blog"]
[style '(multiple)]
[min-height 450]
[min-width 400]
[stretchable-width 300]
[vert-margin 0]
[parent this]))
Then the text field allows newlines, and it wraps the text when a line gets too long.
The documentation for this is here. In that, it says:
There are two text field styles:
A single line of text is visible, and a special control event is generated when the user presses Return or Enter (when the text field has the focus) and the event is not handled by the text field’s frame or dialog (see on-traverse-char in top-level-window<%>).
Multiple lines of text are visible, and Enter is not handled specially.
Later, it specifies that the style argument is a list of symbols and says:
The style must contain exactly one of 'single or 'multiple; the former specifies a single-line field and the latter specifies a multiple-line field.

How can I filter buffer-list to only show buffers not visible in windows

I'd like the this function to not return buffers visible in windows.
i.e. I want to eliminate any buffers hanging out in my current frame from the results.
I know that the function 'window-list' returns buffers visible in windows, but I wasn't sure how to make window-list a predicate to the -filter function.
(defun projectile-project-buffers-non-visible ()
"Get a list of non visible project buffers."
(let ((project-root (projectile-project-root)))
(-filter (lambda (buffer)
(projectile-project-buffer-p buffer project-root))
(buffer-list))))
How about this? For each buffer, look at whether it is displayed by a window. If not, return it.
(defun not-visible-buffers (buffers)
"given a list of buffers, return buffers which are not currently visible"
(remove nil
(mapcar
'(lambda (buf)
(unless (get-buffer-window-list buf) buf))
buffers)
))
You could call it as follows:
(not-visible-buffers (buffer-list))
Looks like this works, but I'll leave this open in case anyone has a more elegant solution.
(defun projectile-project-buffers-non-visible ()
"Get a list of non visible project buffers."
(let ((project-root (projectile-project-root)))
(-filter (lambda (buffer)
(and
(projectile-project-buffer-p buffer project-root)
(not (get-buffer-window buffer 'visible))))
(buffer-list)
)))

Implement iOS password entry view in OS X Cocoa

I need to create a view in OS X Cocoa that hides all but the last entered character (until that character is hidden after a set amount of time). Essentially I want to use a UITextField with textfield.secureTextEntry = YES in OS X Cocoa.
I couldn't find a way to import UITextField in OS X Cocoa. I don't think this is possible anyways, since NSTextField and UITextField are from two different frameworks.
NSSecureTextField in OS X Cocoa does not keep the last character visible for a set amount of time, and I could not find an instance or class property that I could set that would change this behavior.
I tried to roll my own implementation, starting from NSTextField and triggering events from keyUp:, but I'm running into problems with corner cases. My current approach is to change the text stored in NSTextField to hidden characters (all but last character, for a set amount of time, etc.). However with this technique, what happens when a user selects the 2nd thru 5th character in a 7-character password and deletes them. How can I then figure out which of the hidden characters were deleted, so that I can retrieve the correct entered password?
So I think if I need to roll my own implementation, I need to not change the actual text stored in the NSTextField, and instead alter the way that the view is displayed on the screen. However I can't find a way to do this currently. Help here would be appreciated.
I'll also say that I have a strong bias to not roll my own implementation. If anyone knows of a way that I can either leverage prior work or simply import UITextField to OS X Cocoa to solve this problem, I'd welcome (and strongly favor) those solutions.
Using NSTextField, I could not successfully separate the drawing of the text from the stored text. I tried customizing the NSFormatter and looking briefly into customizing the field editor. The closest I got was by customizing drawInteriorWithFrame:inView:. This gave me control over drawing, but only when the field editor did not have control of the object (i.e., only when not editing text in the object). All approaches seemed overly complex for what I was trying to do.
Using NSTextView I was able to get this to work. The approach is to separate the drawing of the characters from the stored characters, and store the actual text in the object. The drawing method simply uses asterisks when necessary to hide the text, and that does not influence the text value stored in the object.
Here's some code that works using this approach. It's implemented in Clozure Common Lisp, communicating with Cocoa through Clozure's Objective C bridge:
(defclass easygui::cocoa-password-entry-text-view (easygui::cocoa-text-view)
((pending-fun :accessor pending-fun :initform nil)
(visible-char-time-secs :reader visible-char-time-secs :initform 1))
(:metaclass ns:+ns-object))
(defclass easygui::cocoa-password-entry-layout-manager (ns:ns-layout-manager)
((last-char-vis-p :accessor last-char-vis-p :initform nil))
(:metaclass ns:+ns-object))
(defclass password-entry-text-view (text-view)
()
(:default-initargs :specifically 'easygui::cocoa-password-entry-text-view))
(objc:defmethod #/initWithFrame: ((self easygui::cocoa-password-entry-text-view) (frame #>NSRect))
(unwind-protect (call-next-method frame)
(#/replaceLayoutManager: (#/textContainer self)
(#/init (#/alloc easygui::cocoa-password-entry-layout-manager)))
(#/setFont: self
(convert-font "Courier" 12))))
(objc:defmethod (#/drawGlyphsForGlyphRange:atPoint: :void) ((self easygui::cocoa-password-entry-layout-manager) (glyph-range #>NSRange) (at-point #>NSPoint))
(let ((glyph-cnt (#/numberOfGlyphs self)))
(let ((hide-until (if (last-char-vis-p self) (1- glyph-cnt) glyph-cnt)))
(dotimes (i hide-until)
(#/replaceGlyphAtIndex:withGlyph: self i 13))))
(call-next-method glyph-range at-point))
(defmethod dialog-item-hidden-text ((view password-entry-text-view))
(let ((text (dialog-item-text view)))
(let ((layout-manager (#/layoutManager (cocoa-ref view))))
(with-output-to-string (strm)
(dotimes (i (1- (length text)))
(format strm "*"))
(format strm "~a" (if (last-char-vis-p layout-manager)
(char text (1- (length text)))
"*"))))))
(defmethod cursor-at-end-of-text-p ((cocoa-self easygui::cocoa-password-entry-text-view))
(awhen (#/selectedRanges cocoa-self)
(when (eq (#/count it) 1)
(awhen (#/rangeValue (#/objectAtIndex: it 0))
(let ((pos (ns:ns-range-location it)))
(let ((length (ns:ns-range-length it)))
(when (eq length 0)
(when (eq pos (#/length (#/string cocoa-self)))
t))))))))
(objc:defmethod (#/keyDown: :void) ((cocoa-self easygui::cocoa-password-entry-text-view) the-event)
(call-next-method the-event)
(labels ((get-keypress (the-event)
(let* ((chars (#/characters the-event))
(str (objc:lisp-string-from-nsstring chars))
(char (char str 0)))
char)))
(handle-keypress-on-view
(easygui::easygui-view-of cocoa-self)
(get-keypress the-event))))
(defmethod handle-keypress-on-view ((view password-entry-text-view) keypress)
(let ((cocoa-self (cocoa-ref view)))
(cond ((or (eq keypress #\rubout)
(not (cursor-at-end-of-text-p cocoa-self)))
(setf (last-char-vis-p (#/layoutManager cocoa-self)) nil))
(t
(setf (last-char-vis-p (#/layoutManager cocoa-self)) t)
(setf (pending-fun cocoa-self)
(alambda ()
(when (eq #'self (pending-fun cocoa-self))
(setf (last-char-vis-p (#/layoutManager cocoa-self)) nil)
(#/setNeedsDisplay: cocoa-self #$YES))))
(schedule-for-event-process
(pending-fun cocoa-self)
(visible-char-time-secs cocoa-self))))))

Missing method in mred:canvas%?

I used MrEd Designer to make a user interface for a Scheme program. It includes a mred:canvas% on which I'd like to plot points using draw-point. It's defined as:
(define (naca-ui-init
{...}
#:airfoil-canvas-class
(airfoil-canvas-class canvas%)
{...})
and later:
(set! airfoil-canvas
(new
airfoil-canvas-class
(parent vertical-pane-2165)
(horiz-margin 0)
(min-width 350)
(vert-margin 0)
(gl-config #f)
(stretchable-width #t)
(enabled #t)
(stretchable-height #t)
(min-height 175)
(label "Canvas")
(style '(border))))
When I try to (send airfoil-canvas draw-point 15 30), however, I get:
send: no such method: draw-point for class: canvas%
=== context ===
/usr/local/lib/racket/collects/racket/private/class-internal.rkt:4543:0: obj-error
/home/jason/NACA/naca-ui.scm:29:8: plot-point
/home/jason/NACA/naca.scm:225:23
/home/jason/NACA/naca-ui.scm:21:10: inner
Am I misreading the documentation, or is there something else I don't get here?
PS: Can someone with more rep add the tag MrEd? It would be pertinent here, but you need 1500 to add new tags.
Answering my own question: in short, my docs were moldy and I should have used http://docs.racket-lang.org/. From there, it was easy to see that:
(send airfoil-canvas draw-point 15 30)
should have been:
(send (send airfoil-canvas get-dc) draw-point 15 30)
You need to do your drawing on a drawing-context these days.

Resources